Beispiel #1
0
int txop_ward_init(shpeer_t *cli_peer, tx_ward_t *ward)
{
  unsigned char context[128];
  shkey_t sig_key;
  tx_context_t ctx;
  tx_t *tx;
  int err;

  if (ward->ward_stamp == SHTIME_UNDEFINED)
    ward->ward_stamp = shtime();
  if (ward->ward_expire == SHTIME_UNDEFINED)
    ward->ward_expire = shtime_adj(shtime(), MAX_SHARE_SESSION_TIME);

  memcpy(&ward->ward_tx.tx_peer, shpeer_kpriv(sharedaemon_peer()), sizeof(shpeer_t));


  memset(&ctx, 0, sizeof(ctx));
  err = inittx_context(&ctx, get_tx_key(ward), ashkey_uniq());
  if (err)
    return (err);

  err = tx_save(&ctx);
  if (err)
    return (err);

  /* store key reference to generated context */
  memcpy(&ward->ward_ctx, &ctx.ctx_tx.tx_key, sizeof(ward->ward_ctx));


  return (0);
}
Beispiel #2
0
int txop_contract_confirm(shpeer_t *peer, tx_contract_t *contract)
{
  shtime_t now;
  shkey_t *key;
  int sig_ok;

  if (0 != strcmp(contract->con_cur, COIN_USDE) &&
      0 != strcmp(contract->con_cur, COIN_GMC) &&
      0 != strcmp(contract->con_cur, COIN_SYS))
    return (SHERR_INVAL);

  now = shtime();
  if (shtime_before(shtime(), contract->con_birth) ||
      shtime_before(shtime(), contract->con_stamp) ||
      shtime_after(contract->con_birth, contract->con_stamp))
    return (SHERR_TIME);

  key = shkey_hexgen(contract->con_key + 8);
  if (!key)
    return (SHERR_NOKEY);
  sig_ok = shkey_cmp(key, &contract->con_sig);
  shkey_free(&key);
  if (!sig_ok)
    return (SHERR_KEYREJECTED);

  return (0);
}
Beispiel #3
0
static int shgeodb_geo_sql_cb(void *p, int arg_nr, char **args, char **cols)
{
  shgeo_t *value_p = (shgeo_t *)p;
  shtime_t stamp = SHTIME_UNDEFINED;
  shnum_t lat = 0;
  shnum_t lon = 0;
  int alt = 0;

  if (arg_nr == 2) {
    if (args[0])
      lat = (shnum_t)atof(args[0]);
    if (args[1])
      lon = (shnum_t)atof(args[1]);
#if 0
    if (args[2])
      alt = atoi(args[2]);
#endif
    shgeo_set(value_p, lat, lon, alt);

#if 0
    if (args[3])
      value_p->geo_stamp = (shtime_t)atoll(args[3]);
#endif
    if (value_p->geo_stamp == SHTIME_UNDEFINED)
      value_p->geo_stamp = shtime();
  }

  return (0);
}
Beispiel #4
0
uint64_t shrand(void)
{
  char buf[MAX_RANDOM_SEED_SIZE];
  int i; 

  memset(buf, 0, sizeof(buf));
  FILE *ran = fopen("/dev/urandom", "r");
  if (ran) {
    fread(buf, 1, sizeof(buf), ran);
    fclose(ran);    
  } else {
    static int init;
    if (!init) {
      init = 1;
      srand((unsigned int)shtime());
    }
    unsigned int rval;
    for (i = 0; i < MAX_RANDOM_SEED_SIZE; i += 4) {
      rval = rand();
      memcpy(buf + i, &rval, sizeof(rval));     
    }
  }

  return (shcrc(buf, sizeof(buf)));
}
Beispiel #5
0
int txop_contract_init(shpeer_t *cli_peer, tx_contract_t *contract)
{
  shkey_t *key;

  contract->con_birth = shtime();

  key = shkey_hexgen(contract->con_key + 8);
  if (!key)
    return (SHERR_NOKEY);
  memcpy(&contract->con_sig, key, sizeof(contract->con_sig));
  shkey_free(&key);

  contract->con_stamp = shtime();

  return (0);
}
Beispiel #6
0
int create_bond_asset(shkey_t *id_key, tx_bond_t *bond, size_t bond_nr, tx_asset_t **asset_p)
{
  tx_asset_t *asset;
  shpeer_t *peer;
  size_t len;
  int err;

  peer = load_asset_peer(id_key);
  if (!peer)
    return (SHERR_INVAL);

  len = sizeof(tx_asset_t) + (sizeof(tx_bond_t) * bond_nr);
  asset = (tx_asset_t *)calloc(len, sizeof(char));
  if (!asset)
    return (SHERR_NOMEM);

  asset->ass.ass_birth = shtime();
  asset->ass_type = TX_BOND;

  /* asset content */
  asset->ass_size = (sizeof(tx_bond_t) * bond_nr);
  memcpy((unsigned char *)asset->ass_data, 
      (unsigned char *)bond, (sizeof(tx_bond_t) * bond_nr));

  /* generate signature based on identity's priveleged key */
  memcpy(&asset->ass.ass_id, id_key, sizeof(asset->ass.ass_id));
  generate_asset_signature(asset, peer);

  err = tx_init(NULL, (tx_t *)asset, TX_ASSET);
  if (err)
    return (err);

  return (0); 
}
Beispiel #7
0
int install_sexe_userdata(sexe_t *S, char *tag)
{
  SHFL *fl;
  shjson_t *udata;
  shfs_t *fs;
  shbuf_t *buff;
  shkey_t *k;
  char path[PATH_MAX+1];
  int is_new;

  k = shkey_str(tag);
  sprintf(path, "/sys/data/sexe/%s", shkey_hex(k)); 
  memcpy(&S->pname, k, sizeof(S->pname));
  shkey_free(&k);

  buff = shbuf_init();
  fs = shfs_init(NULL);
  fl = shfs_file_find(fs, path);
  is_new = shfs_read(fl, buff);

  udata = shjson_init(shbuf_size(buff) ? (char *)shbuf_data(buff) : NULL);
  shbuf_free(&buff);

  if (is_new)
    shjson_num_add(udata, "birth", shtimef(shtime()));

  sexe_table_set(S, udata);
  lua_setglobal(S, "userdata");
  shjson_free(&udata);

  shfs_free(&fs);

  return (0);
}
Beispiel #8
0
int shpam_pshadow_set(shfs_ino_t *file, shseed_t *seed, shkey_t *sess_key)
{
  shadow_t save;
  int err;

  err = shpam_shadow_load(file, seed->seed_uid, &save);
  if (err)
    return (err);

  /* validate session key */
  if (shtime_after(shtime(), save.sh_expire))
    return (SHERR_KEYEXPIRED);
  if (!shkey_cmp(&save.sh_sess, sess_key))
    return (SHERR_KEYREJECTED);

  err = shpam_shadow_session_expire(file, seed->seed_uid, sess_key);
  if (err)
    return (err);

  err = shpam_pshadow_store(file, seed);
  if (err)
    return (err);

  return (0);
}
Beispiel #9
0
int shpam_shadow_remove(shfs_ino_t *file, uint64_t uid, shkey_t *sess_key)
{
  shadow_t *ent;
  shadow_t save;
  shkey_t *key;
  int err;

  if (!sess_key)
    return (SHERR_NOKEY);

  err = shpam_shadow_load(file, uid, &save);
  if (err) {
    return (err);
}

  if (shtime_after(shtime(), save.sh_expire))
    return (SHERR_KEYEXPIRED);

  if (!shkey_cmp(&save.sh_sess, sess_key))
    return (SHERR_KEYREJECTED);

  key = shkey_bin((char *)&uid, sizeof(uid));
  err = shfs_cred_remove(file, key);
  shkey_free(&key);
  if (err) {
    return (err);
}

  return (0);
}
Beispiel #10
0
static int shgeodb_scan_sql_cb(void *p, int arg_nr, char **args, char **cols)
{
  shgeo_t *value_p = (shgeo_t *)p;
  shnum_t lat = 0;
  shnum_t lon = 0;
  int idx;

  for (idx = 0; idx < MAX_SHGEO_SCAN_RECORDS; idx++) {
    if (value_p[idx].geo_stamp == SHTIME_UNDEFINED)
      break;
  }
  if (idx == MAX_SHGEO_SCAN_RECORDS)
    return (0); /* full list */

  if (arg_nr == 2) {
    if (args[0] == NULL || args[1] == NULL)
      return (0);

    lat = (shnum_t)atof(args[0]);
    lon = (shnum_t)atof(args[1]);
    shgeo_set(value_p + idx, lat, lon, 0);
    if (value_p[idx].geo_stamp == SHTIME_UNDEFINED)
      value_p[idx].geo_stamp = shtime();
  }

  return (0);
}
Beispiel #11
0
static void shproc_state_set(shproc_t *proc, int state)
{
  int ostate = proc->proc_state;

  if (state < 0 || state >= MAX_SHPROC_STATES)
    return;

  if (proc->proc_state == state)
    return;

  if (proc->proc_stamp) {
    proc->stat.span_tot[ostate] += (shtimef(shtime()) - shtimef(proc->proc_stamp));
    proc->stat.span_cnt[ostate]++;
  }

  proc->proc_stamp = shtime();
  proc->proc_state = state;
}
Beispiel #12
0
void shlog_write(shbuf_t *buff, int level, int err_code, char *log_str)
{
  static char log_path[PATH_MAX+1];
  char line[640];
  char *beg_line;
  size_t mem_size;

  if (!buff)
    return;

  if (!*log_path) {
		char *label;
    shpeer_t peer;

    memcpy(&peer, ashpeer(), sizeof(peer));
		label = (!*peer.label ? PACKAGE_NAME : peer.label);
		sprintf(log_path, "%s%s.log", shlog_path(label), label); 
  }
  if (*log_path && !_shlog_file) {
    _shlog_file = fopen(log_path, "ab");
  }

  beg_line = shbuf_data(buff) + shbuf_size(buff);

  sprintf(line, "%s", shstrtime(shtime(), "[%x %T] "));
  shbuf_catstr(buff, line);

  if (level == SHLOG_ERROR) {
    shbuf_catstr(buff, "error");
  } else if (level == SHLOG_WARNING) {
    shbuf_catstr(buff, "warning");
  } else {
    shbuf_catstr(buff, "info");
  }

  if (err_code) {
    memset(line, 0, sizeof(line));
    snprintf(line, sizeof(line) - 1,
        ": %s [code %d]", strerror(-(err_code)), (err_code));
    shbuf_catstr(buff, line);
  }

  if (log_str) {
    shbuf_catstr(buff, ": ");
    shbuf_catstr(buff, log_str);
  }

  mem_size = shlog_mem_size();
  if (mem_size > 100000) {
    sprintf(line, " (mem:%dm)", (mem_size / 1000)); 
    shbuf_catstr(buff, line);
  }

  shbuf_catstr(buff, "\n");

}
Beispiel #13
0
int shcert_init_default(shcert_t *cert)
{
  int i;

  /* certificate version */
  cert->cert_ver = 3;

  /* certificate's issuer peer entity */
  memcpy(&cert->cert_sub.ent_peer, ashpeer(), sizeof(cert->cert_sub.ent_peer));

  /* set birth and expiration time-stamps */
  cert->cert_sub.ent_sig.sig_stamp = shtime_adj(shtime(), -1);
  cert->cert_sub.ent_sig.sig_expire = 
    shtime_adj(shtime(), SHARE_DEFAULT_EXPIRE_TIME);

  /* fill with random serial number */
  shcert_init_serial(shcert_sub_ser(cert));

}
Beispiel #14
0
void shgeo_set(shgeo_t *geo, shnum_t lat, shnum_t lon, int alt)
{

  alt = MIN(alt, 1584000000);

  geo->geo_stamp = shtime();
  shnum_set((shnum_t)lat, &geo->geo_lat);
  shnum_set((shnum_t)lon, &geo->geo_lon);
  geo->geo_alt = (uint32_t)alt;

}
Beispiel #15
0
void
dcc_partyline(sock_t *sender, char *msg)
{
        sock_t *sock = main_sock;
        char msgbuf[BUFSIZE];

        snprintf(msgbuf, BUFSIZE, "(%s) (%s) %s\n", shtime(), sender->name, msg);
        for (; sock; sock = sock->next)
                if (sock->flags & (SOCK_DCC|SOCK_TELNET) && sock->flags & SOCK_AUTH &&
                    (sock != sender || sender->flags & SOCK_DCC_ECHO))
                        write(sock->socket, msgbuf, strlen(msgbuf));
}
Beispiel #16
0
int tx_send(shpeer_t *cli_peer, tx_t *tx)
{
  unsigned char *data = (unsigned char *)tx;
  size_t data_len;
  tx_ledger_t *l;
  int err;
  txop_t *op;

  if (!data)
    return (SHERR_INVAL);

  data_len = get_tx_size(tx);
  if (!data_len)
    return (0);

  op = get_tx_op(tx->tx_op);
  if (!op)
    return (SHERR_INVAL);

  if (op->op_send) {
    err = op->op_send(cli_peer, data);
    if (err) {
      if (err == SHERR_OPNOTSUPP)
        return (0);

      return (err);
    }
  }

  if (is_tx_stored(tx->tx_op)) {
    l = ledger_load(shpeer_kpriv(sharedaemon_peer()), shtime());
    if (l) {
      ledger_tx_add(l, (tx_t *)data);   
      ledger_close(l);
    }
  }

#if 0
  /* encapsulate for network transfer. */
  tx_wrap(shpeer_kpriv(cli_peer), (tx_t *)tx);
#endif

  if (cli_peer) {
    sched_tx_sink(shpeer_kpriv(cli_peer), data, data_len);
  } else {
    sched_tx(data, data_len);
  }

  return (0);
}
Beispiel #17
0
void generate_asset_signature(tx_asset_t *asset, shpeer_t *peer)
{
  shkey_t *sig_key;
  uint64_t crc;

  if (asset->ass.ass_expire == SHTIME_UNDEFINED)
    asset->ass.ass_expire = shtime_adj(shtime(), SHARE_DEFAULT_EXPIRE_TIME); 

  crc = shcrc((unsigned char *)asset->ass_data, asset->ass_size);
  sig_key = shkey_cert(shpeer_kpriv(peer), crc, asset->ass.ass_expire);
  memcpy(&asset->ass.ass_sig, sig_key, sizeof(shkey_t));
  shkey_free(&sig_key);

}
Beispiel #18
0
void shmap_print(shmap_t *h, shbuf_t *ret_buff)
{
  shmap_entry_t *ent;
  shmap_value_t mval;
  shmap_index_t *hi;
  shkey_t *key;
  char buf[256];
  char *val;
  char str[4096];
  ssize_t len;
  int flag;
  int idx;
  int i;

  if (!h || !ret_buff)
    return; /* all done */

  i = 0;

  for (hi = shmap_first(h); hi; hi = shmap_next(hi)) {
    shmap_self(hi, &key, &val, &len, &flag);
    if (!len || !val)
      continue;

    flag &= ~SHMAP_ALLOC;

    memset(&mval, 0, sizeof(mval));
    memcpy(&mval.name, key, sizeof(mval.name));
    mval.magic = SHMEM32_MAGIC;
    mval.stamp = shtime();
    mval.crc = shcrc(val, len); 
    mval.pf = flag;
    mval.sz = len;

    shbuf_cat(ret_buff, &mval, sizeof(shmap_value_t));
    shbuf_cat(ret_buff, val, len);

#if 0
    hdr = (shmap_value_t *)val;
    memcpy(&hdr->name, key, sizeof(shkey_t));
    shbuf_cat(ret_buff, hdr, sizeof(shmap_value_t));
    shbuf_cat(ret_buff, ((char *)val + sizeof(shmap_value_t)), hdr->sz);
#endif

    i++;
  }

}
Beispiel #19
0
int shpam_shadow_session(shfs_ino_t *file, shseed_t *seed, shkey_t **sess_p, shtime_t *expire_p)
{
  shadow_t *ent;
  shadow_t save;
  shkey_t *sess_key;
  shkey_t *ret_key;
  shkey_t *seed_key;
  shtime_t stamp;
  shtime_t now;
  uint64_t crc;
  int err;

  if (!file->tree)
    return (SHERR_INVAL);

  err = shpam_shadow_load(file, seed->seed_uid, &save);
  if (err) {
    return (err);
}

  now = shtime();
  if (shtime_after(now, save.sh_expire)) {
    /* generate new session key with default expiration */
    stamp = shtime_adj(now, MAX_SHARE_SESSION_TIME);
    sess_key = _shpam_shadow_session_gen(seed, &save.sh_id, stamp); 
    if (!sess_key)
      return (SHERR_KEYREVOKED);

    save.sh_expire = stamp;
    memcpy(&save.sh_sess, sess_key, sizeof(save.sh_sess));
    err = shpam_shadow_store(file, &save);
    shkey_free(&sess_key);
    if (err) {
      return (err);
}
  }

  if (expire_p)
    *expire_p = save.sh_expire;

  if (sess_p) {
    ret_key = (shkey_t *)calloc(1, sizeof(shkey_t));
    memcpy(ret_key, &save.sh_sess, sizeof(shkey_t));
    *sess_p = ret_key;
  }

  return (0);
}
Beispiel #20
0
void
alldcc(char *tosend, ...)
{
	sock_t *sock = main_sock;
	char msgbuf[BUFSIZE], sendbuf[BUFSIZE], *temp = shtime();
	va_list ap;

	va_start(ap, tosend);
	leet_vsprintf(msgbuf, tosend, ap);
	for (; sock; sock = sock->next)
		if (sock->flags & (SOCK_DCC|SOCK_TELNET) && sock->flags & SOCK_AUTH) {
			memset(sendbuf, 0, BUFSIZE);
			snprintf(sendbuf, BUFSIZE, "(%s) %s\n", temp, msgbuf);
			write(sock->socket, sendbuf, strlen(sendbuf));
		}
	va_end(ap);
}
Beispiel #21
0
/**
 * Manually set the device's current location.
 */
void shgeo_local_set(shgeo_t *geo)
{
  shnum_t lat, lon;
  char buf[256];

  if (!geo)
    return;

  /* set in-memory */
  memcpy(&_local_geo_index, geo, sizeof(_local_geo_index));
  _local_geo_index.geo_stamp = shtime();

  /* set persistent */
  shgeo_loc(geo, &lat, &lon, NULL);
  sprintf(buf, "%Lf,%Lf", lat, lon);
  shpref_set(SHPREF_ACC_GEO, buf);
}
Beispiel #22
0
/** Associated a particular context with releasing a ward. */
void txward_context_sign(tx_ward_t *ward, tx_context_t *ctx)
{
  shkey_t *sig_key;

  if (!ward || !ctx)
    return;

  if (!shkey_cmp(&ward->ward_ref, &ctx->ctx_ref))
    return (SHERR_INVAL);

  if (ward->ward_tx.tx_stamp == SHTIME_UNDEFINED)
    ward->ward_tx.tx_stamp = shtime();

  sig_key = shkey_cert(&ctx->ctx_sig,
      shkey_crc(&ctx->ctx_ref), ward->ward_tx.tx_stamp);
  memcpy(&ward->ward_sig, sig_key, sizeof(ward->ward_sig));
  shkey_free(&sig_key);

}
Beispiel #23
0
int shpam_shadow_session_expire(shfs_ino_t *file, uint64_t uid, shkey_t *sess_key)
{
  shadow_t *ent;
  shadow_t save;
  int err;

  err = shpam_shadow_load(file, uid, &save);
  if (err)
    return (err);

  if (shtime_after(shtime(), save.sh_expire))
    return (SHERR_KEYEXPIRED);
  if (!shkey_cmp(&save.sh_sess, sess_key))
    return (SHERR_KEYREJECTED);

  save.sh_expire = 0;
  err = shpam_shadow_store(file, &save);
  if (err)
    return (err);

  return (0);
}
Beispiel #24
0
int shcert_verify(shcert_t *cert, shcert_t *parent)
{
  shtime_t now;
  int err;

  if (!(cert->cert_flag & SHCERT_CERT_CHAIN)) {
    /* initial (CA) chain entity */
    if (parent)
      return (SHERR_INVAL);

    return (0);
  }

  /* supplemental chain entity */
  if (!parent)
    return (SHERR_INVAL);

  /* The Issuer of each certificate (except the last one) matches the Subject of the next (parent) certificate in the list. */
  if (0 != strcasecmp(cert->cert_iss.ent_name, 
        parent->cert_sub.ent_name)) {
    return (SHERR_ACCESS); 
  }

  now = shtime();
  if (!shtime_after(now, shcert_sub_stamp(cert))) {
    return (SHERR_ACCESS);
  }
  if (!shtime_before(now, shcert_sub_expire(cert))) {
    return (SHERR_KEYEXPIRED);
  }

  /* The signature of one certificate can be verified using the public key contained in the following certificate. */
  err = shcert_sign_verify(cert, parent); 
  if (err)
    return (err);

  return (0);
}
Beispiel #25
0
time_t shgeo_lifespan(shgeo_t *geo)
{
  return ((time_t)shtime_diff(shtime(), geo->geo_stamp));
}
Beispiel #26
0
static int _lfunc_sexe_time(lua_State *L)
{
  lua_pushnumber(L, shtimef(shtime()));
  return 1; /* 'time' */
}
Beispiel #27
0
int sharedaemon_bcast_recv(void)
{
    struct sockaddr_in addr;
    socklen_t addr_len;
    struct timeval to;
    fd_set read_set;
    shpeer_t *peer;
    char dgram[512];
    ssize_t r_len;
    int err;

    err = bcast_recv_init();
    if (err) {
        return (err);
    }

    FD_ZERO(&read_set);
    FD_SET(_bcast_recv_fd, &read_set);

    /* nonblocking read */
    memset(&to, 0, sizeof(to));
    err = select(_bcast_recv_fd+1, &read_set, NULL, NULL, &to);
    if (err < 0) {
        return (-errno);
    }
    if (err == 0) {
//fprintf(stderr, "\rWaiting for select(_bcast_recv_fd)..");
//fflush(stderr);
        return (0); /* nothing to read */
    }

    addr_len = sizeof(addr);
    memset(&addr, 0, addr_len);
    r_len = recvfrom(_bcast_recv_fd,
                     dgram, sizeof dgram, 0, &addr, &addr_len);
    if (r_len < 0) {
        fprintf(stderr, "DEBUG: %d = recvfrom()\n", r_len);
        return (-errno);
    }

    /* and who are you? */
    if (r_len < sizeof(shpeer_t)) {
        fprintf(stderr, "DEBUG: <%d bytes> pending..\n", r_len);
        return (SHERR_INVAL);
    }

#if 0
    now = shtime();
    tx = (tx_t *)dgram;
    if (shtime_after(tx->tx_stamp, now) ||
            shtime_before(tx->tx_stamp, shtime_adj(now, -BROADCAST_TIMEOUT))) {
        /* broadcast message must indicate sane time-frame. */
        return (SHERR_TIME);
    }

    switch (tx->tx_op) {
    case TX_PEER:
        peer_tx = (tx_peer_t *)dgram;
        if (0 != shkey_cmp(&tx->tx_peer, shpeer_kpriv(&peer_tx->peer)))
            return (SHERR_INVAL); /* only accept self-referencing broadcast */
    }
#endif


    /* share-daemon broadcasting it's peer address. */
    peer = (shpeer_t *)dgram;

    if (!shkey_cmp(shpeer_kpub(sharedaemon_peer()), shpeer_kpub(peer))) {
        /* this is not a shared peer */
        return (0); /* all done */
    }

    if (!shkey_cmp(shpeer_kpub(sharedaemon_peer()), shpeer_kpub(peer))) {
        fprintf(stderr, "DEBUG: invalid key\n");
        /* this is a peer referencing ourselves. */
        //err = sharedaemon_netclient_alias(&addr);
    }



    switch (peer->type) {
    case SHNET_PEER_LOCAL:
    case SHNET_PEER_IPV4:

        /*
           memset(&addr, '\000', sizeof(struct sockaddr_in));
           memcpy(&addr, &peer_tx->peer.addr, sizeof(peer_tx->peer.addr));
           */
        fprintf(stderr, "DEBUG: received UDP broadcast with peer \"%s\"\n", shpeer_print(peer));
        fprintf(stderr, "DEBUG: received UDP broadcast for \"%s\" port %d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
        if (!peer->addr.sin_port)
            break; /* otay */

        addr.sin_family = AF_INET;
        err = sharedaemon_netclient_conn(peer, &addr);
        if (err)
            return (err);

        break;
    }
    fprintf(stderr, "DEBUG: processed bcast recv\n");

    return (0);
}
Beispiel #28
0
int tx_recv(shpeer_t *cli_peer, tx_t *tx)
{
  tx_ledger_t *l;
  tx_t *rec_tx;
  txop_t *op;
  int err;

  if (!tx)
    return (SHERR_INVAL);

#if 0
  if (ledger_tx_load(shpeer_kpriv(cli_peer), tx->hash, tx->tx_stamp)) {
fprintf(stderr, "DEBUG: tx_recv: skipping duplicate tx '%s'\n", tx->hash); 
    return (SHERR_NOTUNIQ);
}
#endif

  op = get_tx_op(tx->tx_op);
  if (!op)
    return (SHERR_INVAL);

  if (tx->tx_flag & TXF_WARD) {
    err = txward_confirm(tx); 
    if (err)
      return (err);
  }


/* check for dup in ledger (?) */

  rec_tx = (tx_t *)tx_load(tx->tx_op, get_tx_key(tx));
  if (!rec_tx) {
    rec_tx = (tx_t *)calloc(1, op->op_size);
    if (!rec_tx)
      return (SHERR_NOMEM);

    memcpy(rec_tx, tx, op->op_size);
#if 0 
    err = tx_init(cli_peer, rec_tx);
    if (err) {
      pstore_free(rec_tx);
      return (err);
    }
#endif

    err = tx_save(rec_tx);
    if (err) {
      pstore_free(rec_tx);
      return (err);
    }
  }

  if (op->op_recv) {
    err = op->op_recv(cli_peer, tx);
    if (err) {
      pstore_free(rec_tx);
      return (err);
    }
  }

  pstore_free(rec_tx);

  if (is_tx_stored(tx->tx_op)) {
    l = ledger_load(shpeer_kpriv(cli_peer), shtime());
    if (l) {
      ledger_tx_add(l, tx);
      ledger_close(l);
    }
  }

  return (0);
}