static bool nc_msg_block(struct nc_conn *conn) { struct const_buffer buf = { conn->msg.data, conn->msg.hdr.data_len }; struct bp_block block; bp_block_init(&block); bool rc = false; if (!deser_bp_block(&block, &buf)) goto out; bp_block_calc_sha256(&block); char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &block.sha256); log_debug("net: %s block %s", conn->addr_str, hexstr); if (!bp_block_valid(&block)) { log_info("net: %s invalid block %s", conn->addr_str, hexstr); goto out; } if (!conn->nci->block_process(&block, &conn->msg.hdr, &buf)) goto out; rc = true; out: bp_block_free(&block); return rc; }
static void bp_json_set_new_int256(json_t *obj, const char *key, const bu256_t *i) { char hexstr[(32 * 2) + 1]; bu256_hex(hexstr, i); json_t *val = json_string(hexstr); assert(val != NULL); int rc = json_object_set_new(obj, key, val); assert(rc == 0); }
static bool spend_block(struct bp_utxo_set *uset, const struct bp_block *block, unsigned int height) { unsigned int i; for (i = 0; i < block->vtx->len; i++) { struct bp_tx *tx; tx = parr_idx(block->vtx, i); if (!spend_tx(uset, tx, i, height)) { char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &tx->sha256); fprintf(plog, "brd: spent_block tx fail %s\n", hexstr); return false; } } return true; }
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 bool spend_block(struct bp_utxo_set *uset, const struct bp_block *block, unsigned int height) { unsigned int i; if (height % 10000 == 0) fprintf(stderr, "chain-verf: spend block @ %u\n", height); for (i = 0; i < block->vtx->len; i++) { struct bp_tx *tx; tx = g_ptr_array_index(block->vtx, i); if (!spend_tx(uset, tx, i, height)) { char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &tx->sha256); fprintf(stderr, "chain-verf: tx fail %s\n", hexstr); return false; } } return true; }
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 nc_msg_inv(struct nc_conn *conn) { struct const_buffer buf = { conn->msg.data, conn->msg.hdr.data_len }; struct msg_vinv mv, mv_out; bool rc = false; msg_vinv_init(&mv); msg_vinv_init(&mv_out); if (!deser_msg_vinv(&mv, &buf)) goto out; if (log_state->debug && mv.invs && mv.invs->len == 1) { struct bp_inv *inv = parr_idx(mv.invs, 0); char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &inv->hash); char typestr[32]; switch (inv->type) { case MSG_TX: strcpy(typestr, "tx"); break; case MSG_BLOCK: strcpy(typestr, "block"); break; default: sprintf(typestr, "unknown 0x%x", inv->type); break; } log_debug("net: %s inv %s %s", conn->addr_str, typestr, hexstr); } else if (mv.invs) { log_debug("net: %s inv (%zu sz)", conn->addr_str, mv.invs->len); } if (!mv.invs || !mv.invs->len) goto out_ok; /* scan incoming inv's for interesting material */ unsigned int i; for (i = 0; i < mv.invs->len; i++) { struct bp_inv *inv = parr_idx(mv.invs, i); switch (inv->type) { case MSG_BLOCK: if (conn->nci->inv_block_process(&inv->hash)) msg_vinv_push(&mv_out, MSG_BLOCK, &inv->hash); break; case MSG_TX: default: break; } } /* send getdata, if they have anything we want */ if (mv_out.invs && mv_out.invs->len) { cstring *s = ser_msg_vinv(&mv_out); rc = nc_conn_send(conn, "getdata", s->str, s->len); cstr_free(s, true); } out_ok: rc = true; out: msg_vinv_free(&mv); msg_vinv_free(&mv_out); return rc; }
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 test_tx_valid(bool is_valid, struct bp_hashtab *input_map, cstring *tx_ser, bool enforce_p2sh) { struct bp_tx tx; bp_tx_init(&tx); struct const_buffer buf = { tx_ser->str, tx_ser->len }; assert(deser_bp_tx(&tx, &buf) == true); if (is_valid) { /* checking for valid tx; !bp_tx_valid implies test fail */ assert(bp_tx_valid(&tx) == true); } else { /* checking for invalid tx; bp_tx_valid==false implies test * succeeded; no more work to do; bp_tx_valid==true * implies the test will detect the invalid condition * further down in the code */ if (bp_tx_valid(&tx) == false) goto out; } bp_tx_calc_sha256(&tx); unsigned int i; for (i = 0; i < tx.vin->len; i++) { struct bp_txin *txin; txin = parr_idx(tx.vin, i); assert(txin != NULL); cstring *scriptPubKey = bp_hashtab_get(input_map, &txin->prevout); if (scriptPubKey == NULL) { if (!is_valid) { /* if testing tx_invalid.json, missing input * is invalid, and therefore correct */ continue; } char tx_hexstr[BU256_STRSZ], hexstr[BU256_STRSZ]; bu256_hex(tx_hexstr, &tx.sha256); bu256_hex(hexstr, &txin->prevout.hash); dump_comments(); fprintf(stderr, "tx-valid: TX %s\n" "tx-valid: prevout (%s, %u) not found\n", tx_hexstr, hexstr, txin->prevout.n); assert(scriptPubKey != NULL); } bool rc = bp_script_verify(txin->scriptSig, scriptPubKey, &tx, i, enforce_p2sh ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); if (rc != is_valid) { char tx_hexstr[BU256_STRSZ]; bu256_hex(tx_hexstr, &tx.sha256); dump_comments(); fprintf(stderr, "tx-valid: TX %s\n" "tx-valid: txin %u script verification failed\n", tx_hexstr, i); assert(rc == is_valid); } } out: bp_tx_free(&tx); }
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); int fd = file_seq_open(ser_fn); if (fd < 0) { perror(ser_fn); exit(1); } struct p2p_message msg = {}; bool read_ok = false; bool rc = fread_message(fd, &msg, &read_ok); assert(rc); assert(read_ok); assert(!strncmp(msg.hdr.command, "block", 12)); close(fd); 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((24 + msg.hdr.data_len) == size); struct bp_block block; bp_block_init(&block); struct const_buffer buf = { msg.data, msg.hdr.data_len }; rc = deser_bp_block(&block, &buf); assert(rc); cstring *gs = cstr_new_sz(100000); ser_bp_block(gs, &block); if (gs->len != msg.hdr.data_len) { fprintf(stderr, "gs->len %ld, msg.hdr.data_len %u\n", (long)gs->len, msg.hdr.data_len); assert(gs->len == msg.hdr.data_len); } assert(memcmp(gs->str, msg.data, msg.hdr.data_len) == 0); bp_block_calc_sha256(&block); char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &block.sha256); if (strcmp(hexstr, hashstr)) { fprintf(stderr, "block: wanted hash %s,\n got hash %s\n", hashstr, hexstr); assert(!strcmp(hexstr, hashstr)); } rc = bp_block_valid(&block); assert(rc); bp_block_free(&block); cstr_free(gs, true); free(msg.data); free(fn); free(ser_fn); json_decref(meta); }
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 nc_msg_block(struct nc_conn *conn) { struct const_buffer buf = { conn->msg.data, conn->msg.hdr.data_len }; struct bp_block block; bp_block_init(&block); bool rc = false; if (!deser_bp_block(&block, &buf)) goto out; bp_block_calc_sha256(&block); char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &block.sha256); if (debugging) { fprintf(plog, "net: %s block %s\n", conn->addr_str, hexstr); } if (!bp_block_valid(&block)) { fprintf(plog, "net: %s invalid block %s\n", conn->addr_str, hexstr); goto out; } /* check for duplicate block */ if (blkdb_lookup(&db, &block.sha256) || have_orphan(&block.sha256)) goto out_ok; struct iovec iov[2]; iov[0].iov_base = &conn->msg.hdr; // TODO: endian bug? iov[0].iov_len = sizeof(conn->msg.hdr); iov[1].iov_base = (void *) buf.p; // cast away 'const' iov[1].iov_len = buf.len; size_t total_write = iov[0].iov_len + iov[1].iov_len; /* store current file position */ off64_t fpos64 = lseek64(blocks_fd, 0, SEEK_CUR); if (fpos64 == (off64_t)-1) { fprintf(plog, "blocks: lseek64 failed %s\n", strerror(errno)); goto out; } /* write new block to disk */ errno = 0; ssize_t bwritten = writev(blocks_fd, iov, ARRAY_SIZE(iov)); if (bwritten != total_write) { fprintf(plog, "blocks: write failed %s\n", strerror(errno)); goto out; } /* process block */ if (!process_block(&block, fpos64)) { fprintf(plog, "blocks: process-block failed\n"); goto out; } out_ok: rc = true; out: bp_block_free(&block); return rc; }
/* * struct msg_vinv { parr *invs; array of bp_inv }; struct bp_inv { uint32_t type; bu256_t hash; }; * * */ static bool nc_msg_inv(struct nc_conn *conn) { struct const_buffer buf = { conn->msg.data, conn->msg.hdr.data_len }; struct msg_vinv mv, mv_out; bool rc = false; unsigned int i=0; int msg_tx, msg_block, msg_other; msg_tx = msg_block = msg_other = 0; msg_vinv_init(&mv);//memset msg_vinv_init(&mv_out);//memset if (!deser_msg_vinv(&mv, &buf)) goto out; if (mv.invs){ //LOG("Peer [%s] gave %d inventory vectors",conn->addr_str, mv.invs->len); for (i = 0;i < mv.invs->len; i++){ struct bp_inv *inv = parr_idx(mv.invs, 0); switch (inv->type){ case MSG_TX: msg_tx++; break; case MSG_BLOCK: msg_block++; break; default: msg_other++; } } if (mv.invs->len > 0){ LOG("Inventory vectors: msg[%d], block[%d], other[%d]", msg_tx, msg_block, msg_other); } } if (debugging && mv.invs && mv.invs->len == 1) { struct bp_inv *inv = parr_idx(mv.invs, 0); char hexstr[BU256_STRSZ]; bu256_hex(hexstr, &inv->hash); char typestr[32]; switch (inv->type) { case MSG_TX: strcpy(typestr, "tx"); break; case MSG_BLOCK: strcpy(typestr, "block"); break; default: sprintf(typestr, "unknown 0x%x", inv->type); break; } fprintf(plog, "net: %s inv %s %s\n", conn->addr_str, typestr, hexstr); } else if (debugging && mv.invs) { fprintf(plog, "net: %s inv (%zu sz)\n", conn->addr_str, mv.invs->len); } if (!mv.invs || !mv.invs->len) goto out_ok; /* scan incoming inv's for interesting material */ /*declared at beginnning of function--> unsigned int i;*/ for (i = 0; i < mv.invs->len; i++) { struct bp_inv *inv = parr_idx(mv.invs, i); switch (inv->type) { case MSG_BLOCK: if (!blkdb_lookup(&db, &inv->hash) && !have_orphan(&inv->hash)) msg_vinv_push(&mv_out, MSG_BLOCK, &inv->hash); break; case MSG_TX: default: break; } } /* send getdata, if they have anything we want */ if (mv_out.invs && mv_out.invs->len) { cstring *s = ser_msg_vinv(&mv_out); rc = nc_conn_send(conn, "getdata", s->str, s->len); cstr_free(s, true); } out_ok: rc = true; out: msg_vinv_free(&mv); msg_vinv_free(&mv_out); return rc; }