static int blockstore_set_chain_links(struct blockstore *bs, struct blockentry *be) { struct blockentry *prev; char hashStr[80]; uint256 hash; bool s; hash256_calc(&be->header, sizeof be->header, &hash); uint256_snprintf_reverse(hashStr, sizeof hashStr, &hash); if (be->height >= 0) { struct blockentry *li; /* * We've reached the junction with the current/old best chain. All the * entries from now on need to be made orphans. */ Log(LGPFX" Reached block %s\n", hashStr); li = be->next; while (li) { hash256_calc(&li->header, sizeof li->header, &hash); uint256_snprintf_reverse(hashStr, sizeof hashStr, &hash); Log(LGPFX" moving #%d %s from blk -> orphan\n", li->height, hashStr); s = hashtable_remove(bs->hash_blk, &hash, sizeof hash); ASSERT(s); li->height = -1; s = hashtable_insert(bs->hash_orphans, &hash, sizeof hash, li); ASSERT(s); li = li->next; } return be->height; } ASSERT(be->height == -1); // orphan prev = blockstore_lookup(bs, &be->header.prevBlock); ASSERT(prev); be->height = 1 + blockstore_set_chain_links(bs, prev); Log(LGPFX" moving #%d %s from orphan -> blk\n", be->height, hashStr); prev->next = be; be->prev = prev; s = hashtable_remove(bs->hash_orphans, &hash, sizeof hash); ASSERT(s); s = hashtable_insert(bs->hash_blk, &hash, sizeof hash, be); ASSERT(s); return be->height; }
void blockstore_get_locator_hashes(const struct blockstore *bs, uint256 **hash, int *num) { volatile struct blockentry *be; uint256 h[64]; uint32 step = 1; int n = 0; *hash = NULL; *num = 0; be = bs->best_chain; while (be) { int i; ASSERT(n < ARRAYSIZE(h)); hash256_calc((void *)&be->header, sizeof be->header, h + n); n++; if (n >= 10) { step *= 2; } for (i = 0; i < step && be; i++) { be = be->prev; } } *num = n; if (n > 0) { *hash = safe_malloc(n * sizeof(uint256)); memcpy(*hash, h, n * sizeof(uint256)); } }
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_next_hashes(struct blockstore *bs, const uint256 *start, uint256 **hash, int *n) { struct blockentry *be; uint256 *table; int num; bool s; int i; i = 0; table = NULL; s = hashtable_lookup(bs->hash_blk, start, sizeof *start, (void*)&be); if (s == 0 || be->next == NULL) { goto exit; } be = be->next; num = 1000; table = safe_malloc(num * sizeof *table); while (be && i < num) { hash256_calc(&be->header, sizeof be->header, table + i); i++; be = be->next; } exit: *n = i; *hash = table; }
bool blockstore_add_header(struct blockstore *bs, const btc_block_header *hdr, const uint256 *hash, bool *orphan) { static unsigned int count; struct blockentry *be; *orphan = 0; /* * The caller is supposed to compute the checksum of the header it's adding. * Verify that it doesn't get this wrong every few blocks. */ count++; if ((count % 32) == 0) { uint256 hash0; hash256_calc(hdr, sizeof *hdr, &hash0); ASSERT(uint256_issame(hash, &hash0)); } be = blockstore_lookup(bs, hash); if (be) { return 0; } ASSERT(blockstore_validate_chkpt(hash, bs->height + 1)); ASSERT(bs->best_chain || uint256_issame(hash, &bs->genesis_hash)); be = blockstore_alloc_entry(hdr); blockstore_add_entry(bs, be, hash); *orphan = be->height == -1; return 1; }
static void script_tx_sighash(struct wallet *wallet, uint256 *hash, const struct buff *scriptPubKey, const struct btc_msg_tx *tx, uint32 idx, enum script_hash_type hashType) { struct btc_msg_tx *tx2; struct buff *buf; int i; ASSERT(idx < tx->in_count); memset(hash, 0, sizeof *hash); tx2 = btc_msg_tx_dup(tx); Log(LGPFX " Computing sighash for txi-%u/%llu\n", idx, tx2->in_count); /* * Zero-out all the inputs' signatures. */ for (i = 0; i < tx2->in_count; i++) { tx2->tx_in[i].scriptLength = 0; } size_t len = buff_maxlen(scriptPubKey); ASSERT(len > 0); ASSERT(tx2->tx_in[idx].scriptSig == NULL); ASSERT(tx2->tx_in[idx].scriptLength == 0); tx2->tx_in[idx].scriptLength = len; tx2->tx_in[idx].scriptSig = safe_malloc(len); memcpy(tx2->tx_in[idx].scriptSig, buff_base(scriptPubKey), len); ASSERT((hashType & 0x1f) == SIGHASH_ALL); /* * Final step: * * Serialize tx + hashType (as a uint32) and compute hash. */ buf = buff_alloc(); serialize_tx(buf, tx2); serialize_uint32(buf, hashType); hash256_calc(buff_base(buf), buff_curlen(buf), hash); buff_free(buf); btc_msg_tx_free(tx2); free(tx2); }
bool blockstore_is_next(struct blockstore *bs, const uint256 *prev, const uint256 *next) { struct blockentry *be; uint256 hash; bool s; s = hashtable_lookup(bs->hash_blk, prev, sizeof *prev, (void*)&be); if (s == 0 || be->next == NULL) { return 0; } hash256_calc(&be->next->header, sizeof be->next->header, &hash); return uint256_issame(&hash, next); }
void blockstore_get_hash_from_birth(const struct blockstore *bs, time_t birth, uint256 *hash) { struct blockentry *e; for (e = bs->best_chain; e != bs->genesis; e = e->prev) { if (e->header.timestamp < birth) { char hashStr[80]; uint64 ts = birth; hash256_calc(&e->header, sizeof e->header, hash); uint256_snprintf_reverse(hashStr, sizeof hashStr, hash); Log(LGPFX" birth %llu --> block %s.\n", ts, hashStr); return; } } memcpy(hash, &bs->genesis_hash, sizeof *hash); }
bool blockstore_get_block_at_height(struct blockstore *bs, int height, uint256 *hash, btc_block_header *header) { struct blockentry *e; bool s = 0; mutex_lock(bs->lock); ASSERT(height <= bs->height); for (e = bs->best_chain; e != bs->genesis; e = e->prev) { if (e->height == height) { hash256_calc(&e->header, sizeof e->header, hash); *header = e->header; s = 1; break; } } mutex_unlock(bs->lock); return s; }
static int blockset_open_file(struct blockstore *blockStore, struct blockset *bs) { uint64 offset; mtime_t ts; int res; res = file_open(bs->filename, 0 /* R/O */, 0 /* !unbuf */, &bs->desc); if (res) { return res; } bs->filesize = file_getsize(bs->desc); if (bs->filesize < 0) { return errno; } if (bs->filesize > 0) { char *s = print_size(bs->filesize); char *name = file_getname(bs->filename); Log(LGPFX" reading file %s -- %s -- %llu headers.\n", name, s, bs->filesize / sizeof(btc_block_header)); free(name); free(s); } ts = time_get(); offset = 0; while (offset < bs->filesize) { btc_block_header buf[10000]; size_t numRead; size_t numBytes; int numHeaders; int i; numBytes = MIN(bs->filesize - offset, sizeof buf); res = file_pread(bs->desc, offset, buf, numBytes, &numRead); if (res != 0) { break; } if (btc->stop != 0) { res = 1; NOT_TESTED(); break; } numHeaders = numRead / sizeof(btc_block_header); for (i = 0; i < numHeaders; i++) { struct blockentry *be; uint256 hash; be = blockstore_alloc_entry(buf + i); be->written = 1; hash256_calc(buf + i, sizeof buf[0], &hash); if (!blockstore_validate_chkpt(&hash, blockStore->height + 1)) { return 1; } blockstore_add_entry(blockStore, be, &hash); if (i == numHeaders - 1) { bitcui_set_status("loading headers .. %llu%%", (offset + numBytes) * 100 / bs->filesize); } if (i == numHeaders - 1 || (numBytes < sizeof buf && i > numHeaders - 256)) { bitcui_set_last_block_info(&hash, blockStore->height, be->header.timestamp); } } offset += numRead; } ts = time_get() - ts; char hashStr[80]; char *latStr; uint256_snprintf_reverse(hashStr, sizeof hashStr, &blockStore->best_hash); Log(LGPFX" loaded blocks up to %s\n", hashStr); latStr = print_latency(ts); Log(LGPFX" this took %s\n", latStr); free(latStr); return res; }
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; }
int txdb_craft_tx(struct txdb *txdb, const struct btc_tx_desc *tx_desc, btc_msg_tx *tx) { struct buff *buf; char hashStr[80]; uint256 txHash; uint64 change; uint32 numCoins; bool relevant; mtime_t ts; int res; res = 0; relevant = 0; tx->version = 1; txdb_prepare_txout(tx_desc, tx); /* * In order to properly size 'tx->txIn', we need to determine how many coins * we're going to use. Right now, let's just vastly overestimate. */ numCoins = hashtable_getnumentries(txdb->hash_txo); tx->tx_in = safe_calloc(numCoins, sizeof *tx->tx_in); txdb_print_coins(txdb, 1); txdb_select_coins(txdb, tx_desc, tx, &change); /* * Change! XXX: fix me. */ if (change > 0) { const char *btc_change; btc_change = wallet_get_change_addr(btc->wallet); tx->out_count++; txdb_set_txo(tx, tx->out_count - 1, btc_change, change); Warning(LGPFX" change: %llu -- %.8f BTC\n", change, change / ONE_BTC); } txdb_sign_tx_inputs(txdb, tx); /* * Now that the tx is ready, serialize it and check that it's not too big. */ btcmsg_print_tx(tx); buf = buff_alloc(); serialize_tx(buf, tx); if (buff_curlen(buf) > BTC_TX_MAX_SIZE) { Warning(LGPFX" tx too large: %zu\n", buff_curlen(buf)); res = 1; goto exit; } hash256_calc(buff_base(buf), buff_curlen(buf), &txHash); uint256_snprintf_reverse(hashStr, sizeof hashStr, &txHash); Warning(LGPFX" %s (%zu bytes)\n", hashStr, buff_curlen(buf)); Log_Bytes(LGPFX" TX: ", buff_base(buf), buff_curlen(buf)); if (bitc_testing) { Warning("TESTING! Not saving/relaying tx.\n"); goto exit; } ts = time(NULL); res = txdb_remember_tx(txdb, 0 /* save to disk */, ts, buff_base(buf), buff_curlen(buf), &txHash, NULL, &relevant); txdb_save_tx_label(tx_desc, hashStr); txdb_export_tx_info(txdb); res = peergroup_new_tx_broadcast(btc->peerGroup, buf, ts + 2 * 60 * 60, &txHash); if (res) { Warning(LGPFX" failed to transmit tx: %d\n", res); bitcui_set_status("got errors while broadcasting tx"); } exit: buff_free(buf); /* * XXX: We should mark the coins used by this tx as "reserved", so that we * do not attempt to use conflicting coins in subsequent TXs. */ return res; }