Beispiel #1
0
int
txdb_handle_tx(struct txdb   *txdb,
               const uint256 *blkHash,
               const uint8   *buf,
               size_t         len,
               bool          *relevant)
{
    uint256 txHash;
    mtime_t ts = 0;
    bool txKnown;

    *relevant = 0;
    hash256_calc(buf, len, &txHash);

    txKnown = txdb_has_tx(txdb, &txHash);

    if (!uint256_iszero(blkHash)) {
        txdb_confirm_one_tx(txdb, blkHash, &txHash);
    }

    if (txKnown) {
        return 0;
    }

    if (uint256_iszero(blkHash)) {
        ts = time(NULL);
    } else {
        ts = blockstore_get_block_timestamp(btc->blockStore, blkHash);
    }

    return txdb_remember_tx(txdb, 0 /* save to disk */, ts, buf, len,
                            &txHash, blkHash, relevant);
}
Beispiel #2
0
void
blockstore_get_highest(struct blockstore *bs,
                       const uint256 *hash0,
                       const uint256 *hash1,
                       uint256 *hash)
{
   int height0;
   int height1;

   if (uint256_iszero(hash0)) {
      memcpy(hash, hash1, sizeof *hash1);
      return;
   }
   if (uint256_iszero(hash1)) {
      memcpy(hash, hash0, sizeof *hash0);
      return;
   }

   height0 = blockstore_get_block_height(bs, hash0);
   height1 = blockstore_get_block_height(bs, hash1);
   ASSERT(height0 > 0);
   ASSERT(height1 > 0);

   if (height0 < height1) {
      memcpy(hash, hash1, sizeof *hash1);
   } else {
      memcpy(hash, hash0, sizeof *hash0);
   }
}
Beispiel #3
0
int blockstore_get_block_height(struct blockstore *bs, const uint256 *hash) {
  struct blockentry *be;
  int height;
  bool s;

  if (uint256_iszero(hash)) {
    return 0;
  }

  mutex_lock(bs->lock);

  s = hashtable_lookup(bs->hash_blk, hash, sizeof *hash, (void *)&be);
  if (s == 0) {
    char hashStr[80];

    uint256_snprintf_reverse(hashStr, sizeof hashStr, hash);
    Panic(LGPFX " block %s not found.\n", hashStr);
  }

  height = be->height;

  mutex_unlock(bs->lock);

  return height;
}
Beispiel #4
0
time_t blockstore_get_block_timestamp(const struct blockstore *bs,
                                      const uint256 *hash) {
  struct blockentry *be;
  time_t ts = 0;

  mutex_lock(bs->lock);

  if (uint256_iszero(hash)) {
    goto done;
  }

  be = blockstore_lookup(bs, hash);
  if (be == NULL) {
    char hashStr[80];

    uint256_snprintf_reverse(hashStr, sizeof hashStr, hash);
    Panic(LGPFX " block %s not found.\n", hashStr);
  }

  ts = be->header.timestamp;

done:
  mutex_unlock(bs->lock);

  return ts;
}
Beispiel #5
0
static void
txdb_select_coins(struct txdb              *txdb,
                  const struct btc_tx_desc *desc,
                  btc_msg_tx               *tx,
                  uint64                   *change)
{
    struct txo_entry *txo_array;
    uint64 value;
    int txo_num;
    int i;

    i = 0;
    value = 0;
    tx->in_count = 0;
    txo_array = txdb_get_coins_sorted(txdb);
    txo_num = hashtable_getnumentries(txdb->hash_txo);

    /*
     * txo_array is sorted in chronological order, so we'll be consuming old
     * coins first.
     */
    Log(LGPFX" select_coins: total_value=%llu fee=%llu\n",
        desc->total_value, desc->fee);

    while (value < (desc->total_value + desc->fee) && i < txo_num) {
        struct txo_entry *txo_ent = &txo_array[i++];
        char hashStr[80];

        if (txo_ent->spent == 1 ||
                txo_ent->spendable == 0) {
            continue;
        }
        if (uint256_iszero(&txo_ent->blkHash)) {
            NOT_TESTED();
            continue;
        }

        uint256_snprintf_reverse(hashStr, sizeof hashStr, &txo_ent->txHash);
        Log(LGPFX" using txo for %s id=%3u of %s\n",
            txo_ent->btc_addr, txo_ent->outIdx, hashStr);
        value += txo_ent->value;
        memcpy(&tx->tx_in[tx->in_count].prevTxHash, &txo_ent->txHash, sizeof txo_ent->txHash);
        tx->tx_in[tx->in_count].prevTxOutIdx = txo_ent->outIdx;
        tx->tx_in[tx->in_count].sequence = UINT_MAX;
        tx->in_count++;
    }

    ASSERT(value >= desc->total_value);
    *change = value - desc->total_value - desc->fee;
    Log(LGPFX" change=%llu\n", *change);
    free(txo_array);
}
Beispiel #6
0
static void
txdb_export_tx_cb(const void *key,
                  size_t keyLen,
                  void *cbData,
                  void *keyData)
{
    struct bitcui_tx **txiPtr = (struct bitcui_tx **)cbData;
    struct tx_entry *txe = (struct tx_entry *)keyData;
    struct bitcui_tx *txi = *txiPtr;
    char hashStr[80];

    if (txe->relevant == 0) {
        return;
    }

    /*
     * We need to weed out transactions that made it in an orphan block but were
     * not integrated later on in the main chain.
     */

    txi->src  = NULL;
    txi->dst  = NULL;
    txi->desc = NULL;

    ASSERT(keyLen == sizeof(uint256));
    memcpy(&txi->txHash, key, keyLen);
    txi->value  = txdb_get_tx_credit(&txe->tx);
    txi->value -= txdb_get_tx_debit(&txe->tx);

    txi->blockHeight = -1;
    if (!uint256_iszero(&txe->blkHash)) {
        txi->blockHeight = blockstore_get_block_height(btc->blockStore,
                           &txe->blkHash);
    }

    uint256_snprintf_reverse(hashStr, sizeof hashStr, (uint256*)key);
    txi->desc = config_getstring(btc->txLabelsCfg, NULL, "tx.%s.label", hashStr) ;
    /*
     * This is a workaround for a bug caused by truncated hashStr.
     */
    if (txi->desc == NULL) {
        hashStr[63] = '\0';
        txi->desc = config_getstring(btc->txLabelsCfg, NULL, "tx.%s.label", hashStr) ;
    }

    txi->timestamp = txe->timestamp;
    *txiPtr += 1;
}
Beispiel #7
0
time_t
blockstore_get_block_timestamp(const struct blockstore *bs,
                               const uint256 *hash)
{
   struct blockentry *be;

   if (uint256_iszero(hash)) {
      return 0;
   }

   be = blockstore_lookup(bs, hash);
   if (be == NULL) {
      char hashStr[80];

      uint256_snprintf_reverse(hashStr, sizeof hashStr, hash);
      Warning(LGPFX" block %s not found.\n", hashStr);
      ASSERT(0);
      return 0;
   }

   return be->header.timestamp;
}
Beispiel #8
0
int
blockstore_get_block_height(struct blockstore *bs,
                            const uint256 *hash)
{
   struct blockentry *be;
   bool s;

   if (uint256_iszero(hash)) {
      return 0;
   }

   s = hashtable_lookup(bs->hash_blk, hash, sizeof *hash, (void*)&be);
   if (s == 0) {
      char hashStr[80];

      uint256_snprintf_reverse(hashStr, sizeof hashStr, hash);
      Warning(LGPFX" block %s not found.\n", hashStr);
      //ASSERT(0);
      return 0;
   }

   return be->height;
}
Beispiel #9
0
static int
txdb_load_tx(struct txdb *txdb,
             const char  *key,
             size_t       klen,
             const char  *val,
             size_t       vlen)
{
    struct tx_ser_data *txd;
    struct tx_ser_key *txk;
    char hashStr[80];
    bool relevant = 0;
    uint256 txHash;
    bool confirmed;
    int res = 0;

    ASSERT(strncmp(key, "/tx/", 4) == 0);

    txk = txdb_deserialize_tx_key(key, klen);
    txd = txdb_deserialize_tx_data(val, vlen);

    ASSERT(txdb->tx_seq == txk->seq);
    txdb->tx_seq++;

    confirmed = !uint256_iszero(&txd->blkHash);

    hash256_calc(txd->buf, txd->len, &txHash);
    ASSERT(uint256_issame(&txHash, &txk->txHash));
    uint256_snprintf_reverse(hashStr, sizeof hashStr, &txHash);
    LOG(1, (LGPFX" loading %ctx %s\n", confirmed ? 'c' : 'u', hashStr));

    res = txdb_remember_tx(txdb, 1 /* not on disk, just hashtable */,
                           txd->timestamp, txd->buf, txd->len,
                           &txk->txHash, &txd->blkHash, &relevant);

    /*
     * If the transaction is still unconfirmed, add to relay set.
     */
    if (!confirmed) {
        struct buff buf;
        int numSec;

        numSec = time(NULL) - txd->timestamp;

        if (numSec > 0) {
            int hours = numSec / (60 * 60);
            int min  = (numSec % (60 * 60)) / 60;

            Log(LGPFX" unconfirmed tx %s was sent %d hours %d min ago.\n",
                hashStr, hours, min);
        }

        buff_init(&buf, txd->buf, txd->len);

        Log(LGPFX" adding tx %s to relay-set\n", hashStr);
        peergroup_new_tx_broadcast(btc->peerGroup, &buf,
                                   txd->timestamp + 2 * 60 * 60,
                                   &txHash);
    } else {
        uint256_snprintf_reverse(hashStr, sizeof hashStr, &txd->blkHash);
        Log(LGPFX" tx in block %s\n", hashStr);
    }

    free(txd->buf);
    free(txd);
    free(txk);

    return res;
}
Beispiel #10
0
void
txdb_confirm_one_tx(struct txdb   *txdb,
                    const uint256 *blkHash,
                    const uint256 *txHash)
{
    leveldb_iterator_t *iter;
    struct tx_entry *txe;
    char bkHashStr[80];
    char txHashStr[80];

    ASSERT(!uint256_iszero(blkHash));
    ASSERT(!uint256_iszero(txHash));

    txe = txdb_get_tx_entry(txdb, txHash);
    if (txe == NULL) {
        return;
    }

    if (txe->relevant == 0) {
        txdb_remove_from_hashtable(txdb, txHash);
        NOT_TESTED();
        return;
    }

    if (!uint256_iszero(&txe->blkHash)) {
        /*
         * It's possible for the ASSERT below to fire if a tx is confirmed in
         * a block that is later orphaned. The tx should then be relayed again
         * until it finds its way in a new block.
         */
        ASSERT(uint256_issame(&txe->blkHash, blkHash));
        return;
    }

    peergroup_stop_broadcast_tx(btc->peerGroup, txHash);
    memcpy(&txe->blkHash, blkHash, sizeof *blkHash);

    uint256_snprintf_reverse(bkHashStr, sizeof bkHashStr, blkHash);
    uint256_snprintf_reverse(txHashStr, sizeof txHashStr, txHash);
    Warning(LGPFX" %s confirmed in %s\n", txHashStr, bkHashStr);

    NOT_TESTED();

    iter = leveldb_create_iterator(txdb->db, txdb->rd_opts);
    leveldb_iter_seek_to_first(iter);

    while (leveldb_iter_valid(iter)) {
        struct tx_ser_key *txkey;
        struct tx_ser_data *txdata;
        struct buff *buf;
        const char *key;
        const char *val;
        size_t klen;
        size_t vlen;
        char *err = NULL;

        key = leveldb_iter_key(iter, &klen);
        txkey = txdb_deserialize_tx_key(key, klen);

        if (txkey == NULL || uint256_issame(txHash, &txkey->txHash) == 0) {
            free(txkey);
            leveldb_iter_next(iter);
            continue;
        }

        NOT_TESTED();

        val = leveldb_iter_value(iter, &vlen);
        txdata = txdb_deserialize_tx_data(val, vlen);
        ASSERT(uint256_iszero(&txdata->blkHash));
        ASSERT(txdata->timestamp != 0);
        memcpy(&txdata->blkHash, blkHash, sizeof *blkHash);

        buf = txdb_serialize_tx_data(txdata);

        leveldb_put(txdb->db, txdb->wr_opts, key, klen,
                    buff_base(buf), buff_curlen(buf), &err);
        buff_free(buf);
        if (err) {
            Warning(LGPFX" failed to write tx entry: %s\n", err);
        }

        txdb_export_tx_info(txdb);

        free(txkey);
        free(txdata->buf);
        free(txdata);
        break;
    }
    leveldb_iter_destroy(iter);
}