gboolean g_bu256_equal(gconstpointer a_, gconstpointer b_) { const bu256_t *a = a_; const bu256_t *b = b_; return bu256_equal(a, b) ? TRUE : FALSE; }
static bool bp_block_valid_merkle(struct bp_block *block) { bu256_t merkle; bp_block_merkle(&merkle, block); return bu256_equal(&merkle, &block->hashMerkleRoot); }
static bool process_block(const struct bp_block *block, int64_t fpos) { struct blkinfo *bi = bi_new(); bu256_copy(&bi->hash, &block->sha256); bp_block_copy_hdr(&bi->hdr, block); bi->n_file = 0; bi->n_pos = fpos; struct blkdb_reorg reorg; if (!blkdb_add(&db, bi, &reorg)) { fprintf(plog, "brd: blkdb add fail\n"); goto err_out; } /* FIXME: support reorg */ assert(reorg.conn == 1); assert(reorg.disconn == 0); /* if best chain, mark TX's as spent */ if (bu256_equal(&db.best_chain->hash, &bi->hdr.sha256)) { if (!spend_block(&uset, block, bi->height)) { char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &bi->hdr.sha256); fprintf(plog, "brd: block spend fail %u %s\n", bi->height, hexstr); /* FIXME: bad record is now in blkdb */ goto err_out; } } return true; err_out: bi_free(bi); return false; }
static void read_test_msg(struct blkdb *db, struct bp_utxo_set *uset, const struct p2p_message *msg, int64_t fpos) { assert(strncmp(msg->hdr.command, "block", sizeof(msg->hdr.command)) == 0); struct bp_block block; bp_block_init(&block); struct const_buffer buf = { msg->data, msg->hdr.data_len }; assert(deser_bp_block(&block, &buf) == true); bp_block_calc_sha256(&block); assert(bp_block_valid(&block) == true); struct blkinfo *bi = bi_new(); bu256_copy(&bi->hash, &block.sha256); bp_block_copy_hdr(&bi->hdr, &block); bi->n_file = 0; bi->n_pos = fpos + P2P_HDR_SZ; assert(blkdb_add(db, bi) == true); /* if best chain, mark TX's as spent */ if (bu256_equal(&db->hashBestChain, &bi->hdr.sha256)) { if (!spend_block(uset, &block, bi->height)) { char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &bi->hdr.sha256); fprintf(stderr, "chain-verf: block fail %u %s\n", bi->height, hexstr); assert(!"spend_block"); } } bp_block_free(&block); }
static bool blkdb_read_rec(struct blkdb *db, const struct p2p_message *msg) { struct blkinfo *bi; struct const_buffer buf = { msg->data, msg->hdr.data_len }; if (strncmp(msg->hdr.command, "rec", 12)) return false; bi = bi_new(); if (!bi) return false; /* deserialize record */ if (!deser_u256(&bi->hash, &buf)) goto err_out; if (!deser_bp_block(&bi->hdr, &buf)) goto err_out; /* verify that provided hash matches block header, as an additional * self-verification step */ bp_block_calc_sha256(&bi->hdr); if (!bu256_equal(&bi->hash, &bi->hdr.sha256)) goto err_out; /* verify block may be added to chain, then add it */ struct blkdb_reorg dummy; if (!blkdb_connect(db, bi, &dummy)) goto err_out; return true; err_out: bi_free(bi); return false; }
static void runtest(const char *json_base_fn, const char *ser_in_fn, const char *block_in_hash, const char *tx_in_hash) { /* read wallet data */ char *json_fn = test_filename(json_base_fn); json_t *wallet = read_json(json_fn); assert(wallet != NULL); /* read block data containing incoming payment */ char *fn = test_filename(ser_in_fn); void *data; size_t data_len; assert(bu_read_file(fn, &data, &data_len, 1 * 1024 * 1024) == true); struct bp_block block_in; bp_block_init(&block_in); struct const_buffer buf = { data, data_len }; assert(deser_bp_block(&block_in, &buf) == true); bp_block_calc_sha256(&block_in); /* verify block-in data matches expected block-in hash */ bu256_t check_hash; assert(hex_bu256(&check_hash, block_in_hash) == true); assert(bu256_equal(&block_in.sha256, &check_hash) == true); /* load key that has received an incoming payment */ struct bp_key key; assert(bp_key_init(&key) == true); load_json_key(wallet, &key); /* load key into keyset */ struct bp_keyset ks; bpks_init(&ks); assert(bpks_add(&ks, &key) == true); /* find key matches in block */ parr *matches; matches = bp_block_match(&block_in, &ks); assert(matches != NULL); assert(matches->len == 1); struct bp_block_match *match = parr_idx(matches, 0); assert(match->n == 1); /* match 2nd tx, index 1 */ /* get matching transaction */ struct bp_tx *tx = parr_idx(block_in.vtx, match->n); bp_tx_calc_sha256(tx); /* verify txid matches expected */ char tx_hexstr[BU256_STRSZ]; bu256_hex(tx_hexstr, &tx->sha256); assert(strcmp(tx_hexstr, tx_in_hash) == 0); /* verify mask matches 2nd txout (1 << 1) */ BIGNUM tmp_mask; BN_init(&tmp_mask); BN_one(&tmp_mask); BN_lshift(&tmp_mask, &tmp_mask, 1); assert(BN_cmp(&tmp_mask, &match->mask) == 0); /* build merkle tree, tx's branch */ parr *mtree = bp_block_merkle_tree(&block_in); assert(mtree != NULL); parr *mbranch = bp_block_merkle_branch(&block_in, mtree, match->n); assert(mbranch != NULL); /* verify merkle branch for tx matches expected */ bu256_t mrk_check; bp_check_merkle_branch(&mrk_check, &tx->sha256, mbranch, match->n); assert(bu256_equal(&mrk_check, &block_in.hashMerkleRoot) == true); /* release resources */ parr_free(mtree, true); parr_free(mbranch, true); BN_clear_free(&tmp_mask); parr_free(matches, true); bpks_free(&ks); bp_key_free(&key); bp_block_free(&block_in); json_decref(wallet); free(data); free(fn); free(json_fn); }
static void runtest(const char *json_fn_base, const char *ser_fn_base) { char *fn = test_filename(json_fn_base); json_t *meta = read_json(fn); assert(json_is_object(meta)); char *ser_fn = test_filename(ser_fn_base); void *data = NULL; size_t data_len = 0; bool rc = bu_read_file(ser_fn, &data, &data_len, 100 * 1024 * 1024); assert(rc); const char *hashstr = json_string_value(json_object_get(meta, "hash")); assert(hashstr != NULL); unsigned int size = json_integer_value(json_object_get(meta, "size")); assert(data_len == size); struct bp_tx tx; bp_tx_init(&tx); struct const_buffer buf = { data, data_len }; rc = deser_bp_tx(&tx, &buf); assert(rc); cstring *gs = cstr_new_sz(10000); ser_bp_tx(gs, &tx); if (gs->len != data_len) { fprintf(stderr, "gs->len %ld, data_len %lu\n", (long)gs->len, data_len); assert(gs->len == data_len); } assert(memcmp(gs->str, data, data_len) == 0); bp_tx_calc_sha256(&tx); char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &tx.sha256); if (strcmp(hexstr, hashstr)) { fprintf(stderr, "tx: wanted hash %s,\n got hash %s\n", hashstr, hexstr); assert(!strcmp(hexstr, hashstr)); } assert(tx.vin->len == 1); assert(tx.vout->len == 2); struct bp_tx tx_copy; bp_tx_init(&tx_copy); bp_tx_copy(&tx_copy, &tx); bp_tx_calc_sha256(&tx_copy); assert(bu256_equal(&tx_copy.sha256, &tx.sha256) == true); bp_tx_free(&tx); bp_tx_free(&tx_copy); cstr_free(gs, true); free(data); free(fn); free(ser_fn); json_decref(meta); }
static bool blkdb_connect(struct blkdb *db, struct blkinfo *bi, struct blkdb_reorg *reorg_info) { memset(reorg_info, 0, sizeof(*reorg_info)); if (blkdb_lookup(db, &bi->hash)) return false; bool rc = false; BIGNUM cur_work; BN_init(&cur_work); u256_from_compact(&cur_work, bi->hdr.nBits); bool best_chain = false; /* verify genesis block matches first record */ if (bp_hashtab_size(db->blocks) == 0) { if (!bu256_equal(&bi->hdr.sha256, &db->block0)) goto out; /* bi->prev = NULL; */ bi->height = 0; BN_copy(&bi->work, &cur_work); best_chain = true; } /* lookup and verify previous block */ else { struct blkinfo *prev = blkdb_lookup(db, &bi->hdr.hashPrevBlock); if (!prev) goto out; bi->prev = prev; bi->height = prev->height + 1; if (!BN_add(&bi->work, &cur_work, &prev->work)) goto out; if (BN_cmp(&bi->work, &db->best_chain->work) > 0) best_chain = true; } /* add to block map */ bp_hashtab_put(db->blocks, &bi->hash, bi); /* if new best chain found, update pointers */ if (best_chain) { struct blkinfo *old_best = db->best_chain; struct blkinfo *new_best = bi; reorg_info->old_best = old_best; /* likely case: new best chain has greater height */ if (!old_best) { while (new_best) { new_best = new_best->prev; reorg_info->conn++; } } else { while (new_best && (new_best->height > old_best->height)) { new_best = new_best->prev; reorg_info->conn++; } } /* unlikely case: old best chain has greater height */ while (old_best && new_best && (old_best->height > new_best->height)) { old_best = old_best->prev; reorg_info->disconn++; } /* height matches, but we are still walking parallel chains */ while (old_best && new_best && (old_best != new_best)) { new_best = new_best->prev; reorg_info->conn++; old_best = old_best->prev; reorg_info->disconn++; } /* reorg analyzed. update database's best-chain pointer */ db->best_chain = bi; } rc = true; out: BN_clear_free(&cur_work); return rc; }