static bool wallet_eq(const struct wallet *wlt1, const struct wallet *wlt2) { unsigned int i; if (wlt1->version != wlt2->version) return false; if (wlt1->chain != wlt2->chain) return false; if (wlt1->keys->len != wlt2->keys->len) return false; for (i = 0; i < wlt1->keys->len; i++) { const struct bp_key *key1, *key2; key1 = parr_idx(wlt1->keys, i); key2 = parr_idx(wlt2->keys, i); if (!key_eq(key1, key2)) return false; } return true; }
void nc_conns_gc(struct net_child_info *nci, bool free_all) { clist *dead = NULL; unsigned int n_gc = 0; /* build list of dead connections */ unsigned int i; for (i = 0; i < nci->conns->len; i++) { struct nc_conn *conn = parr_idx(nci->conns, i); if (free_all || conn->dead) dead = clist_prepend(dead, conn); } /* remove and free dead connections */ clist *tmp = dead; while (tmp) { struct nc_conn *conn = tmp->data; tmp = tmp->next; parr_remove(nci->conns, conn); nc_conn_free(conn); n_gc++; } clist_free(dead); log_debug("net: gc'd %u connections", n_gc); }
bool bp_sign_sig(struct bp_keystore *ks, const struct bp_utxo *txFrom, struct bp_tx *txTo, unsigned int nIn, unsigned int flags, int nHashType) { if (!ks || !txFrom || !txFrom->vout || !txTo || !txTo->vin || nIn >= txTo->vin->len) return false; struct bp_txin *txin = parr_idx(txTo->vin, nIn); if (txin->prevout.n >= txFrom->vout->len) return false; struct bp_txout *txout = parr_idx(txFrom->vout, txin->prevout.n); return bp_script_sign(ks, txout->scriptPubKey, txTo, nIn, nHashType); }
static void dump_comments(void) { unsigned int i; for (i = 0; i < comments->len; i++) { fprintf(stderr, "tx-valid cmt: %s\n", (char *)parr_idx(comments, i)); } }
/** *struct msg_addr { parr *addrs; //bp_address array }; */ static bool nc_msg_addr(struct nc_conn *conn) { struct const_buffer buf = { conn->msg.data, conn->msg.hdr.data_len }; struct msg_addr ma; bool rc = false; msg_addr_init(&ma);/* just a memset 0*/ if (!deser_msg_addr(conn->protover, &ma, &buf)) goto out; unsigned int i; time_t cutoff = time(NULL) - (7 * 24 * 60 * 60); if (debugging) { unsigned int old = 0; for (i = 0; i < ma.addrs->len; i++) { struct bp_address *addr = parr_idx(ma.addrs, i); if (addr->nTime < cutoff) old++; } fprintf(plog, "net: %s addr(%zu addresses, %u old)\n", conn->addr_str, ma.addrs->len, old); } /* ignore ancient addresses */ if (conn->protover < CADDR_TIME_VERSION){ LOG("all addresses rejected because protover < CADDR_TIME_VERSION"); goto out_ok; } /* feed addresses to peer manager */ int l_cnt = 0; for (i = 0; i < ma.addrs->len; i++) { struct bp_address *addr = parr_idx(ma.addrs, i); if (addr->nTime > cutoff){ l_cnt++; peerman_add_addr(conn->nci->peers, addr, false); } } LOG("number of addresses added from peer:%d", l_cnt); out_ok: rc = true; out: msg_addr_free(&ma); return rc; }
static void runtest(const char *ser_fn_base) { 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); struct bitc_block block; bitc_block_init(&block); struct const_buffer buf = { msg.data, msg.hdr.data_len }; rc = deser_bitc_block(&block, &buf); assert(rc); unsigned int n_tx, n_out; for (n_tx = 0; n_tx < block.vtx->len; n_tx++) { struct bitc_tx *tx = parr_idx(block.vtx, n_tx); for (n_out = 0; n_out < tx->vout->len; n_out++) { struct bitc_txout *txout; txout = parr_idx(tx->vout, n_out); test_txout(txout); } } bitc_block_free(&block); free(msg.data); free(ser_fn); }
static bool nc_msg_addr(struct nc_conn *conn) { struct const_buffer buf = { conn->msg.data, conn->msg.hdr.data_len }; struct msg_addr ma; bool rc = false; msg_addr_init(&ma); if (!deser_msg_addr(conn->protover, &ma, &buf)) goto out; unsigned int i; time_t cutoff = time(NULL) - (7 * 24 * 60 * 60); if (log_state->debug) { unsigned int old = 0; for (i = 0; i < ma.addrs->len; i++) { struct bp_address *addr = parr_idx(ma.addrs, i); if (addr->nTime < cutoff) old++; } log_debug("net: %s addr(%zu addresses, %u old)", conn->addr_str, ma.addrs->len, old); } /* ignore ancient addresses */ if (conn->protover < CADDR_TIME_VERSION) goto out_ok; /* feed addresses to peer manager */ for (i = 0; i < ma.addrs->len; i++) { struct bp_address *addr = parr_idx(ma.addrs, i); if (addr->nTime > cutoff) peerman_add_addr(conn->nci->peers, addr, false); } out_ok: rc = true; out: msg_addr_free(&ma); return rc; }
bool bp_verify_sig(const struct bp_utxo *txFrom, const struct bp_tx *txTo, unsigned int nIn, unsigned int flags, int nHashType) { if (!txFrom || !txFrom->vout || !txFrom->vout->len || !txTo || !txTo->vin || !txTo->vin->len || (txTo->vin->len <= nIn)) return false; struct bp_txin *txin = parr_idx(txTo->vin, nIn); if (txin->prevout.n >= txFrom->vout->len) return false; struct bp_txout *txout = parr_idx(txFrom->vout, txin->prevout.n); if (!txout) return false; return bp_script_verify(txin->scriptSig, txout->scriptPubKey, txTo, nIn, flags, nHashType); }
static bool nc_conn_ip_active(struct net_child_info *nci, const unsigned char *ip) { unsigned int i; for (i = 0; i < nci->conns->len; i++) { struct nc_conn *conn; conn = parr_idx(nci->conns, i); if (!memcmp(conn->peer.addr.ip, ip, 16)) return true; } return false; }
struct wallet_account *account_byname(struct wallet *wlt, const char *name) { if (!wlt || !wlt->accounts || !wlt->accounts->len) return NULL; unsigned int i; for (i = 0; i < wlt->accounts->len; i++) { struct wallet_account *acct = parr_idx(wlt->accounts, i); if (!strcmp(name, acct->name->str)) return acct; } return NULL; }
bool bp_tx_match(const struct bp_tx *tx, const struct bp_keyset *ks) { if (!tx || !tx->vout || !ks) return false; unsigned int i; for (i = 0; i < tx->vout->len; i++) { struct bp_txout *txout; txout = parr_idx(tx->vout, i); if (bp_txout_match(txout, ks)) return true; } return false; }
bool CheckSequence(const unsigned int nSequence, const struct bp_tx *txTo, unsigned int nIn) { const struct bp_txin *txin = parr_idx(txTo->vin, nIn); // Relative lock times are supported by comparing the passed // in operand to the sequence number of the input. const int64_t txToSequence = (int64_t)txin->nSequence; // Fail if the transaction's version number is not set high // enough to trigger BIP 68 rules. if (txTo->nVersion < 2) return false; // Sequence numbers with their most significant bit set are not // consensus constrained. Testing that the transaction's sequence // number do not have this bit set prevents using this property // to get around a CHECKSEQUENCEVERIFY check. if (txToSequence & SEQUENCE_LOCKTIME_DISABLE_FLAG) return false; // Mask off any bits that do not have consensus-enforced meaning // before doing the integer comparisons const uint32_t nLockTimeMask = SEQUENCE_LOCKTIME_TYPE_FLAG | SEQUENCE_LOCKTIME_MASK; const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; const uint64_t nSequenceMasked = nSequence & nLockTimeMask; // There are two kinds of nSequence: lock-by-blockheight // and lock-by-blocktime, distinguished by whether // nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. // // We want to compare apples to apples, so fail the script // unless the type of nSequenceMasked being tested is the same as // the nSequenceMasked in the transaction. if (!( (txToSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG) || (txToSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG) )) { return false; } // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. if (nSequenceMasked > txToSequenceMasked) return false; return true; }
static void output_json_vout(json_t *obj) { json_t *arr = json_array(); assert(arr != NULL); int rc = json_object_set_new(obj, "vout", arr); assert(rc == 0); if (!tx.vout) return; unsigned int i; for (i = 0; i < tx.vout->len; i++) { struct bp_txout *txout = parr_idx(tx.vout, i); output_json_txout(arr, txout, i); } }
void msg_headers_free(struct msg_headers *mh) { if (!mh) return; if (mh->headers) { unsigned int i; for (i = 0; i < mh->headers->len; i++) { struct bp_block *block; block = parr_idx(mh->headers, i); bp_block_free(block); } parr_free(mh->headers, true); mh->headers = NULL; } }
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 nc_conn_group_active(struct net_child_info *nci, const struct peer *peer) { // FIXME return false; unsigned int group_len = peer->group_len; unsigned int i; for (i = 0; i < nci->conns->len; i++) { struct nc_conn *conn; conn = parr_idx(nci->conns, i); if ((group_len == conn->peer.group_len) && !memcmp(peer->group, conn->peer.group, group_len)) return true; } return false; }
bool bp_tx_match_mask(BIGNUM *mask, const struct bp_tx *tx, const struct bp_keyset *ks) { if (!tx || !tx->vout || !ks || !mask) return false; BN_zero(mask); unsigned int i; for (i = 0; i < tx->vout->len; i++) { struct bp_txout *txout; txout = parr_idx(tx->vout, i); if (bp_txout_match(txout, ks)) BN_set_bit(mask, i); } return true; }
parr *bp_block_match(const struct bp_block *block, const struct bp_keyset *ks) { if (!block || !block->vtx || !ks) return NULL; parr *arr = parr_new(block->vtx->len, bbm_free); if (!arr) return NULL; BIGNUM tmp_mask; BN_init(&tmp_mask); unsigned int n; for (n = 0; n < block->vtx->len; n++) { struct bp_tx *tx; tx = parr_idx(block->vtx, n); if (!bp_tx_match_mask(&tmp_mask, tx, ks)) goto err_out; if (!BN_is_zero(&tmp_mask)) { struct bp_block_match *match; match = bbm_new(); match->n = n; BN_copy(&match->mask, &tmp_mask); parr_add(arr, match); } } BN_clear_free(&tmp_mask); return arr; err_out: BN_clear_free(&tmp_mask); parr_free(arr, true); return NULL; }
cstring *ser_msg_addr(unsigned int protover, const struct msg_addr *ma) { cstring *s = cstr_new(NULL); if (!ma || !ma->addrs || !ma->addrs->len) { ser_varlen(s, 0); return s; } ser_varlen(s, ma->addrs->len); unsigned int i; for (i = 0; i < ma->addrs->len; i++) { struct bp_address *addr; addr = parr_idx(ma->addrs, i); ser_bp_addr(s, protover, addr); } return s; }
cstring *ser_msg_headers(const struct msg_headers *mh) { cstring *s = cstr_new(NULL); if (!mh || !mh->headers || !mh->headers->len) { ser_varlen(s, 0); return s; } ser_varlen(s, mh->headers->len); unsigned int i; for (i = 0; i < mh->headers->len; i++) { struct bp_block *block; block = parr_idx(mh->headers, i); ser_bp_block(s, block); } return s; }
cstring *ser_msg_vinv(const struct msg_vinv *mv) { cstring *s = cstr_new(NULL); if (!mv || !mv->invs || !mv->invs->len) { ser_varlen(s, 0); return s; } ser_varlen(s, mv->invs->len); unsigned int i; for (i = 0; i < mv->invs->len; i++) { struct bp_inv *inv; inv = parr_idx(mv->invs, i); ser_bp_inv(s, inv); } return s; }
cstring *wallet_new_address(struct wallet *wlt) { struct hd_path_seg hdpath[] = { { 44, true }, // BIP 44 { 0, true }, // chain: BTC { 0, true }, // acct# { 0, false }, // change? { 0, false }, // key index }; struct wallet_account *acct = account_byname(wlt, wlt->def_acct->str); if (!acct) return NULL; // patch HD path based on account settings hdpath[2].index = acct->acct_idx; hdpath[4].index = acct->next_key_idx; assert(wlt->hdmaster && (wlt->hdmaster->len > 0)); struct hd_extended_key *master = parr_idx(wlt->hdmaster, 0); assert(master != NULL); struct hd_extended_key child; hd_extended_key_init(&child); if (!hd_derive(&child, master, hdpath, ARRAY_SIZE(hdpath))) { hd_extended_key_free(&child); return NULL; } acct->next_key_idx++; cstring *rs = bp_pubkey_get_address(&child.key,wlt->chain->addr_pubkey); hd_extended_key_free(&child); return rs; }
static void nc_conns_gc(struct net_child_info *nci, bool free_all) { clist *dead = NULL; unsigned int n_gc = 0; /*nr of dead connections*/ size_t nr_dead = 0; /* build list of dead connections */ unsigned int i; for (i = 0; i < nci->conns->len; i++) { struct nc_conn *conn = parr_idx(nci->conns, i); if (free_all || conn->dead) dead = clist_prepend(dead, conn); } nr_dead = clist_length( dead ); if (nr_dead){ LOG("Nr of dead connections:%d\n", nr_dead); } /* remove and free dead connections */ clist *tmp = dead; while (tmp) { struct nc_conn *conn = tmp->data; tmp = tmp->next; parr_remove(nci->conns, conn); nc_conn_free(conn); n_gc++; } clist_free(dead); if (debugging) fprintf(plog, "net: gc'd %u connections\n", n_gc); }
bool CheckLockTime(const unsigned int nLockTime, const struct bp_tx *txTo, unsigned int nIn) { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether // nLockTime < LOCKTIME_THRESHOLD. // // We want to compare apples to apples, so fail the script // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) )) return false; // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. if (nLockTime > (int64_t)txTo->nLockTime) return false; // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making // the opcode ineffective. // // Testing if this vin is not final is sufficient to // prevent this condition. Alternatively we could test all // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. struct bp_txin *txin = parr_idx(txTo->vin, nIn); if (SEQUENCE_FINAL == txin->nSequence) return false; return true; }
static void stack_copy(parr *dest, const parr *src) { unsigned int i; for (i = 0; i < src->len; i++) stack_push(dest, parr_idx(src, i)); }
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 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_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); }
bool bp_script_sign(struct bp_keystore *ks, const cstring *fromPubKey, const struct bp_tx *txTo, unsigned int nIn, int nHashType) { if (!txTo || !txTo->vin || nIn >= txTo->vin->len) return false; struct bp_txin *txin = parr_idx(txTo->vin, nIn); /* get signature hash */ bu256_t hash; bp_tx_sighash(&hash, fromPubKey, txTo, nIn, nHashType); /* match fromPubKey against templates, to find what pubkey[hashes] * are required for signing */ struct bscript_addr addrs; if (!bsp_addr_parse(&addrs, fromPubKey->str, fromPubKey->len)) return false; cstring *scriptSig = cstr_new_sz(64); bool rc = false; bu160_t key_id; struct buffer *kbuf; /* sign, based on script template matched above */ switch (addrs.txtype) { case TX_PUBKEY: kbuf = addrs.pub->data; bu_Hash160((unsigned char *)&key_id, kbuf->p, kbuf->len); if (!sign1(&key_id, ks, &hash, nHashType, scriptSig)) goto out; break; case TX_PUBKEYHASH: kbuf = addrs.pubhash->data; memcpy(&key_id, kbuf->p, kbuf->len); if (!sign1(&key_id, ks, &hash, nHashType, scriptSig)) goto out; if (!bkeys_pubkey_append(ks, &key_id, scriptSig)) goto out; break; case TX_SCRIPTHASH: /* TODO; not supported yet */ case TX_MULTISIG: goto out; case TX_NONSTANDARD: /* unknown script type, cannot sign */ goto out; } if (txin->scriptSig) cstr_free(txin->scriptSig, true); txin->scriptSig = scriptSig; scriptSig = NULL; rc = true; out: if (scriptSig) cstr_free(scriptSig, true); bsp_addr_free(&addrs); return rc; }
void bp_tx_sigserializer(cstring *s, const cstring *scriptCode, const struct bp_tx *txTo, unsigned int nIn, int nHashType) { const bool fAnyoneCanPay = (!!(nHashType & SIGHASH_ANYONECANPAY)); const bool fHashSingle = ((nHashType & 0x1f) == SIGHASH_SINGLE); const bool fHashNone = ((nHashType & 0x1f) == SIGHASH_NONE); /** Serialize txTo */ // Serialize nVersion ser_u32(s, txTo->nVersion); // Serialize vin unsigned int nInputs = fAnyoneCanPay ? 1 : txTo->vin->len; ser_varlen(s, nInputs); unsigned int nInput; for (nInput = 0; nInput < nInputs; nInput++) { /** Serialize an input of txTo */ // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized if (fAnyoneCanPay) nInput = nIn; struct bp_txin *txin = parr_idx(txTo->vin, nInput); // Serialize the prevout ser_bp_outpt(s, &txin->prevout); // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures ser_varlen(s, (int)0); else if (scriptCode == NULL) cstr_append_c(s, 0); else { /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */ struct const_buffer it = { scriptCode->str, scriptCode->len }; struct const_buffer itBegin = it; struct bscript_op op; unsigned int nCodeSeparators = 0; struct bscript_parser bp; bsp_start(&bp, &it); while (bsp_getop(&op, &bp)) { if (op.op == OP_CODESEPARATOR) nCodeSeparators++; } ser_varlen(s, scriptCode->len - nCodeSeparators); it = itBegin; bsp_start(&bp, &it); while (bsp_getop(&op, &bp)) { if (op.op == OP_CODESEPARATOR) { ser_bytes(s, itBegin.p, it.p - itBegin.p - 1); itBegin = it; } } if (itBegin.p != scriptCode->str + scriptCode->len) ser_bytes(s, itBegin.p, it.p - itBegin.p); } // Serialize the nSequence if ((nInput != nIn) && (fHashSingle || fHashNone)) // let the others update at will ser_u32(s, (int)0); else ser_u32(s, txin->nSequence); } // Serialize vout unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? (nIn + 1) : txTo->vout->len); ser_varlen(s, nOutputs); unsigned int nOutput; for (nOutput = 0; nOutput < nOutputs; nOutput++) { struct bp_txout *txout = parr_idx(txTo->vout, nOutput); if (fHashSingle && (nOutput != nIn)) // Do not lock-in the txout payee at other indices as txin; bp_txout_set_null(txout); ser_bp_txout(s, txout); } // Serialize nLockTime ser_u32(s, txTo->nLockTime); }