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); }
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); } }
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; }
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; }
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); }
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; }
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; }
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; }
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; }
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); }