def test_prioritised_transactions(self): # Ensure that fee deltas used via prioritisetransaction are # correctly used by replacement logic # 1. Check that feeperkb uses modified fees tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # Higher fee, but the actual fee per KB is much lower. tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1b.vout = [CTxOut(int(0.001 * COIN), CScript([b'a' * 740000]))] tx1b_hex = txToHex(tx1b) # Verify tx1b cannot replace tx1a. assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) # Use prioritisetransaction to set tx1a's fee to 0. self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1 * COIN)) # Now tx1b should be able to replace tx1a tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) assert tx1b_txid in self.nodes[0].getrawmempool() # 2. Check that absolute fee checks use modified fee. tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx2a_hex = txToHex(tx2a) self.nodes[0].sendrawtransaction(tx2a_hex, True) # Lower fee, but we'll prioritise it tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] tx2b.vout = [CTxOut(int(1.01 * COIN), CScript([b'a' * 35]))] tx2b.rehash() tx2b_hex = txToHex(tx2b) # Verify tx2b cannot replace tx2a. assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now prioritise tx2b to have a higher modified fee self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1 * COIN)) # tx2b should now be accepted tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) assert tx2b_txid in self.nodes[0].getrawmempool()
def test_opt_in(self): """Replacing should only work if orig tx opted in""" tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) # Create a non-opting in transaction tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)] tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # This transaction isn't shown as replaceable assert_equal( self.nodes[0].getmempoolentry(tx1a_txid)['bip125-replaceable'], False) # Shouldn't be able to double-spend tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] tx1b_hex = txToHex(tx1b) # This will raise an exception assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True) tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) # Create a different non-opting in transaction tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)] tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx2a_hex = txToHex(tx2a) tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) # Still shouldn't be able to double-spend tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] tx2b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] tx2b_hex = txToHex(tx2b) # This will raise an exception assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now create a new transaction that spends from tx1a and tx2a # opt-in on one of the inputs # Transaction should be replaceable on either input tx1a_txid = int(tx1a_txid, 16) tx2a_txid = int(tx2a_txid, 16) tx3a = CTransaction() tx3a.vin = [ CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff), CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd) ] tx3a.vout = [ CTxOut(int(0.9 * COIN), CScript([b'c'])), CTxOut(int(0.9 * COIN), CScript([b'd'])) ] tx3a_hex = txToHex(tx3a) tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, True) # This transaction is shown as replaceable assert_equal( self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True) tx3b = CTransaction() tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] tx3b.vout = [CTxOut(int(0.5 * COIN), CScript([b'e' * 35]))] tx3b_hex = txToHex(tx3b) tx3c = CTransaction() tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)] tx3c.vout = [CTxOut(int(0.5 * COIN), CScript([b'f' * 35]))] tx3c_hex = txToHex(tx3c) self.nodes[0].sendrawtransaction(tx3b_hex, True) # If tx3b was accepted, tx3c won't look like a replacement, # but make sure it is accepted anyway self.nodes[0].sendrawtransaction(tx3c_hex, True)
def run_test(self): self.nodes[0].generate(161) # block 161 self.log.info( "Verify sigops are counted in GBT with pre-BIP141 rules before the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert tmpl['sizelimit'] == 100000 assert 'weightlimit' not in tmpl assert tmpl['sigoplimit'] == 2000 assert tmpl['transactions'][0]['hash'] == txid assert tmpl['transactions'][0]['sigops'] == 2 tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert tmpl['sizelimit'] == 100000 assert 'weightlimit' not in tmpl assert tmpl['sigoplimit'] == 2000 assert tmpl['transactions'][0]['hash'] == txid assert tmpl['transactions'][0]['sigops'] == 2 self.nodes[0].generate(1) # block 162 balance_presetup = self.nodes[0].getbalance() self.pubkey = [] p2sh_ids = [ ] # p2sh_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE embedded in p2sh wit_ids = [ ] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness for i in range(3): newaddress = self.nodes[i].getnewaddress() self.pubkey.append( self.nodes[i].getaddressinfo(newaddress)["pubkey"]) multiscript = CScript([ OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG ]) p2sh_ms_addr = self.nodes[i].addmultisigaddress( 1, [self.pubkey[-1]], '', 'p2sh-segwit')['address'] bip173_ms_addr = self.nodes[i].addmultisigaddress( 1, [self.pubkey[-1]], '', 'bech32')['address'] assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript)) assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript)) p2sh_ids.append([]) wit_ids.append([]) for v in range(2): p2sh_ids[i].append([]) wit_ids[i].append([]) for i in range(5): for n in range(3): for v in range(2): wit_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999"))) p2sh_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999"))) self.nodes[0].generate(1) # block 163 self.sync_blocks() # Make sure all nodes recognize the transactions as theirs assert_equal(self.nodes[0].getbalance(), balance_presetup - 60 * 50 + 20 * Decimal("49.999") + 50) assert_equal(self.nodes[1].getbalance(), 20 * Decimal("49.999")) assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999")) self.nodes[0].generate(260) # block 423 self.sync_blocks() self.log.info( "Verify witness txs are skipped for mining before the fork") self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) # block 424 self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) # block 425 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) # block 426 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) # block 427 self.log.info( "Verify unsigned p2sh witness txs without a redeem script are invalid" ) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V0][1], False) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V1][1], False) self.nodes[2].generate(4) # blocks 428-431 self.log.info( "Verify previous witness txs skipped for mining can now be mined") assert_equal(len(self.nodes[2].getrawmempool()), 4) blockhash = self.nodes[2].generate(1)[ 0] # block 432 (first block with new rules; 432 = 144 * 3) self.sync_blocks() assert_equal(len(self.nodes[2].getrawmempool()), 0) segwit_tx_list = self.nodes[2].getblock(blockhash)["tx"] assert_equal(len(segwit_tx_list), 5) self.log.info( "Verify default node can't accept txs with missing witness") # unsigned, no scriptsig self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V0][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V1][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False) # unsigned with redeem script self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False, witness_script(False, self.pubkey[0])) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False, witness_script(True, self.pubkey[0])) self.log.info( "Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag" ) assert self.nodes[2].getblock( blockhash, False) != self.nodes[0].getblock(blockhash, False) assert self.nodes[1].getblock(blockhash, False) == self.nodes[2].getblock( blockhash, False) for tx_id in segwit_tx_list: tx = FromHex(CTransaction(), self.nodes[2].gettransaction(tx_id)["hex"]) assert self.nodes[2].getrawtransaction( tx_id, False, blockhash) != self.nodes[0].getrawtransaction( tx_id, False, blockhash) assert self.nodes[1].getrawtransaction( tx_id, False, blockhash) == self.nodes[2].getrawtransaction( tx_id, False, blockhash) assert self.nodes[0].getrawtransaction( tx_id, False, blockhash) != self.nodes[2].gettransaction(tx_id)["hex"] assert self.nodes[1].getrawtransaction( tx_id, False, blockhash) == self.nodes[2].gettransaction(tx_id)["hex"] assert self.nodes[0].getrawtransaction( tx_id, False, blockhash) == tx.serialize_without_witness().hex() self.log.info( "Verify witness txs without witness data are invalid after the fork" ) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch) (code 64)', wit_ids[NODE_2][WIT_V0][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness) (code 64)', wit_ids[NODE_2][WIT_V1][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch) (code 64)', p2sh_ids[NODE_2][WIT_V0][2], sign=False, redeem_script=witness_script(False, self.pubkey[2])) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness) (code 64)', p2sh_ids[NODE_2][WIT_V1][2], sign=False, redeem_script=witness_script(True, self.pubkey[2])) self.log.info("Verify default node can now use witness txs") self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) # block 432 self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) # block 433 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) # block 434 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) # block 435 self.log.info( "Verify sigops are counted in GBT with BIP141 rules after the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert tmpl[ 'sizelimit'] >= 390000 # actual maximum size is lower due to minimum mandatory non-witness data assert tmpl['weightlimit'] == 400000 assert tmpl['sigoplimit'] == 8000 assert tmpl['transactions'][0]['txid'] == txid assert tmpl['transactions'][0]['sigops'] == 8 self.nodes[0].generate(1) # Mine a block to clear the gbt cache self.log.info( "Non-segwit miners are able to use GBT response after activation.") # Create a 3-tx chain: tx1 (non-segwit input, paying to a segwit output) -> # tx2 (segwit input, paying to a non-segwit output) -> # tx3 (non-segwit input, paying to a non-segwit output). # tx1 is allowed to appear in the block, but no others. txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996")) hex_tx = self.nodes[0].gettransaction(txid)['hex'] tx = FromHex(CTransaction(), hex_tx) assert tx.wit.is_null() # This should not be a segwit input assert txid1 in self.nodes[0].getrawmempool() tx1_hex = self.nodes[0].gettransaction(txid1)['hex'] tx1 = FromHex(CTransaction(), tx1_hex) # Check that wtxid is properly reported in mempool entry (txid1) assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid1) assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4) assert_equal( self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness()) * 3 + len(tx1.serialize_with_witness())) # Now create tx2, which will spend from txid1. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) tx.vout.append( CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = FromHex(CTransaction(), tx2_hex) assert not tx.wit.is_null() # Check that wtxid is properly reported in mempool entry (txid2) assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid2) assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4) assert_equal( self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness()) * 3 + len(tx.serialize_with_witness())) # Now create tx3, which will spend from txid2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b"")) tx.vout.append( CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee tx.calc_sha256() txid3 = self.nodes[0].sendrawtransaction(hexstring=ToHex(tx), maxfeerate=0) assert tx.wit.is_null() assert txid3 in self.nodes[0].getrawmempool() # Check that getblocktemplate includes all transactions. template = self.nodes[0].getblocktemplate({"rules": ["segwit"]}) template_txids = [t['txid'] for t in template['transactions']] assert txid1 in template_txids assert txid2 in template_txids assert txid3 in template_txids # Check that wtxid is properly reported in mempool entry (txid3) assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid3) assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4) assert_equal( self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness()) * 3 + len(tx.serialize_with_witness())) # Mine a block to clear the gbt cache again. self.nodes[0].generate(1) self.log.info("Verify behaviour of importaddress and listunspent") # Some public keys to be used later pubkeys = [ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # b4Vfz2Ly8GAubXRrhpSGF9ctmorBYVzdokEQcDrbV2EmnzB5LonH "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # b4bVUqL7X7ZJpqzDnF6Ks32YM9GXbVdrEbmznQMRXcTixRM1AbGA "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 8iW8cP2tV3YUkc8XrPz3v7CvFjV5VkhpzgKos82q1LWshZEooJo "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # b4BabAFLEnDwVU4FB2SosQPc42WvquuCqaa1rE34tV8rmhbQbjQv "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # b54DUJnyPL6VQMoCd4sXtvCBvhM1vG2vSCwqShSRE8ryS7Cuu9H1 "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # b8HQcxqFUhg4BsdjE21bisYRkwT4jvKhTUmcYh5ege5SQbLsmrAz "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ ] # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "8j9PwFko4f5TjUAyE9ssZAQSNmbCHXdV6sBwuh2ouxyeg41E8Vu") uncompressed_spendable_address = ["cg37jZdKe7YsxJMUVZNKD36EuaDpPdbZqe"] self.nodes[0].importprivkey( "b2yTVwqY6fX1PqXUEqWbUCYAaUo4YFQc8nRZavfUt9Ki77ewQaDr") compressed_spendable_address = ["cWjYG6zbUdBfsULfCHD8xQF928QYxcy4ZZ"] assert not self.nodes[0].getaddressinfo( uncompressed_spendable_address[0])['iscompressed'] assert self.nodes[0].getaddressinfo( compressed_spendable_address[0])['iscompressed'] self.nodes[0].importpubkey(pubkeys[0]) compressed_solvable_address = [key_to_p2pkh(pubkeys[0])] self.nodes[0].importpubkey(pubkeys[1]) compressed_solvable_address.append(key_to_p2pkh(pubkeys[1])) self.nodes[0].importpubkey(pubkeys[2]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])] spendable_anytime = [ ] # These outputs should be seen anytime after importprivkey and addmultisigaddress spendable_after_importaddress = [ ] # These outputs should be seen after importaddress solvable_after_importaddress = [ ] # These outputs should be seen after importaddress but not spendable unsolvable_after_importaddress = [ ] # These outputs should be unsolvable after importaddress solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [ compressed_spendable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_solvable_address[0], compressed_solvable_address[1] ])['address']) # Test multisig_without_privkey # We have 2 public keys without private keys, use addmultisigaddress to add to wallet. # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address. multisig_without_privkey_address = self.nodes[0].addmultisigaddress( 2, [pubkeys[3], pubkeys[4]])['address'] script = CScript([ OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG ]) solvable_after_importaddress.append( CScript([OP_HASH160, hash160(script), OP_EQUAL])) for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with compressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with compressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with uncompressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): # Multisig without private is not seen after addmultisigaddress, but seen after importaddress [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) solvable_after_importaddress.extend( [bare, p2sh, p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress solvable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress solvable_after_importaddress.extend([bare, p2sh]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) op1 = CScript([OP_1]) op0 = CScript([OP_0]) # dTXLAVZMSwCLfWDF4us6U6F1FWbyWyBYwK is the P2SH(P2PKH) version of cV2MQNbEFyXpjGihDYNqf4s1RQGbS94jVC unsolvable_address_key = hex_str_to_bytes( "02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D" ) unsolvablep2pkh = CScript([ OP_DUP, OP_HASH160, hash160(unsolvable_address_key), OP_EQUALVERIFY, OP_CHECKSIG ]) unsolvablep2wshp2pkh = CScript([OP_0, sha256(unsolvablep2pkh)]) p2shop0 = CScript([OP_HASH160, hash160(op0), OP_EQUAL]) p2wshop1 = CScript([OP_0, sha256(op1)]) unsolvable_after_importaddress.append(unsolvablep2pkh) unsolvable_after_importaddress.append(unsolvablep2wshp2pkh) unsolvable_after_importaddress.append( op1) # OP_1 will be imported as script unsolvable_after_importaddress.append(p2wshop1) unseen_anytime.append( op0 ) # OP_0 will be imported as P2SH address with no script provided unsolvable_after_importaddress.append(p2shop0) spendable_txid = [] solvable_txid = [] spendable_txid.append( self.mine_and_test_listunspent(spendable_anytime, 2)) solvable_txid.append( self.mine_and_test_listunspent(solvable_anytime, 1)) self.mine_and_test_listunspent( spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0) importlist = [] for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): bare = hex_str_to_bytes(v['hex']) importlist.append(bare.hex()) importlist.append(CScript([OP_0, sha256(bare)]).hex()) else: pubkey = hex_str_to_bytes(v['pubkey']) p2pk = CScript([pubkey, OP_CHECKSIG]) p2pkh = CScript([ OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG ]) importlist.append(p2pk.hex()) importlist.append(p2pkh.hex()) importlist.append(CScript([OP_0, hash160(pubkey)]).hex()) importlist.append(CScript([OP_0, sha256(p2pk)]).hex()) importlist.append(CScript([OP_0, sha256(p2pkh)]).hex()) importlist.append(unsolvablep2pkh.hex()) importlist.append(unsolvablep2wshp2pkh.hex()) importlist.append(op1.hex()) importlist.append(p2wshop1.hex()) for i in importlist: # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC # exceptions and continue. try_rpc( -4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True) self.nodes[0].importaddress( script_to_p2sh(op0)) # import OP_0 as address only self.nodes[0].importaddress( multisig_without_privkey_address) # Test multisig_without_privkey spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # Repeat some tests. This time we don't add witness scripts with importaddress # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "8id8M1PDTjZimEZBfxp2iYgp9xFZ865PHcVqdksDhja21H3kuZC") uncompressed_spendable_address = ["cS8VPRWos5pYHt6ay9WAnenT6LeDpfdtVP"] self.nodes[0].importprivkey( "b2QBP8LNcftKZAW4zx7DdZYa3FvxMmuAAuCvkgmKcvEptAiiFsvU") compressed_spendable_address = ["ckhW8KuyAKe1AvKYy5FXcP8JZrWA9n6u3g"] self.nodes[0].importpubkey(pubkeys[5]) compressed_solvable_address = [key_to_p2pkh(pubkeys[5])] self.nodes[0].importpubkey(pubkeys[6]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])] unseen_anytime = [] # These outputs should never be seen solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_solvable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) premature_witaddress = [] for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH are always spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen unseen_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable solvable_anytime.extend([p2wpkh, p2sh_p2wpkh]) self.mine_and_test_listunspent(spendable_anytime, 2) self.mine_and_test_listunspent(solvable_anytime, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works v1_addr = program_to_witness(1, [3, 5]) v1_tx = self.nodes[0].createrawtransaction( [getutxo(spendable_txid[0])], {v1_addr: 1}) v1_decoded = self.nodes[1].decoderawtransaction(v1_tx) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305") # Check that spendable outputs are really spendable self.create_and_mine_tx_from_txids(spendable_txid) # import all the private keys so solvable addresses become spendable self.nodes[0].importprivkey( "b4Vfz2Ly8GAubXRrhpSGF9ctmorBYVzdokEQcDrbV2EmnzB5LonH") self.nodes[0].importprivkey( "b4bVUqL7X7ZJpqzDnF6Ks32YM9GXbVdrEbmznQMRXcTixRM1AbGA") self.nodes[0].importprivkey( "8iW8cP2tV3YUkc8XrPz3v7CvFjV5VkhpzgKos82q1LWshZEooJo") self.nodes[0].importprivkey( "b4BabAFLEnDwVU4FB2SosQPc42WvquuCqaa1rE34tV8rmhbQbjQv") self.nodes[0].importprivkey( "b54DUJnyPL6VQMoCd4sXtvCBvhM1vG2vSCwqShSRE8ryS7Cuu9H1") self.nodes[0].importprivkey( "b8HQcxqFUhg4BsdjE21bisYRkwT4jvKhTUmcYh5ege5SQbLsmrAz") self.create_and_mine_tx_from_txids(solvable_txid) # Test that importing native P2WPKH/P2WSH scripts works for use_p2wsh in [False, True]: if use_p2wsh: scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a" transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000" else: scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87" transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000" self.nodes[1].importaddress(scriptPubKey, "", False) rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex'] rawtxfund = self.nodes[1].signrawtransactionwithwallet( rawtxfund)["hex"] txid = self.nodes[1].sendrawtransaction(rawtxfund) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) # Assert it is properly saved self.stop_node(1) self.start_node(1) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
def test_sequence_lock_unconfirmed_inputs(self): # Store height so we can easily reset the chain at the end of the test cur_height = self.nodes[0].getblockcount() # Create a mempool tx. txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) tx1.rehash() # Anyone-can-spend mempool tx. # Sequence lock of 0 should pass. tx2 = CTransaction() tx2.nVersion = 2 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [ CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), DUMMY_P2WPKH_SCRIPT) ] tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) tx2.rehash() self.nodes[0].sendrawtransaction(tx2_raw) # Create a spend of the 0th output of orig_tx with a sequence lock # of 1, and test what happens when submitting. # orig_tx.vout[0] must be an anyone-can-spend output def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock): sequence_value = 1 if not use_height_lock: sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx = CTransaction() tx.nVersion = 2 tx.vin = [ CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value) ] tx.vout = [ CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), DUMMY_P2WPKH_SCRIPT) ] tx.rehash() if (orig_tx.hash in node.getrawmempool()): # sendrawtransaction should fail if the tx is in the mempool assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx)) else: # sendrawtransaction should succeed if the tx is not in the mempool node.sendrawtransaction(ToHex(tx)) return tx test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) # Now mine some blocks, but make sure tx2 doesn't get mined. # Use prioritisetransaction to lower the effective feerate to 0 self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(-self.relayfee * COIN)) cur_time = int(time.time()) for i in range(10): self.nodes[0].setmocktime(cur_time + 600) self.nodes[0].generate(1) cur_time += 600 assert tx2.hash in self.nodes[0].getrawmempool() test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) # Mine tx2, and then try again self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(self.relayfee * COIN)) # Advance the time on the node so that we can test timelocks self.nodes[0].setmocktime(cur_time + 600) self.nodes[0].generate(1) assert tx2.hash not in self.nodes[0].getrawmempool() # Now that tx2 is not in the mempool, a sequence locked spend should # succeed tx3 = test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) assert tx3.hash in self.nodes[0].getrawmempool() self.nodes[0].generate(1) assert tx3.hash not in self.nodes[0].getrawmempool() # One more test, this time using height locks tx4 = test_nonzero_locks(tx3, self.nodes[0], self.relayfee, use_height_lock=True) assert tx4.hash in self.nodes[0].getrawmempool() # Now try combining confirmed and unconfirmed inputs tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True) assert tx5.hash not in self.nodes[0].getrawmempool() utxos = self.nodes[0].listunspent() tx5.vin.append( CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1)) tx5.vout[0].nValue += int(utxos[0]["amount"] * COIN) raw_tx5 = self.nodes[0].signrawtransactionwithwallet(ToHex(tx5))["hex"] assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) # Test mempool-BIP68 consistency after reorg # # State of the transactions in the last blocks: # ... -> [ tx2 ] -> [ tx3 ] # tip-1 tip # And currently tx4 is in the mempool. # # If we invalidate the tip, tx3 should get added to the mempool, causing # tx4 to be removed (fails sequence-lock). self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) assert tx4.hash not in self.nodes[0].getrawmempool() assert tx3.hash in self.nodes[0].getrawmempool() # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in # diagram above). # This would cause tx2 to be added back to the mempool, which in turn causes # tx3 to be removed. tip = int( self.nodes[0].getblockhash(self.nodes[0].getblockcount() - 1), 16) height = self.nodes[0].getblockcount() for i in range(2): block = create_block(tip, create_coinbase(height), cur_time) block.nVersion = 3 block.rehash() block.solve() tip = block.sha256 height += 1 self.nodes[0].submitblock(ToHex(block)) cur_time += 1 mempool = self.nodes[0].getrawmempool() assert tx3.hash not in mempool assert tx2.hash in mempool # Reset the chain and get rid of the mocktimed-blocks self.nodes[0].setmocktime(0) self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height + 1)) self.nodes[0].generate(10)
def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.nodes[0].generatetoaddress(COINBASE_MATURITY, self.nodes[0].get_deterministic_priv_key().address) # Iterate through a list of known invalid transaction types, ensuring each is # rejected. Some are consensus invalid and some just violate policy. for BadTxTemplate in invalid_txs.iter_all_templates(): self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__) template = BadTxTemplate(spend_block=block1) tx = template.get_tx() node.p2p.send_txs_and_test( [tx], node, success=False, expect_disconnect=template.expect_disconnect, reject_reason=template.reject_reason, ) if template.expect_disconnect: self.log.info("Reconnecting to peer") self.reconnect_p2p() # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withhold until all dependent transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) tx_withhold.vout.append(CTxOut(nValue=INITIAL_BLOCK_REWARD * COIN - 1200000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_withhold.calc_sha256() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3 tx_orphan_1.calc_sha256() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 1200000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_sha256() # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) # Send invalid tx from p2ps[1] node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected self.log.info('Send the withhold tx ... ') with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]): node.p2p.send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { t.hash for t in [ tx_withhold, # The transaction that is the root for all orphans tx_orphan_1, # The orphan transaction that splits the coins tx_orphan_2_valid, # The valid transaction (with sufficient fee) ] } # Transactions that do not end up in the mempool # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool()))
def next_block(self, number, spend=None, script=CScript([OP_TRUE]), block_size=0, extra_sigops=0): if self.tip is None: base_block_hash = self.genesis_hash block_time = int(time.time()) + 1 else: base_block_hash = self.tip.sha256 block_time = self.tip.nTime + 1 # First create the coinbase height = self.block_heights[base_block_hash] + 1 coinbase = create_coinbase(height) coinbase.rehash() if spend is None: # We need to have something to spend to fill the block. assert_equal(block_size, 0) block = create_block(base_block_hash, coinbase, block_time) else: # all but one satoshi to fees coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 coinbase.rehash() block = create_block(base_block_hash, coinbase, block_time) # Make sure we have plenty engough to spend going forward. spendable_outputs = deque([spend]) def get_base_transaction(): # Create the new transaction tx = CTransaction() # Spend from one of the spendable outputs spend = spendable_outputs.popleft() tx.vin.append(CTxIn(COutPoint(spend.tx.sha256, spend.n))) # Add spendable outputs for i in range(4): tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) spendable_outputs.append(PreviousSpendableOutput(tx, i)) return tx tx = get_base_transaction() # Make it the same format as transaction added for padding and save the size. # It's missing the padding output, so we add a constant to account # for it. tx.rehash() base_tx_size = len(tx.serialize()) + 18 # If a specific script is required, add it. if script is not None: tx.vout.append(CTxOut(1, script)) # Put some random data into the first transaction of the chain to # randomize ids. tx.vout.append( CTxOut(0, CScript([random.randint(0, 256), OP_RETURN]))) # Add the transaction to the block self.add_transactions_to_block(block, [tx]) # If we have a block size requirement, just fill # the block until we get there current_block_size = len(block.serialize()) while current_block_size < block_size: # We will add a new transaction. That means the size of # the field enumerating how many transaction go in the block # may change. current_block_size -= len(ser_compact_size(len(block.vtx))) current_block_size += len(ser_compact_size(len(block.vtx) + 1)) # Create the new transaction tx = get_base_transaction() # Add padding to fill the block. script_length = block_size - current_block_size - base_tx_size if script_length > 510000: if script_length < 1000000: # Make sure we don't find ourselves in a position where we # need to generate a transaction smaller than what we # expected. script_length = script_length // 2 else: script_length = 500000 tx_sigops = min(extra_sigops, script_length, MAX_TX_SIGOPS_COUNT) extra_sigops -= tx_sigops script_pad_len = script_length - tx_sigops script_output = CScript([b'\x00' * script_pad_len] + [OP_CHECKSIG] * tx_sigops) tx.vout.append(CTxOut(0, script_output)) # Add the tx to the list of transactions to be included # in the block. self.add_transactions_to_block(block, [tx]) current_block_size += len(tx.serialize()) # Now that we added a bunch of transaction, we need to recompute # the merkle root. make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() # Check that the block size is what's expected if block_size > 0: assert_equal(len(block.serialize()), block_size) # Do PoW, which is cheap on regnet block.solve() self.tip = block self.block_heights[block.sha256] = height assert number not in self.blocks self.blocks[number] = block return block
def run_test(self): print("Testing wallet secret recovery") self.test_wallet_recovery() print("Test blech32 python roundtrip") # blech/bech are aliased, both are blech32 for addrtype in ["bech32", "blech32"]: addr_to_rt = self.nodes[0].getnewaddress("", addrtype) hrp = addr_to_rt[:2] assert_equal(hrp, "el") (witver, witprog) = decode(hrp, addr_to_rt) assert_equal(encode(hrp, witver, witprog), addr_to_rt) # Test that "blech32" gives a blinded segwit address. blech32_addr = self.nodes[0].getnewaddress("", "blech32") blech32_addr_info = self.nodes[0].getaddressinfo(blech32_addr) assert_equal(blech32_addr_info["iswitness"], True) assert_equal(blech32_addr_info["confidential"], blech32_addr) print("General Confidential tests") # Running balances node0 = self.nodes[0].getbalance()["bitcoin"] assert_equal(node0, 21000000) # just making sure initialfreecoins is working node1 = 0 node2 = 0 self.nodes[0].generate(101) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), node0, "", "", True) self.nodes[0].generate(101) self.sync_all() assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance("*", 1, False, False, "bitcoin"), node2) # Send 3 BTC from 0 to a new unconfidential address of 2 with # the sendtoaddress call address = self.nodes[2].getnewaddress() unconfidential_address = self.nodes[2].validateaddress( address)["unconfidential"] value0 = 3 self.nodes[0].sendtoaddress(unconfidential_address, value0) self.nodes[0].generate(101) self.sync_all() node0 = node0 - value0 node2 = node2 + value0 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Send 5 BTC from 0 to a new address of 2 with the sendtoaddress call address2 = self.nodes[2].getnewaddress() unconfidential_address2 = self.nodes[2].validateaddress( address2)["unconfidential"] value1 = 5 confidential_tx_id = self.nodes[0].sendtoaddress(address2, value1) self.nodes[0].generate(101) self.sync_all() node0 = node0 - value1 node2 = node2 + value1 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Send 7 BTC from 0 to the unconfidential address of 2 and 11 BTC to the # confidential address using the raw transaction interface change_address = self.nodes[0].getnewaddress() value2 = 7 value3 = 11 value23 = value2 + value3 unspent = self.nodes[0].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) unspent = [i for i in unspent if i['amount'] > value23] assert_equal(len(unspent), 1) fee = Decimal('0.0001') tx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"] }], { unconfidential_address: value2, address2: value3, change_address: unspent[0]["amount"] - value2 - value3 - fee, "fee": fee }) tx = self.nodes[0].blindrawtransaction(tx) tx_signed = self.nodes[0].signrawtransactionwithwallet(tx) raw_tx_id = self.nodes[0].sendrawtransaction(tx_signed['hex']) self.nodes[0].generate(101) self.sync_all() node0 -= (value2 + value3) node2 += value2 + value3 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Check 2's listreceivedbyaddress received_by_address = self.nodes[2].listreceivedbyaddress( 0, False, False, "", "bitcoin") validate_by_address = [(address2, value1 + value3), (address, value0 + value2)] assert_equal( sorted([(ele['address'], ele['amount']) for ele in received_by_address], key=lambda t: t[0]), sorted(validate_by_address, key=lambda t: t[0])) received_by_address = self.nodes[2].listreceivedbyaddress( 0, False, False, "") validate_by_address = [(address2, { "bitcoin": value1 + value3 }), (address, { "bitcoin": value0 + value2 })] assert_equal( sorted([(ele['address'], ele['amount']) for ele in received_by_address], key=lambda t: t[0]), sorted(validate_by_address, key=lambda t: t[0])) # Give an auditor (node 1) a blinding key to allow her to look at # transaction values self.nodes[1].importaddress(address2) received_by_address = self.nodes[1].listreceivedbyaddress( 1, False, True) #Node sees nothing unless it understands the values assert_equal(len(received_by_address), 0) assert_equal( len(self.nodes[1].listunspent(1, 9999999, [], True, {"asset": "bitcoin"})), 0) # Import the blinding key blindingkey = self.nodes[2].dumpblindingkey(address2) self.nodes[1].importblindingkey(address2, blindingkey) # Check the auditor's gettransaction and listreceivedbyaddress # Needs rescan to update wallet txns conf_tx = self.nodes[1].gettransaction(confidential_tx_id, True) assert_equal(conf_tx['amount']["bitcoin"], value1) # Make sure wallet can now deblind part of transaction deblinded_tx = self.nodes[1].unblindrawtransaction( conf_tx['hex'])['hex'] for output in self.nodes[1].decoderawtransaction(deblinded_tx)["vout"]: if "value" in output and output["scriptPubKey"]["type"] != "fee": assert_equal( output["scriptPubKey"]["addresses"][0], self.nodes[1].validateaddress(address2)['unconfidential']) found_unblinded = True assert found_unblinded assert_equal( self.nodes[1].gettransaction(raw_tx_id, True)['amount']["bitcoin"], value3) assert_equal( self.nodes[1].gettransaction(raw_tx_id, True, False, "bitcoin")['amount'], value3) list_unspent = self.nodes[1].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) assert_equal(list_unspent[0]['amount'] + list_unspent[1]['amount'], value1 + value3) received_by_address = self.nodes[1].listreceivedbyaddress( 1, False, True) assert_equal(len(received_by_address), 1) assert_equal((received_by_address[0]['address'], received_by_address[0]['amount']['bitcoin']), (unconfidential_address2, value1 + value3)) # Spending a single confidential output and sending it to a # unconfidential output is not possible with CT. Test the # correct behavior of blindrawtransaction. unspent = self.nodes[0].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) unspent = [i for i in unspent if i['amount'] > value23] assert_equal(len(unspent), 1) tx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"] }], { unconfidential_address: unspent[0]["amount"] - fee, "fee": fee }) # Test that blindrawtransaction adds an OP_RETURN output to balance blinders temptx = self.nodes[0].blindrawtransaction(tx) decodedtx = self.nodes[0].decoderawtransaction(temptx) assert_equal(decodedtx["vout"][-1]["scriptPubKey"]["asm"], "OP_RETURN") assert_equal(len(decodedtx["vout"]), 3) # Create same transaction but with a change/dummy output. # It should pass the blinding step. value4 = 17 change_address = self.nodes[0].getrawchangeaddress() tx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"] }], { unconfidential_address: value4, change_address: unspent[0]["amount"] - value4 - fee, "fee": fee }) tx = self.nodes[0].blindrawtransaction(tx) tx_signed = self.nodes[0].signrawtransactionwithwallet(tx) txid = self.nodes[0].sendrawtransaction(tx_signed['hex']) decodedtx = self.nodes[0].decoderawtransaction(tx_signed["hex"]) self.nodes[0].generate(101) self.sync_all() unblindfound = False for i in range(len(decodedtx["vout"])): txout = self.nodes[0].gettxout(txid, i) if txout is not None and "asset" in txout: unblindfound = True if unblindfound == False: raise Exception( "No unconfidential output detected when one should exist") node0 -= value4 node2 += value4 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Testing wallet's ability to deblind its own outputs addr = self.nodes[0].getnewaddress() addr2 = self.nodes[0].getnewaddress() # We add two to-blind outputs, fundraw adds an already-blinded change output # If we only add one, the newly blinded will be 0-blinded because input = -output raw = self.nodes[0].createrawtransaction([], { addr: Decimal('1.1'), addr2: 1 }) funded = self.nodes[0].fundrawtransaction(raw) # fund again to make sure no blinded outputs were created (would fail) funded = self.nodes[0].fundrawtransaction(funded["hex"]) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) # blind again to make sure we know output blinders blinded2 = self.nodes[0].blindrawtransaction(blinded) # then sign and send signed = self.nodes[0].signrawtransactionwithwallet(blinded2) self.nodes[0].sendrawtransaction(signed["hex"]) # Aside: Check all outputs after fundraw are properly marked for blinding fund_decode = self.nodes[0].decoderawtransaction(funded["hex"]) for output in fund_decode["vout"][:-1]: assert "asset" in output assert "value" in output assert output["scriptPubKey"]["type"] != "fee" assert output["commitmentnonce_fully_valid"] assert fund_decode["vout"][-1]["scriptPubKey"]["type"] == "fee" assert not fund_decode["vout"][-1]["commitmentnonce_fully_valid"] # Also check that all fundraw outputs marked for blinding are blinded later for blind_tx in [blinded, blinded2]: blind_decode = self.nodes[0].decoderawtransaction(blind_tx) for output in blind_decode["vout"][:-1]: assert "asset" not in output assert "value" not in output assert output["scriptPubKey"]["type"] != "fee" assert output["commitmentnonce_fully_valid"] assert blind_decode["vout"][-1]["scriptPubKey"]["type"] == "fee" assert "asset" in blind_decode["vout"][-1] assert "value" in blind_decode["vout"][-1] assert not blind_decode["vout"][-1]["commitmentnonce_fully_valid"] # Check createblindedaddress functionality blinded_addr = self.nodes[0].getnewaddress() validated_addr = self.nodes[0].validateaddress(blinded_addr) blinding_pubkey = self.nodes[0].validateaddress( blinded_addr)["confidential_key"] blinding_key = self.nodes[0].dumpblindingkey(blinded_addr) assert_equal( blinded_addr, self.nodes[1].createblindedaddress( validated_addr["unconfidential"], blinding_pubkey)) # If a blinding key is over-ridden by a newly imported one, funds may be unaccounted for new_addr = self.nodes[0].getnewaddress() new_validated = self.nodes[0].validateaddress(new_addr) self.nodes[2].sendtoaddress(new_addr, 1) self.sync_all() diff_blind = self.nodes[1].createblindedaddress( new_validated["unconfidential"], blinding_pubkey) assert_equal( len(self.nodes[0].listunspent(0, 0, [new_validated["unconfidential"]])), 1) self.nodes[0].importblindingkey(diff_blind, blinding_key) # CT values for this wallet transaction have been cached via importblindingkey # therefore result will be same even though we change blinding keys assert_equal( len(self.nodes[0].listunspent(0, 0, [new_validated["unconfidential"]])), 1) # Confidential Assets Tests print("Assets tests...") # Bitcoin is the first issuance assert_equal(self.nodes[0].listissuances()[0]["assetlabel"], "bitcoin") assert_equal(len(self.nodes[0].listissuances()), 1) # Unblinded issuance of asset issued = self.nodes[0].issueasset(1, 1, False) self.nodes[0].reissueasset(issued["asset"], 1) # Compare resulting fields with getrawtransaction raw_details = self.nodes[0].getrawtransaction(issued["txid"], 1) assert_equal( issued["entropy"], raw_details["vin"][issued["vin"]]["issuance"]["assetEntropy"]) assert_equal(issued["asset"], raw_details["vin"][issued["vin"]]["issuance"]["asset"]) assert_equal(issued["token"], raw_details["vin"][issued["vin"]]["issuance"]["token"]) self.nodes[0].generate(1) self.sync_all() issued2 = self.nodes[0].issueasset(2, 1) test_asset = issued2["asset"] assert_equal(self.nodes[0].getwalletinfo()['balance'][test_asset], Decimal(2)) assert test_asset not in self.nodes[1].getwalletinfo()['balance'] # Assets balance checking, note that accounts are completely ignored because # balance queries with accounts are horrifically broken upstream assert_equal(self.nodes[0].getbalance("*", 0, False, False, "bitcoin"), self.nodes[0].getbalance("*", 0, False, False, "bitcoin")) assert_equal(self.nodes[0].getbalance("*", 0, False, False)["bitcoin"], self.nodes[0].getbalance("*", 0, False, False, "bitcoin")) assert_equal(self.nodes[0].getwalletinfo()['balance']['bitcoin'], self.nodes[0].getbalance("*", 0, False, False, "bitcoin")) # Send some bitcoin and other assets over as well to fund wallet addr = self.nodes[2].getnewaddress() txid = self.nodes[0].sendtoaddress(addr, 5) # Make sure we're doing 52 bits of hiding which covers 21M BTC worth assert_equal( self.nodes[0].getrawtransaction(txid, 1)["vout"][0]["ct-bits"], 52) self.nodes[0].sendmany("", { addr: 1, self.nodes[2].getnewaddress(): 13 }, 0, "", [], False, 1, "UNSET", {addr: test_asset}) self.sync_all() # Should have exactly 1 in change(trusted, though not confirmed) after sending one off assert_equal( self.nodes[0].getbalance("*", 0, False, False, test_asset), 1) assert_equal(self.nodes[2].getunconfirmedbalance()[test_asset], Decimal(1)) b_utxos = self.nodes[2].listunspent(0, 0, [], True, {"asset": "bitcoin"}) t_utxos = self.nodes[2].listunspent(0, 0, [], True, {"asset": test_asset}) assert_equal(len(self.nodes[2].listunspent(0, 0, [])), len(b_utxos) + len(t_utxos)) # Now craft a blinded transaction via raw api rawaddrs = [] for i in range(2): rawaddrs.append(self.nodes[1].getnewaddress()) raw_assets = self.nodes[2].createrawtransaction( [{ "txid": b_utxos[0]['txid'], "vout": b_utxos[0]['vout'], "nValue": b_utxos[0]['amount'] }, { "txid": b_utxos[1]['txid'], "vout": b_utxos[1]['vout'], "nValue": b_utxos[1]['amount'], "asset": b_utxos[1]['asset'] }, { "txid": t_utxos[0]['txid'], "vout": t_utxos[0]['vout'], "nValue": t_utxos[0]['amount'], "asset": t_utxos[0]['asset'] }], { rawaddrs[1]: Decimal(t_utxos[0]['amount']), rawaddrs[0]: Decimal(b_utxos[0]['amount'] + b_utxos[1]['amount'] - Decimal("0.01")), "fee": Decimal("0.01") }, 0, False, { rawaddrs[0]: b_utxos[0]['asset'], rawaddrs[1]: t_utxos[0]['asset'], "fee": b_utxos[0]['asset'] }) # Sign unblinded, then blinded signed_assets = self.nodes[2].signrawtransactionwithwallet(raw_assets) blind_assets = self.nodes[2].blindrawtransaction(raw_assets) signed_assets = self.nodes[2].signrawtransactionwithwallet( blind_assets) # And finally send self.nodes[2].sendrawtransaction(signed_assets['hex']) self.nodes[2].generate(101) self.sync_all() issuancedata = self.nodes[2].issueasset( 0, Decimal('0.00000006')) #0 of asset, 6 reissuance token # Node 2 will send node 1 a reissuance token, both will generate assets self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), Decimal('0.00000001'), "", "", False, False, 1, "UNSET", False, issuancedata["token"]) # node 1 needs to know about a (re)issuance to reissue itself self.nodes[1].importaddress(self.nodes[2].gettransaction( issuancedata["txid"])["details"][0]["address"]) # also send some bitcoin self.nodes[2].generate(1) self.sync_all() self.nodes[1].reissueasset(issuancedata["asset"], Decimal('0.05')) self.nodes[2].reissueasset(issuancedata["asset"], Decimal('0.025')) self.nodes[1].generate(1) self.sync_all() # Check for value accounting when asset issuance is null but token not, ie unblinded # HACK: Self-send to sweep up bitcoin inputs into blinded output. # We were hitting https://github.com/ElementsProject/elements/issues/473 for the following issuance self.nodes[0].sendtoaddress( self.nodes[0].getnewaddress(), self.nodes[0].getwalletinfo()["balance"]["bitcoin"], "", "", True) issued = self.nodes[0].issueasset(0, 1, False) walletinfo = self.nodes[0].getwalletinfo() assert issued["asset"] not in walletinfo["balance"] assert_equal(walletinfo["balance"][issued["token"]], Decimal(1)) assert issued["asset"] not in walletinfo["unconfirmed_balance"] assert issued["token"] not in walletinfo["unconfirmed_balance"] # Check for value when receiving different assets by same address. self.nodes[0].sendtoaddress(unconfidential_address2, Decimal('0.00000001'), "", "", False, False, 1, "UNSET", False, test_asset) self.nodes[0].sendtoaddress(unconfidential_address2, Decimal('0.00000002'), "", "", False, False, 1, "UNSET", False, test_asset) self.nodes[0].generate(1) self.sync_all() received_by_address = self.nodes[1].listreceivedbyaddress( 0, False, True) multi_asset_amount = [ x for x in received_by_address if x['address'] == unconfidential_address2 ][0]['amount'] assert_equal(multi_asset_amount['bitcoin'], value1 + value3) assert_equal(multi_asset_amount[test_asset], Decimal('0.00000003')) # Check blinded multisig functionality and partial blinding functionality # Get two pubkeys blinded_addr = self.nodes[0].getnewaddress() pubkey = self.nodes[0].getaddressinfo(blinded_addr)["pubkey"] blinded_addr2 = self.nodes[1].getnewaddress() pubkey2 = self.nodes[1].getaddressinfo(blinded_addr2)["pubkey"] pubkeys = [pubkey, pubkey2] # Add multisig address unconfidential_addr = self.nodes[0].addmultisigaddress( 2, pubkeys)["address"] self.nodes[1].addmultisigaddress(2, pubkeys) self.nodes[0].importaddress(unconfidential_addr) self.nodes[1].importaddress(unconfidential_addr) # Use blinding key from node 0's original getnewaddress call blinding_pubkey = self.nodes[0].getaddressinfo( blinded_addr)["confidential_key"] blinding_key = self.nodes[0].dumpblindingkey(blinded_addr) # Create blinded address from p2sh address and import corresponding privkey blinded_multisig_addr = self.nodes[0].createblindedaddress( unconfidential_addr, blinding_pubkey) self.nodes[0].importblindingkey(blinded_multisig_addr, blinding_key) # Issue new asset, to use different assets in one transaction when doing # partial blinding. Just to make these tests a bit more elaborate :-) issued3 = self.nodes[2].issueasset(1, 0) self.nodes[2].generate(1) self.sync_all() node2_balance = self.nodes[2].getbalance() assert issued3['asset'] in node2_balance assert_equal(node2_balance[issued3['asset']], Decimal(1)) # Send asset to blinded multisig address and check that it was received self.nodes[2].sendtoaddress(address=blinded_multisig_addr, amount=1, assetlabel=issued3['asset']) self.sync_all() # We will use this multisig UTXO in our partially-blinded transaction, # and will also check that multisig UTXO can be successfully spent # after the transaction is signed by node1 and node0 in succession. unspent_asset = self.nodes[0].listunspent(0, 0, [unconfidential_addr], True, {"asset": issued3['asset']}) assert_equal(len(unspent_asset), 1) assert issued3['asset'] not in self.nodes[2].getbalance() # Create new UTXO on node0 to be used in our partially-blinded transaction blinded_addr = self.nodes[0].getnewaddress() addr = self.nodes[0].validateaddress(blinded_addr)["unconfidential"] self.nodes[0].sendtoaddress(blinded_addr, 0.1) unspent = self.nodes[0].listunspent(0, 0, [addr]) assert_equal(len(unspent), 1) # Create new UTXO on node1 to be used in our partially-blinded transaction blinded_addr2 = self.nodes[1].getnewaddress() addr2 = self.nodes[1].validateaddress(blinded_addr2)["unconfidential"] self.nodes[1].sendtoaddress(blinded_addr2, 0.11) unspent2 = self.nodes[1].listunspent(0, 0, [addr2]) assert_equal(len(unspent2), 1) # The transaction will have three non-fee outputs dst_addr = self.nodes[0].getnewaddress() dst_addr2 = self.nodes[1].getnewaddress() dst_addr3 = self.nodes[2].getnewaddress() # Inputs are selected up front inputs = [{ "txid": unspent2[0]["txid"], "vout": unspent2[0]["vout"] }, { "txid": unspent[0]["txid"], "vout": unspent[0]["vout"] }, { "txid": unspent_asset[0]["txid"], "vout": unspent_asset[0]["vout"] }] # Create one part of the transaction to partially blind rawtx = self.nodes[0].createrawtransaction( inputs[:1], {dst_addr2: Decimal("0.01")}) # Create another part of the transaction to partially blind rawtx2 = self.nodes[0].createrawtransaction( inputs[1:], { dst_addr: Decimal("0.1"), dst_addr3: Decimal("1.0") }, 0, False, { dst_addr: unspent[0]['asset'], dst_addr3: unspent_asset[0]['asset'] }) sum_i = unspent2[0]["amount"] + unspent[0]["amount"] sum_o = 0.01 + 0.10 + 0.1 assert_equal(int(round(sum_i * COIN)), int(round(sum_o * COIN))) # Blind the first part of the transaction - we need to supply the # assetcommmitments for all of the inputs, for the surjectionproof # to be valid after we combine the transactions blindtx = self.nodes[1].blindrawtransaction(rawtx, True, [ unspent2[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent_asset[0]['assetcommitment'] ]) # Combine the transactions # Blinded, but incomplete transaction. # 1 inputs and 1 output, but no fee output, and # it was blinded with 3 asset commitments, that means # the final transaction should have 3 inputs. btx = CTransaction() btx.deserialize(io.BytesIO(hex_str_to_bytes(blindtx))) # Unblinded transaction, with 2 inputs and 2 outputs. # We will add them to the other transaction to make it complete. ubtx = CTransaction() ubtx.deserialize(io.BytesIO(hex_str_to_bytes(rawtx2))) # We will add outputs of unblinded transaction # on top of inputs and outputs of the blinded, but incomplete transaction. # We also append empty witness instances to make witness arrays match # vin/vout arrays btx.vin.append(ubtx.vin[0]) btx.wit.vtxinwit.append(CTxInWitness()) btx.vout.append(ubtx.vout[0]) btx.wit.vtxoutwit.append(CTxOutWitness()) btx.vin.append(ubtx.vin[1]) btx.wit.vtxinwit.append(CTxInWitness()) btx.vout.append(ubtx.vout[1]) btx.wit.vtxoutwit.append(CTxOutWitness()) # Add explicit fee output btx.vout.append( CTxOut(nValue=CTxOutValue(10000000), nAsset=CTxOutAsset(BITCOIN_ASSET_OUT))) btx.wit.vtxoutwit.append(CTxOutWitness()) # Input 0 is bitcoin asset (already blinded) # Input 1 is also bitcoin asset # Input 2 is our new asset # Blind with wrong order of assetcommitments - such transaction should be rejected blindtx = self.nodes[0].blindrawtransaction( btx.serialize().hex(), True, [ unspent_asset[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent2[0]['assetcommitment'] ]) stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx) stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex']) self.sync_all() assert_raises_rpc_error(-26, "bad-txns-in-ne-out", self.nodes[2].sendrawtransaction, stx['hex']) # Blind with correct order of assetcommitments blindtx = self.nodes[0].blindrawtransaction( btx.serialize().hex(), True, [ unspent2[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent_asset[0]['assetcommitment'] ]) stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx) stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex']) txid = self.nodes[2].sendrawtransaction(stx['hex']) self.nodes[2].generate(1) assert self.nodes[2].gettransaction(txid)['confirmations'] == 1 self.sync_all() # Check that the sent asset has reached its destination unconfidential_dst_addr3 = self.nodes[2].validateaddress( dst_addr3)["unconfidential"] unspent_asset2 = self.nodes[2].listunspent(1, 1, [unconfidential_dst_addr3], True, {"asset": issued3['asset']}) assert_equal(len(unspent_asset2), 1) assert_equal(unspent_asset2[0]['amount'], Decimal(1)) # And that the balance was correctly updated assert_equal(self.nodes[2].getbalance()[issued3['asset']], Decimal(1)) # Basic checks of rawblindrawtransaction functionality blinded_addr = self.nodes[0].getnewaddress() addr = self.nodes[0].validateaddress(blinded_addr)["unconfidential"] self.nodes[0].sendtoaddress(blinded_addr, 1) self.nodes[0].sendtoaddress(blinded_addr, 3) unspent = self.nodes[0].listunspent(0, 0) rawtx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"] }, { "txid": unspent[1]["txid"], "vout": unspent[1]["vout"] }], { addr: unspent[0]["amount"] + unspent[1]["amount"] - Decimal("0.2"), "fee": Decimal("0.2") }) # Blinding will fail with 2 blinded inputs and 0 blinded outputs # since it has no notion of a wallet to fill in a 0-value OP_RETURN output try: self.nodes[0].rawblindrawtransaction( rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) raise AssertionError( "Shouldn't be able to blind 2 input 0 output transaction via rawblindraw" ) except JSONRPCException: pass # Blinded destination added, can blind, sign and send rawtx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"] }, { "txid": unspent[1]["txid"], "vout": unspent[1]["vout"] }], { blinded_addr: unspent[0]["amount"] + unspent[1]["amount"] - Decimal("0.002"), "fee": Decimal("0.002") }) signtx = self.nodes[0].signrawtransactionwithwallet(rawtx) try: self.nodes[0].sendrawtransaction(signtx["hex"]) raise AssertionError( "Shouldn't be able to send unblinded tx with emplaced pubkey in output without additional argument" ) except JSONRPCException: pass # Make sure RPC throws when an invalid blinding factor is provided. bad_blinder = 'FF' * 32 assert_raises_rpc_error( -8, "Unable to blind transaction: Are you sure each asset type to blind is represented in the inputs?", self.nodes[0].rawblindrawtransaction, rawtx, [unspent[0]["amountblinder"], bad_blinder], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) assert_raises_rpc_error( -8, "Unable to blind transaction: Are you sure each asset type to blind is represented in the inputs?", self.nodes[0].rawblindrawtransaction, rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], bad_blinder]) blindtx = self.nodes[0].rawblindrawtransaction( rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) signtx = self.nodes[0].signrawtransactionwithwallet(blindtx) txid = self.nodes[0].sendrawtransaction(signtx["hex"]) for output in self.nodes[0].decoderawtransaction(blindtx)["vout"]: if "asset" in output and output["scriptPubKey"]["type"] != "fee": raise AssertionError("An unblinded output exists") # Test fundrawtransaction with multiple assets issue = self.nodes[0].issueasset(1, 0) assetaddr = self.nodes[0].getnewaddress() rawtx = self.nodes[0].createrawtransaction( [], { assetaddr: 1, self.nodes[0].getnewaddress(): 2 }, 0, False, {assetaddr: issue["asset"]}) funded = self.nodes[0].fundrawtransaction(rawtx) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) signed = self.nodes[0].signrawtransactionwithwallet(blinded) txid = self.nodes[0].sendrawtransaction(signed["hex"]) # Test fundrawtransaction with multiple inputs, creating > vout.size change rawtx = self.nodes[0].createrawtransaction( [{ "txid": txid, "vout": 0 }, { "txid": txid, "vout": 1 }], {self.nodes[0].getnewaddress(): 5}) funded = self.nodes[0].fundrawtransaction(rawtx) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) signed = self.nodes[0].signrawtransactionwithwallet(blinded) txid = self.nodes[0].sendrawtransaction(signed["hex"]) # Test corner case where wallet appends a OP_RETURN output, yet doesn't blind it # due to the fact that the output value is 0-value and input pedersen commitments # self-balance. This is rare corner case, but ok. unblinded = self.nodes[0].validateaddress( self.nodes[0].getnewaddress())["unconfidential"] self.nodes[0].sendtoaddress(unblinded, self.nodes[0].getbalance()["bitcoin"], "", "", True) # Make tx with blinded destination and change outputs only self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[0].getbalance()["bitcoin"] / 2) # Send back again, this transaction should have 3 outputs, all unblinded txid = self.nodes[0].sendtoaddress( unblinded, self.nodes[0].getbalance()["bitcoin"], "", "", True) outputs = self.nodes[0].getrawtransaction(txid, 1)["vout"] assert_equal(len(outputs), 3) assert "value" in outputs[0] and "value" in outputs[ 1] and "value" in outputs[2] assert_equal(outputs[2]["scriptPubKey"]["type"], 'nulldata') # Test burn argument in createrawtransaction raw_burn1 = self.nodes[0].createrawtransaction( [], { self.nodes[0].getnewaddress(): 1, "burn": 2 }) decode_burn1 = self.nodes[0].decoderawtransaction(raw_burn1) assert_equal(len(decode_burn1["vout"]), 2) found_pay = False found_burn = False for output in decode_burn1["vout"]: if output["scriptPubKey"]["asm"] == "OP_RETURN": found_burn = True if output["asset"] != self.nodes[0].dumpassetlabels( )["bitcoin"]: raise Exception( "Burn should have been bitcoin(policyAsset)") if output["scriptPubKey"]["type"] == "witness_v0_keyhash": found_pay = True assert found_pay and found_burn raw_burn2 = self.nodes[0].createrawtransaction( [], { self.nodes[0].getnewaddress(): 1, "burn": 2 }, 101, False, {"burn": "deadbeef" * 8}) decode_burn2 = self.nodes[0].decoderawtransaction(raw_burn2) assert_equal(len(decode_burn2["vout"]), 2) found_pay = False found_burn = False for output in decode_burn2["vout"]: if output["scriptPubKey"]["asm"] == "OP_RETURN": found_burn = True if output["asset"] != "deadbeef" * 8: raise Exception("Burn should have been deadbeef") if output["scriptPubKey"]["type"] == "witness_v0_keyhash": found_pay = True assert found_pay and found_burn
def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.nodes[0].generate(100) # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) # and we get disconnected immediately self.log.info('Test a transaction that is rejected') tx1 = create_tx_with_script(block1.vtx[0], 0, script_sig=b'\x64' * 35, amount=50 * COIN - 12000) node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True) # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withhold until all dependend transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() tx_withhold.vin.append( CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) tx_withhold.vout.append( CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_withhold.calc_sha256() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append( CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) tx_orphan_1.vout = [ CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE) ] * 3 tx_orphan_1.calc_sha256() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) tx_orphan_2_no_fee.vout.append( CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) tx_orphan_2_valid.vout.append( CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_sha256() # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) tx_orphan_2_invalid.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2p.send_txs_and_test( [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) # Send invalid tx from p2ps[1] node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected self.log.info('Send the withhold tx ... ') node.p2p.send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { t.hash for t in [ tx_withhold, # The transaction that is the root for all orphans tx_orphan_1, # The orphan transaction that splits the coins tx_orphan_2_valid, # The valid transaction (with sufficient fee) ] } # Transactions that do not end up in the mempool # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool())) # restart node with sending BIP61 messages disabled, check that it disconnects without sending the reject message self.log.info( 'Test a transaction that is rejected, with BIP61 disabled') self.restart_node(0, ['-enablebip61=0', '-persistmempool=0']) self.reconnect_p2p(num_connections=1) node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True) # send_txs_and_test will have waited for disconnect, so we can safely check that no reject has been received assert_equal(node.p2p.reject_code_received, None)
def test_namescript_p2sh(self): """ Tests how name prefixes interact with P2SH outputs and redeem scripts. """ self.log.info("Testing name prefix and P2SH interactions...") # This test only needs a single node and no syncing. node = self.nodes[0] name = "d/p2sh" value = val("value") node.name_register(name, value) node.generate(1) baseHeight = node.getblockcount() self.checkNameWithHeight(0, name, value, baseHeight) # Prepare some scripts and P2SH addresses we use later. We build the # name script prefix for an update to our testname, so that we can build # P2SH redeem scripts with (or without) it. nameBytes = codecs.encode(name, 'ascii') valueBytes = codecs.encode(value, 'ascii') updOps = [OP_NAME_UPDATE, nameBytes, valueBytes, OP_2DROP, OP_DROP] anyoneOps = [OP_TRUE] updScript = CScript(updOps) anyoneScript = CScript(anyoneOps) updAndAnyoneScript = CScript(updOps + anyoneOps) anyoneAddr = self.getP2SH(0, anyoneScript) updAndAnyoneAddr = self.getP2SH(0, updAndAnyoneScript) # Send the name to the anyone-can-spend name-update script directly. # This is expected to update the name (verifies the update script is good). tx = CTransaction() data = node.name_show(name) tx.vin.append(CTxIn(COutPoint(int(data['txid'], 16), data['vout']))) tx.vout.append(CTxOut(COIN // 100, updAndAnyoneScript)) txHex = tx.serialize().hex() txHex = node.fundrawtransaction(txHex)['hex'] signed = node.signrawtransactionwithwallet(txHex) assert signed['complete'] node.sendrawtransaction(signed['hex']) node.generate(1) self.checkNameWithHeight(0, name, value, baseHeight + 1) # Send the name to the anyone-can-spend P2SH address. This should just # work fine and update the name. self.updateAnyoneCanSpendName(0, name, val("value2"), anyoneAddr, []) node.generate(1) self.checkNameWithHeight(0, name, val("value2"), baseHeight + 2) # Send a coin to the P2SH address with name prefix. This should just # work fine but not update the name. We should be able to spend the coin # again from that address. txid = node.sendtoaddress(updAndAnyoneAddr, 2) tx = node.getrawtransaction(txid) ind = self.rawtxOutputIndex(0, tx, updAndAnyoneAddr) node.generate(1) ins = [{"txid": txid, "vout": ind}] addr = node.getnewaddress() out = {addr: 1} tx = node.createrawtransaction(ins, out) tx = self.setScriptSigOps(tx, 0, [updAndAnyoneScript]) node.sendrawtransaction(tx, 0) node.generate(1) self.checkNameWithHeight(0, name, val("value2"), baseHeight + 2) found = False for u in node.listunspent(): if u['address'] == addr and u['amount'] == 1: found = True break if not found: raise AssertionError("Coin not sent to expected address") # Send the name to the P2SH address with name prefix and then spend it # again. Spending should work fine, and the name should just be updated # ordinarily; the name prefix of the redeem script should have no effect. self.updateAnyoneCanSpendName(0, name, val("value3"), updAndAnyoneAddr, [anyoneScript]) node.generate(1) self.checkNameWithHeight(0, name, val("value3"), baseHeight + 5) self.updateAnyoneCanSpendName(0, name, val("value4"), anyoneAddr, [updAndAnyoneScript]) node.generate(1) self.checkNameWithHeight(0, name, val("value4"), baseHeight + 6)
def run_test(self): # Create a block with 2500 stakeable outputs self.build_coins_to_stake() # Propagate it to nodes 1 and 2 and stop them for now self.sync_first_block() # Key Management for node 0 keytool = KeyTool.for_node(self.nodes[0]) # Connect to node0 p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) # Build the blockchain self.tip = int(self.nodes[0].getbestblockhash(), 16) self.block_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] + 1 self.blocks = [] # Get a pubkey for the coinbase TXO coinbase_key = keytool.make_privkey() coinbase_pubkey = bytes(coinbase_key.get_pubkey()) keytool.upload_key(coinbase_key) self.log.info( "Create the first block with a coinbase output to our key") height = 2 snapshot_meta = get_tip_snapshot_meta(self.nodes[0]) coin = self.get_coin_to_stake() coinbase = sign_coinbase( self.nodes[0], create_coinbase(height, coin, snapshot_meta.hash, coinbase_pubkey)) block = create_block(self.tip, coinbase, self.block_time) self.blocks.append(block) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 utxo1 = UTXO(height, TxType.COINBASE, COutPoint(coinbase.sha256, 0), coinbase.vout[0]) snapshot_meta = update_snapshot_with_tx(self.nodes[0], snapshot_meta, height, coinbase) height += 1 self.log.info( "Bury the block 100 deep so the coinbase output is spendable") for i in range(100): coin = self.get_coin_to_stake() coinbase = sign_coinbase( self.nodes[0], create_coinbase(height, coin, snapshot_meta.hash, coinbase_pubkey)) block = create_block(self.tip, coinbase, self.block_time) block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 snapshot_meta = update_snapshot_with_tx(self.nodes[0], snapshot_meta, height, coinbase) height += 1 self.log.info( "Create a transaction spending the coinbase output with an invalid (null) signature" ) tx = CTransaction() tx.vin.append( CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b"")) tx.vout.append( CTxOut((PROPOSER_REWARD - 1) * 100000000, CScript([OP_TRUE]))) tx.calc_sha256() coin = self.get_coin_to_stake() coinbase = sign_coinbase( self.nodes[0], create_coinbase(height, coin, snapshot_meta.hash, coinbase_pubkey)) block102 = create_block(self.tip, coinbase, self.block_time) self.block_time += 1 block102.vtx.extend([tx]) block102.compute_merkle_trees() block102.rehash() block102.solve() self.blocks.append(block102) self.tip = block102.sha256 self.block_time += 1 snapshot_meta = update_snapshot_with_tx(self.nodes[0], snapshot_meta, height, coinbase) utxo2 = UTXO(height, tx.get_type(), COutPoint(tx.sha256, 0), tx.vout[0]) snapshot_meta = calc_snapshot_hash(self.nodes[0], snapshot_meta, height, [utxo1], [utxo2]) height += 1 self.log.info("Bury the assumed valid block 2100 deep") for i in range(2100): coin = self.get_coin_to_stake() coinbase = sign_coinbase( self.nodes[0], create_coinbase(height, coin, snapshot_meta.hash, coinbase_pubkey)) block = create_block(self.tip, coinbase, self.block_time) block.nVersion = 4 block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 snapshot_meta = update_snapshot_with_tx(self.nodes[0], snapshot_meta, height, coinbase) height += 1 self.nodes[0].disconnect_p2ps() self.log.info( "Start node1 and node2 with assumevalid so they accept a block with a bad signature." ) self.start_node(1, extra_args=[ "-assumevalid=" + hex(block102.sha256), ESPERANZA_CONFIG ]) self.start_node(2, extra_args=[ "-assumevalid=" + hex(block102.sha256), ESPERANZA_CONFIG ]) p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) p2p2 = self.nodes[2].add_p2p_connection(BaseNode()) # send header lists to all three nodes p2p0.send_header_for_blocks(self.blocks[0:2000]) p2p0.send_header_for_blocks(self.blocks[2000:]) p2p1.send_header_for_blocks(self.blocks[0:2000]) p2p1.send_header_for_blocks(self.blocks[2000:]) p2p2.send_header_for_blocks(self.blocks[0:200]) self.log.info("Send blocks to node0. Block 103 will be rejected.") self.send_blocks_until_disconnected(p2p0) self.assert_blockchain_height(self.nodes[0], 102) self.log.info("Send all blocks to node1. All blocks will be accepted.") for i in range(2202): p2p1.send_message(msg_block(self.blocks[i])) # Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync. p2p1.sync_with_ping(120) assert_equal( self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2203) self.log.info("Send blocks to node2. Block 102 will be rejected.") self.send_blocks_until_disconnected(p2p2) self.assert_blockchain_height(self.nodes[2], 102)
def def_utxo(height): hex_id = hex_str_to_bytes('0' * 64) uint256 = uint256_from_str(hex_id) return UTXO(height, TxType.REGULAR, COutPoint(uint256, 0), CTxOut(0, b""))
def test_coinbase_witness(self): def WitToHex(obj): return bytes_to_hex_str(obj.serialize(with_witness=True)) block = self.nodes[0].getnewblockhex() block_struct = FromHex(CBlock(), block) # Test vanilla block round-trip self.nodes[0].testproposedblock(WitToHex(block_struct)) # Assert there's scriptWitness in the coinbase input that is the witness nonce and nothing else assert_equal(block_struct.vtx[0].wit.vtxinwit[0].scriptWitness.stack, [b'\x00' * 32]) assert_equal( block_struct.vtx[0].wit.vtxinwit[0].vchIssuanceAmountRangeproof, b'') assert_equal( block_struct.vtx[0].wit.vtxinwit[0].vchInflationKeysRangeproof, b'') assert_equal(block_struct.vtx[0].wit.vtxinwit[0].peginWitness.stack, []) # Add extra witness that isn't covered by witness merkle root, make sure blocks are still valid block_witness_stuffed = copy.deepcopy(block_struct) block_witness_stuffed.vtx[0].wit.vtxinwit[ 0].vchIssuanceAmountRangeproof = b'\x00' assert_raises_rpc_error(-25, "bad-cb-witness", self.nodes[0].testproposedblock, WitToHex(block_witness_stuffed)) block_witness_stuffed = copy.deepcopy(block_struct) block_witness_stuffed.vtx[0].wit.vtxinwit[ 0].vchInflationKeysRangeproof = b'\x00' assert_raises_rpc_error(-25, "bad-cb-witness", self.nodes[0].testproposedblock, WitToHex(block_witness_stuffed)) block_witness_stuffed = copy.deepcopy(block_struct) # Let's blow out block weight limit by adding 4MW here block_witness_stuffed.vtx[0].wit.vtxinwit[0].peginWitness.stack = [ b'\x00' * 4000000 ] assert_raises_rpc_error(-25, "bad-cb-witness", self.nodes[0].testproposedblock, WitToHex(block_witness_stuffed)) # Test that node isn't blinded to the block # Previously an over-stuffed block >4MW would have been marked permanently bad # as it already passes witness merkle and regular merkle root checks block_height = self.nodes[0].getblockcount() assert_equal( self.nodes[0].submitblock(WitToHex(block_witness_stuffed)), "bad-cb-witness") assert_equal(block_height, self.nodes[0].getblockcount()) assert_equal(self.nodes[0].submitblock(WitToHex(block_struct)), None) assert_equal(block_height + 1, self.nodes[0].getblockcount()) # New block since we used the first one block_struct = FromHex(CBlock(), self.nodes[0].getnewblockhex()) block_witness_stuffed = copy.deepcopy(block_struct) # Add extra witness data that is covered by witness merkle root, make sure invalid assert_equal( block_witness_stuffed.vtx[0].wit.vtxoutwit[0].vchSurjectionproof, b'') assert_equal( block_witness_stuffed.vtx[0].wit.vtxoutwit[0].vchRangeproof, b'') block_witness_stuffed.vtx[0].wit.vtxoutwit[ 0].vchRangeproof = b'\x00' * 100000 block_witness_stuffed.vtx[0].wit.vtxoutwit[ 0].vchSurjectionproof = b'\x00' * 100000 assert_raises_rpc_error(-25, "bad-witness-merkle-match", self.nodes[0].testproposedblock, WitToHex(block_witness_stuffed)) witness_root_hex = block_witness_stuffed.calc_witness_merkle_root() witness_root = uint256_from_str( hex_str_to_bytes(witness_root_hex)[::-1]) block_witness_stuffed.vtx[0].vout[-1] = CTxOut( 0, get_witness_script(witness_root, 0)) block_witness_stuffed.vtx[0].rehash() block_witness_stuffed.hashMerkleRoot = block_witness_stuffed.calc_merkle_root( ) block_witness_stuffed.rehash() assert_raises_rpc_error(-25, "bad-cb-amount", self.nodes[0].testproposedblock, WitToHex(block_witness_stuffed)) assert_greater_than( len(WitToHex(block_witness_stuffed)), 100000 * 4) # Make sure the witness data is actually serialized # A CTxIn that always serializes the asset issuance, even for coinbases. class AlwaysIssuanceCTxIn(CTxIn): def serialize(self): r = b'' outpoint = COutPoint() outpoint.hash = self.prevout.hash outpoint.n = self.prevout.n outpoint.n |= OUTPOINT_ISSUANCE_FLAG r += outpoint.serialize() r += ser_string(self.scriptSig) r += struct.pack("<I", self.nSequence) r += self.assetIssuance.serialize() return r # Test that issuance inputs in coinbase don't survive a serialization round-trip # (even though this can't cause issuance to occur either way due to VerifyCoinbaseAmount semantics) block_witness_stuffed = copy.deepcopy(block_struct) coinbase_orig = copy.deepcopy(block_witness_stuffed.vtx[0].vin[0]) coinbase_ser_size = len( block_witness_stuffed.vtx[0].vin[0].serialize()) block_witness_stuffed.vtx[0].vin[0] = AlwaysIssuanceCTxIn() block_witness_stuffed.vtx[0].vin[0].prevout = coinbase_orig.prevout block_witness_stuffed.vtx[0].vin[0].scriptSig = coinbase_orig.scriptSig block_witness_stuffed.vtx[0].vin[0].nSequence = coinbase_orig.nSequence block_witness_stuffed.vtx[0].vin[0].assetIssuance.nAmount.setToAmount( 1) bad_coinbase_ser_size = len( block_witness_stuffed.vtx[0].vin[0].serialize()) # 32+32+9+1 should be serialized for each assetIssuance field assert_equal(bad_coinbase_ser_size, coinbase_ser_size + 32 + 32 + 9 + 1) assert (not block_witness_stuffed.vtx[0].vin[0].assetIssuance.isNull()) assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, ToHex(block_witness_stuffed.vtx[0]))
def run_test(self): node = self.nodes[0] # convenience reference to the node self.address = node.getnewaddress() node.add_p2p_connection(P2PDataStore()) node.p2p.wait_for_getheaders(timeout=5) self.address = self.nodes[0].getnewaddress() self.log.info("Test starting...") #generate 10 blocks for coinbase outputs coinbase_txs = [] for i in range(1, 10): height = node.getblockcount() + 1 coinbase_tx = create_coinbase(height, self.coinbase_pubkey) coinbase_txs.append(coinbase_tx) tip = node.getbestblockhash() block_time = node.getblockheader(tip)["mediantime"] + 1 block = create_block(int(tip, 16), coinbase_tx, block_time) block.solve(self.signblockprivkey) tip = block.hash node.p2p.send_and_ping(msg_block(block)) assert_equal(node.getbestblockhash(), tip) change_script = CScript([self.coinbase_pubkey, OP_CHECKSIG]) burn_script = CScript([hex_str_to_bytes(self.pubkeys[1]), OP_CHECKSIG]) #TxSuccess1 - coinbaseTx1 - issue 100 REISSUABLE + 30 (UTXO-1,2) colorId_reissuable = colorIdReissuable(coinbase_txs[0].vout[0].scriptPubKey) script_reissuable = CP2PHK_script(colorId = colorId_reissuable, pubkey = self.pubkeys[0]) script_transfer_reissuable = CP2PHK_script(colorId = colorId_reissuable, pubkey = self.pubkeys[1]) txSuccess1 = CTransaction() txSuccess1.vin.append(CTxIn(COutPoint(coinbase_txs[0].malfixsha256, 0), b"")) txSuccess1.vout.append(CTxOut(100, script_reissuable)) txSuccess1.vout.append(CTxOut(30 * COIN, CScript([self.coinbase_pubkey, OP_CHECKSIG]))) sig_hash, err = SignatureHash(coinbase_txs[0].vout[0].scriptPubKey, txSuccess1, 0, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL txSuccess1.vin[0].scriptSig = CScript([signature]) txSuccess1.rehash() test_transaction_acceptance(node, txSuccess1, accepted=True) tx_info = node.getrawtransaction(txSuccess1.hashMalFix, 1) assert_equal(tx_info['vout'][0]['token'], bytes_to_hex_str(colorId_reissuable)) assert_equal(tx_info['vout'][0]['value'], 100) #TxSuccess2 - (UTXO-2) - issue 100 NON-REISSUABLE (UTXO-3) colorId_nonreissuable = colorIdNonReissuable(COutPoint(txSuccess1.malfixsha256, 1).serialize()) script_nonreissuable = CP2PHK_script(colorId = colorId_nonreissuable, pubkey = self.pubkeys[0]) script_transfer_nonreissuable = CP2PHK_script(colorId = colorId_nonreissuable, pubkey = self.pubkeys[1]) txSuccess2 = CTransaction() txSuccess2.vin.append(CTxIn(COutPoint(txSuccess1.malfixsha256, 1), b"")) txSuccess2.vout.append(CTxOut(100, script_nonreissuable)) sig_hash, err = SignatureHash(txSuccess1.vout[1].scriptPubKey, txSuccess2, 0, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' txSuccess2.vin[0].scriptSig = CScript([signature]) txSuccess2.rehash() test_transaction_acceptance(node, txSuccess2, accepted=True) tx_info = node.getrawtransaction(txSuccess2.hashMalFix, 1) assert_equal(tx_info['vout'][0]['token'], bytes_to_hex_str(colorId_nonreissuable)) assert_equal(tx_info['vout'][0]['value'], 100) #TxSuccess3 - coinbaseTx2 - issue 1 NFT (UTXO-4) colorId_nft = colorIdNFT(COutPoint(coinbase_txs[1].malfixsha256, 0).serialize()) script_nft = CP2PHK_script(colorId = colorId_nft, pubkey = self.pubkeys[0]) script_transfer_nft = CP2PHK_script(colorId = colorId_nft, pubkey = self.pubkeys[0]) txSuccess3 = CTransaction() txSuccess3.vin.append(CTxIn(COutPoint(coinbase_txs[1].malfixsha256, 0), b"")) txSuccess3.vout.append(CTxOut(1, script_nft)) sig_hash, err = SignatureHash(coinbase_txs[1].vout[0].scriptPubKey, txSuccess3, 0, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' txSuccess3.vin[0].scriptSig = CScript([signature]) txSuccess3.rehash() test_transaction_acceptance(node, txSuccess3, accepted=True) tx_info = node.getrawtransaction(txSuccess3.hashMalFix, 1) assert_equal(tx_info['vout'][0]['token'], bytes_to_hex_str(colorId_nft)) assert_equal(tx_info['vout'][0]['value'], 1) #TxFailure4 - (UTXO-1) - split REISSUABLE - 25 + 75 (UTXO-5,6) # - (UTXO-3) - split NON-REISSUABLE - 40 + 60 (UTXO-7,8) # - coinbaseTx3 - issue 100 REISSUABLE (UTXO-9) TxFailure4 = CTransaction() TxFailure4.vin.append(CTxIn(COutPoint(txSuccess1.malfixsha256, 0), b"")) TxFailure4.vin.append(CTxIn(COutPoint(txSuccess2.malfixsha256, 0), b"")) TxFailure4.vin.append(CTxIn(COutPoint(coinbase_txs[2].malfixsha256, 0), b"")) TxFailure4.vout.append(CTxOut(25, script_reissuable)) TxFailure4.vout.append(CTxOut(75, script_reissuable)) TxFailure4.vout.append(CTxOut(40, script_nonreissuable)) TxFailure4.vout.append(CTxOut(60, script_nonreissuable)) TxFailure4.vout.append(CTxOut(100, script_reissuable)) sig_hash, err = SignatureHash(txSuccess1.vout[0].scriptPubKey, TxFailure4, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure4.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess2.vout[0].scriptPubKey, TxFailure4, 1, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure4.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(coinbase_txs[2].vout[0].scriptPubKey, TxFailure4, 2, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' TxFailure4.vin[2].scriptSig = CScript([signature]) TxFailure4.rehash() test_transaction_acceptance(node, TxFailure4, accepted=False, reason=b"bad-txns-token-balance") #TxSuccess4 - (UTXO-1) - split REISSUABLE - 25 + 75 (UTXO-5,6) # - (UTXO-3) - split NON-REISSUABLE - 40 + 60 (UTXO-7,8) txSuccess4 = CTransaction() txSuccess4.vin.append(CTxIn(COutPoint(txSuccess1.malfixsha256, 0), b"")) txSuccess4.vin.append(CTxIn(COutPoint(txSuccess2.malfixsha256, 0), b"")) txSuccess4.vin.append(CTxIn(COutPoint(coinbase_txs[2].malfixsha256, 0), b"")) txSuccess4.vout.append(CTxOut(25, script_reissuable)) txSuccess4.vout.append(CTxOut(75, script_reissuable)) txSuccess4.vout.append(CTxOut(40, script_nonreissuable)) txSuccess4.vout.append(CTxOut(60, script_nonreissuable)) sig_hash, err = SignatureHash(txSuccess1.vout[0].scriptPubKey, txSuccess4, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess4.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess2.vout[0].scriptPubKey, txSuccess4, 1, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess4.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(coinbase_txs[2].vout[0].scriptPubKey, txSuccess4, 2, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' txSuccess4.vin[2].scriptSig = CScript([signature]) txSuccess4.rehash() test_transaction_acceptance(node, txSuccess4, accepted=True) #TxFailure5 - (UTXO-6) - split REISSUABLE(75) (UTXO-10,11) # - (UTXO-7) - split NON-REISSUABLE(40) (UTXO-12) # - (UTXO-4) - split NFT (UTXO-13) # - coinbaseTx4 TxFailure5 = CTransaction() TxFailure5.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 1), b"")) TxFailure5.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 2), b"")) TxFailure5.vin.append(CTxIn(COutPoint(txSuccess3.malfixsha256, 0), b"")) TxFailure5.vin.append(CTxIn(COutPoint(coinbase_txs[3].malfixsha256, 0), b"")) TxFailure5.vout.append(CTxOut(35, script_reissuable)) TxFailure5.vout.append(CTxOut(40, script_reissuable)) TxFailure5.vout.append(CTxOut(20, script_nonreissuable)) TxFailure5.vout.append(CTxOut(20, script_nonreissuable)) TxFailure5.vout.append(CTxOut(1, script_nft)) TxFailure5.vout.append(CTxOut(1, script_nft)) sig_hash, err = SignatureHash(txSuccess4.vout[1].scriptPubKey, TxFailure5, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure5.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess4.vout[2].scriptPubKey, TxFailure5, 1, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure5.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess3.vout[0].scriptPubKey, TxFailure5, 2, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure5.vin[2].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(coinbase_txs[3].vout[0].scriptPubKey, TxFailure5, 3, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' TxFailure5.vin[3].scriptSig = CScript([signature]) TxFailure5.rehash() test_transaction_acceptance(node, TxFailure5, accepted=False, reason=b"bad-txns-token-balance") #txSuccess5 - (UTXO-6) - split REISSUABLE(75) (UTXO-10,11) # - (UTXO-7) - split NON-REISSUABLE(40) (UTXO-12) # - (UTXO-4) - transfer NFT (UTXO-13) # - coinbaseTx4 txSuccess5 = CTransaction() txSuccess5.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 1), b"")) txSuccess5.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 2), b"")) txSuccess5.vin.append(CTxIn(COutPoint(txSuccess3.malfixsha256, 0), b"")) txSuccess5.vin.append(CTxIn(COutPoint(coinbase_txs[3].malfixsha256, 0), b"")) txSuccess5.vout.append(CTxOut(35, script_reissuable)) txSuccess5.vout.append(CTxOut(40, script_reissuable)) txSuccess5.vout.append(CTxOut(20, script_nonreissuable)) txSuccess5.vout.append(CTxOut(20, script_nonreissuable)) txSuccess5.vout.append(CTxOut(1, script_nft)) sig_hash, err = SignatureHash(txSuccess4.vout[1].scriptPubKey, txSuccess5, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess5.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess4.vout[2].scriptPubKey, txSuccess5, 1, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess5.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess3.vout[0].scriptPubKey, txSuccess5, 2, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess5.vin[2].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(coinbase_txs[3].vout[0].scriptPubKey, txSuccess5, 3, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' txSuccess5.vin[3].scriptSig = CScript([signature]) txSuccess5.rehash() test_transaction_acceptance(node, txSuccess5, accepted=True) #TxFailure6 - (UTXO-11) - transfer REISSUABLE(40) (UTXO-14) # - (UTXO-8) - burn NON-REISSUABLE(60) (UTXO-15)* # - (UTXO-13) - transfer NFT (UTXO-16) # - coinbaseTx5 - issue 1000 REISSUABLE1, change (UTXO-17) colorId_reissuable1 = colorIdReissuable(coinbase_txs[6].vout[0].scriptPubKey) script_reissuable1 = CP2PHK_script(colorId = colorId_reissuable, pubkey = self.pubkeys[0]) TxFailure6 = CTransaction() TxFailure6.vin.append(CTxIn(COutPoint(txSuccess5.malfixsha256, 1), b"")) TxFailure6.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 3), b"")) TxFailure6.vin.append(CTxIn(COutPoint(txSuccess5.malfixsha256, 4), b"")) TxFailure6.vin.append(CTxIn(COutPoint(coinbase_txs[4].malfixsha256, 0), b"")) TxFailure6.vout.append(CTxOut(40, script_transfer_reissuable)) TxFailure6.vout.append(CTxOut(30, script_transfer_nonreissuable)) TxFailure6.vout.append(CTxOut(1, script_transfer_nft)) TxFailure6.vout.append(CTxOut(1000, script_reissuable1)) TxFailure6.vout.append(CTxOut(1*COIN, change_script)) sig_hash, err = SignatureHash(txSuccess5.vout[1].scriptPubKey, TxFailure6, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure6.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess4.vout[3].scriptPubKey, TxFailure6, 1, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure6.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess5.vout[4].scriptPubKey, TxFailure6, 2, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure6.vin[2].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(coinbase_txs[4].vout[0].scriptPubKey, TxFailure6, 3, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' TxFailure6.vin[3].scriptSig = CScript([signature]) TxFailure6.rehash() test_transaction_acceptance(node, TxFailure6, accepted=False, reason=b"bad-txns-token-balance") #TxSuccess6 - (UTXO-11) - transfer REISSUABLE(40) (UTXO-14) # - (UTXO-8) - burn NON-REISSUABLE(60) (UTXO-15)* # - (UTXO-13) - transfer NFT (UTXO-16) # - coinbaseTx5 - change txSuccess6 = CTransaction() txSuccess6.vin.append(CTxIn(COutPoint(txSuccess5.malfixsha256, 1), b"")) txSuccess6.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 3), b"")) txSuccess6.vin.append(CTxIn(COutPoint(txSuccess5.malfixsha256, 4), b"")) txSuccess6.vin.append(CTxIn(COutPoint(coinbase_txs[4].malfixsha256, 0), b"")) txSuccess6.vout.append(CTxOut(40, script_transfer_reissuable)) txSuccess6.vout.append(CTxOut(30, script_transfer_nonreissuable)) txSuccess6.vout.append(CTxOut(1, script_transfer_nft)) txSuccess6.vout.append(CTxOut(1*COIN, change_script)) sig_hash, err = SignatureHash(txSuccess5.vout[1].scriptPubKey, txSuccess6, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess6.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess4.vout[3].scriptPubKey, txSuccess6, 1, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess6.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess5.vout[4].scriptPubKey, txSuccess6, 2, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess6.vin[2].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(coinbase_txs[4].vout[0].scriptPubKey, txSuccess6, 3, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' txSuccess6.vin[3].scriptSig = CScript([signature]) txSuccess6.rehash() test_transaction_acceptance(node, txSuccess6, accepted=True) #TxSuccess7 - coinbaseTx5 - issue 1000 REISSUABLE1, change (UTXO-17) txSuccess7 = CTransaction() txSuccess7.vin.append(CTxIn(COutPoint(coinbase_txs[5].malfixsha256, 0), b"")) txSuccess7.vout.append(CTxOut(1000, script_reissuable1)) sig_hash, err = SignatureHash(coinbase_txs[5].vout[0].scriptPubKey, txSuccess7, 0, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' txSuccess7.vin[0].scriptSig = CScript([signature]) txSuccess7.rehash() test_transaction_acceptance(node, txSuccess7, accepted=True) #TxFailure7 - (UTXO-9,14) - aggregate REISSUABLE(25 + 40) x # - (UTXO-12) - burn NON-REISSUABLE(20) * TxFailure7 = CTransaction() TxFailure7.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 0), b"")) TxFailure7.vin.append(CTxIn(COutPoint(txSuccess6.malfixsha256, 0), b"")) TxFailure7.vin.append(CTxIn(COutPoint(txSuccess5.malfixsha256, 2), b"")) TxFailure7.vout.append(CTxOut(65, script_transfer_reissuable)) sig_hash, err = SignatureHash(txSuccess4.vout[0].scriptPubKey, TxFailure7, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure7.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess6.vout[0].scriptPubKey, TxFailure7, 1, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure7.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess5.vout[2].scriptPubKey, TxFailure7, 2, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' TxFailure7.vin[2].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) TxFailure7.rehash() test_transaction_acceptance(node, TxFailure7, accepted=False, reason=b'min relay fee not met') #txSuccess8 - (UTXO-9,14) - aggregate REISSUABLE(25 + 40) x # - (UTXO-12) - burn NON-REISSUABLE(20) * # - coinbase[6] txSuccess8 = CTransaction() txSuccess8.vin.append(CTxIn(COutPoint(txSuccess4.malfixsha256, 0), b"")) txSuccess8.vin.append(CTxIn(COutPoint(txSuccess6.malfixsha256, 0), b"")) txSuccess8.vin.append(CTxIn(COutPoint(txSuccess5.malfixsha256, 2), b"")) txSuccess8.vin.append(CTxIn(COutPoint(coinbase_txs[6].malfixsha256, 0), b"")) txSuccess8.vout.append(CTxOut(65, script_transfer_reissuable)) sig_hash, err = SignatureHash(txSuccess4.vout[0].scriptPubKey, txSuccess8, 0, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess8.vin[0].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(txSuccess6.vout[0].scriptPubKey, txSuccess8, 1, SIGHASH_ALL) signature = self.privkeys[1].sign(sig_hash) + b'\x01' txSuccess8.vin[1].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[1])]) sig_hash, err = SignatureHash(txSuccess5.vout[2].scriptPubKey, txSuccess8, 2, SIGHASH_ALL) signature = self.privkeys[0].sign(sig_hash) + b'\x01' txSuccess8.vin[2].scriptSig = CScript([signature, hex_str_to_bytes(self.pubkeys[0])]) sig_hash, err = SignatureHash(coinbase_txs[6].vout[0].scriptPubKey, txSuccess8, 3, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' txSuccess8.vin[3].scriptSig = CScript([signature]) txSuccess8.rehash() test_transaction_acceptance(node, txSuccess8, accepted=True) #TxFailure8 - (UTXO-17) - convert REISSUABLE to NON-REISSUABLE TxFailure8 = CTransaction() TxFailure8.vin.append(CTxIn(COutPoint(txSuccess7.malfixsha256, 0), b"")) TxFailure8.vout.append(CTxOut(60, script_transfer_nonreissuable)) sig_hash, err = SignatureHash(txSuccess7.vout[0].scriptPubKey, TxFailure8, 0, SIGHASH_ALL) signature = self.coinbase_key.sign(sig_hash) + b'\x01' TxFailure8.vin[0].scriptSig = CScript([signature]) TxFailure8.rehash() test_transaction_acceptance(node, TxFailure8, accepted=False, reason=b'invalid-colorid')
def run_test(self): self.nodes[0].add_p2p_connection(P2PDataStore()) self.nodeaddress = self.nodes[0].getnewaddress() self.pubkey = self.nodes[0].getaddressinfo(self.nodeaddress)["pubkey"] self.log.info("Mining %d blocks", CHAIN_HEIGHT) self.coinbase_txids = [ self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate( CHAIN_HEIGHT, self.signblockprivkeys) ] ## P2PKH transaction ######################## self.log.info("Test using a P2PKH transaction") spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], self.nodeaddress, amount=10) spendtx.rehash() copy_spendTx = CTransaction(spendtx) #cache hashes hash = spendtx.hash hashMalFix = spendtx.hashMalFix #malleate unDERify(spendtx) spendtx.rehash() # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_not_equal(hash, spendtx.hash) assert_equal(hashMalFix, spendtx.hashMalFix) # verify that hash is spendtx.serialize() hash = encode(hash256(spendtx.serialize())[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature P2PKH transaction tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 1), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) # serialize with and without witness block remains the same assert_equal(block.serialize(with_witness=True), block.serialize()) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False, with_scriptsig=True)) self.log.info("Reject block with non-DER signature") self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_INVALID) assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') self.log.info("Accept block with DER signature") #recreate block with DER sig transaction block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 1), block_time) block.vtx.append(copy_spendTx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## P2SH transaction ######################## self.log.info("Test using P2SH transaction ") REDEEM_SCRIPT_1 = CScript([OP_1, OP_DROP]) P2SH_1 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_1), OP_EQUAL]) tx = CTransaction() tx.vin.append( CTxIn(COutPoint(int(self.coinbase_txids[1], 16), 0), b"", 0xffffffff)) tx.vout.append(CTxOut(10, P2SH_1)) tx.rehash() spendtx_raw = self.nodes[0].signrawtransactionwithwallet( ToHex(tx), [], "ALL", self.options.scheme)["hex"] spendtx = FromHex(spendtx, spendtx_raw) spendtx.rehash() copy_spendTx = CTransaction(spendtx) #cache hashes hash = spendtx.hash hashMalFix = spendtx.hashMalFix #malleate spendtxcopy = spendtx unDERify(spendtxcopy) spendtxcopy.rehash() # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_not_equal(hash, spendtxcopy.hash) assert_equal(hashMalFix, spendtxcopy.hashMalFix) # verify that hash is spendtx.serialize() hash = encode( hash256(spendtx.serialize(with_witness=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_witness=False, with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature P2SH transaction tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 2), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) # serialize with and without witness block remains the same assert_equal(block.serialize(with_witness=True), block.serialize()) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=True, with_scriptsig=True)) self.log.info("Reject block with non-DER signature") self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_INVALID) assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') self.log.info("Accept block with DER signature") #recreate block with DER sig transaction block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 2), block_time) block.vtx.append(copy_spendTx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## redeem previous P2SH ######################### self.log.info("Test using P2SH redeem transaction ") tx = CTransaction() tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) tx.vin.append(CTxIn(COutPoint(block.vtx[1].malfixsha256, 0), b'')) (sighash, err) = SignatureHash(REDEEM_SCRIPT_1, tx, 1, SIGHASH_ALL) signKey = CECKey() signKey.set_secretbytes(b"horsebattery") sig = signKey.sign(sighash) + bytes(bytearray([SIGHASH_ALL])) scriptSig = CScript([sig, REDEEM_SCRIPT_1]) tx.vin[0].scriptSig = scriptSig tx.rehash() spendtx_raw = self.nodes[0].signrawtransactionwithwallet( ToHex(tx), [], "ALL", self.options.scheme)["hex"] spendtx = FromHex(spendtx, spendtx_raw) spendtx.rehash() #cache hashes hash = spendtx.hash hashMalFix = spendtx.hashMalFix #malleate spendtxcopy = spendtx unDERify(spendtxcopy) spendtxcopy.rehash() # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_not_equal(hash, spendtxcopy.hash) assert_equal(hashMalFix, spendtxcopy.hashMalFix) # verify that hash is spendtx.serialize() hash = encode( hash256(spendtx.serialize(with_witness=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_witness=False, with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature P2SH redeem transaction tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 3), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) # serialize with and without witness block remains the same assert_equal(block.serialize(with_witness=True), block.serialize()) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=True, with_scriptsig=True)) self.log.info("Accept block with P2SH redeem transaction") self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## p2sh_p2wpkh transaction ############################## self.log.info("Test using p2sh_p2wpkh transaction ") spendtxStr = create_witness_tx(self.nodes[0], True, getInput(self.coinbase_txids[4]), self.pubkey, amount=1.0) #get CTRansaction object from above hex spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() #cache hashes spendtx.rehash() hash = spendtx.hash hashMalFix = spendtx.hashMalFix withash = spendtx.calc_sha256(True) # malleate unDERify(spendtx) spendtx.rehash() withash2 = spendtx.calc_sha256(True) # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_equal(withash, withash2) assert_equal(hash, spendtx.hash) assert_equal(hashMalFix, spendtx.hashMalFix) # verify that hash is spendtx.serialize() hash = encode(hash256(spendtx.serialize())[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature p2sh_p2wpkh transaction spendtxStr = self.nodes[0].signrawtransactionwithwallet( spendtxStr, [], "ALL", self.options.scheme) assert ("errors" not in spendtxStr or len(["errors"]) == 0) spendtxStr = spendtxStr["hex"] spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 4), block_time) block.vtx.append(spendtx) add_witness_commitment(block) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) # serialize with and without witness assert_equal(block.serialize(with_witness=False), block.serialize()) assert_not_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_not_equal( block.serialize(with_witness=True), block.serialize(with_witness=False, with_scriptsig=True)) self.log.info( "Reject block with p2sh_p2wpkh transaction and witness commitment") assert_raises_rpc_error( -22, "Block does not start with a coinbase", self.nodes[0].submitblock, bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), tip) block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 4), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) self.log.info("Accept block with p2sh_p2wpkh transaction") self.nodes[0].submitblock( bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## p2sh_p2wsh transaction ############################## self.log.info("Test using p2sh_p2wsh transaction") spendtxStr = create_witness_tx(self.nodes[0], True, getInput(self.coinbase_txids[5]), self.pubkey, amount=1.0) #get CTRansaction object from above hex spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() #cache hashes spendtx.rehash() hash = spendtx.hash hashMalFix = spendtx.hashMalFix withash = spendtx.calc_sha256(True) # malleate unDERify(spendtx) spendtx.rehash() withash2 = spendtx.calc_sha256(True) # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_equal(withash, withash2) assert_equal(hash, spendtx.hash) assert_equal(hashMalFix, spendtx.hashMalFix) # verify that hash is spendtx.serialize() hash = encode(hash256(spendtx.serialize())[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature p2sh_p2wsh transaction spendtxStr = self.nodes[0].signrawtransactionwithwallet( spendtxStr, [], "ALL", self.options.scheme) assert ("errors" not in spendtxStr or len(["errors"]) == 0) spendtxStr = spendtxStr["hex"] spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 5), block_time) block.vtx.append(spendtx) add_witness_commitment(block) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) # serialize with and without witness assert_equal(block.serialize(with_witness=False), block.serialize()) assert_not_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_not_equal( block.serialize(with_witness=True), block.serialize(with_witness=False, with_scriptsig=True)) self.log.info( "Reject block with p2sh_p2wsh transaction and witness commitment") assert_raises_rpc_error( -22, "Block does not start with a coinbase", self.nodes[0].submitblock, bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), tip) block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 5), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkeys) self.log.info("Accept block with p2sh_p2wsh transaction") self.nodes[0].submitblock( bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), block.hash)
def run_test(self): self.import_deterministic_coinbase_privkeys( ) # Create wallets for all nodes parent = self.nodes[0] #parent2 = self.nodes[1] sidechain = self.nodes[2] sidechain2 = self.nodes[3] # If we're testing post-transition, force a fedpegscript transition and # getting rid of old fedpegscript by making at least another epoch pass by WSH_OP_TRUE = self.nodes[0].decodescript("51")["segwit"]["hex"] # We just randomize the keys a bit to get another valid fedpegscript new_fedpegscript = sidechain.tweakfedpegscript("f00dbabe")["script"] if self.options.post_transition: print("Running test post-transition") for _ in range(30): block_hex = sidechain.getnewblockhex( 0, { "signblockscript": WSH_OP_TRUE, "max_block_witness": 10, "fedpegscript": new_fedpegscript, "extension_space": [] }) sidechain.submitblock(block_hex) assert_equal(sidechain.getsidechaininfo()["current_fedpegscripts"], [new_fedpegscript] * 2) if self.options.pre_transition: print( "Running test pre-transition, dynafed activated from first block" ) for node in self.nodes: node.importprivkey(privkey=node.get_deterministic_priv_key().key, label="mining") util.node_fastmerkle = sidechain parent.generate(101) sidechain.generate(101) self.log.info("sidechain info: {}".format( sidechain.getsidechaininfo())) addrs = sidechain.getpeginaddress() addr = addrs["mainchain_address"] assert_equal( sidechain.decodescript(addrs["claim_script"])["type"], "witness_v0_keyhash") txid1 = parent.sendtoaddress(addr, 24) vout = find_vout_for_address(parent, txid1, addr) # 10+2 confirms required to get into mempool and confirm assert_equal(sidechain.getsidechaininfo()["pegin_confirmation_depth"], 10) parent.generate(1) time.sleep(2) proof = parent.gettxoutproof([txid1]) raw = parent.gettransaction(txid1)["hex"] # Create a wallet in order to test that multi-wallet support works correctly for claimpegin # (Regression test for https://github.com/ElementsProject/elements/issues/812 .) sidechain.createwallet("throwaway") # Set up our sidechain RPCs to use the first wallet (with empty name). We do this by # overriding the RPC object in a hacky way, to avoid breaking a different hack on TestNode # that enables generate() to work despite the deprecation of the generate RPC. sidechain.rpc = sidechain.get_wallet_rpc("") print("Attempting peg-ins") # First attempt fails the consensus check but gives useful result try: pegtxid = sidechain.claimpegin(raw, proof) raise Exception( "Peg-in should not be mature enough yet, need another block.") except JSONRPCException as e: assert "Peg-in Bitcoin transaction needs more confirmations to be sent." in e.error[ "message"] # Second attempt simply doesn't hit mempool bar parent.generate(10) try: pegtxid = sidechain.claimpegin(raw, proof) raise Exception( "Peg-in should not be mature enough yet, need another block.") except JSONRPCException as e: assert "Peg-in Bitcoin transaction needs more confirmations to be sent." in e.error[ "message"] try: pegtxid = sidechain.createrawpegin(raw, proof, 'AEIOU') raise Exception("Peg-in with non-hex claim_script should fail.") except JSONRPCException as e: assert "Given claim_script is not hex." in e.error["message"] # Should fail due to non-matching wallet address try: scriptpubkey = sidechain.getaddressinfo( get_new_unconfidential_address(sidechain))["scriptPubKey"] pegtxid = sidechain.claimpegin(raw, proof, scriptpubkey) raise Exception( "Peg-in with non-matching claim_script should fail.") except JSONRPCException as e: assert "Given claim_script does not match the given Bitcoin transaction." in e.error[ "message"] # 12 confirms allows in mempool parent.generate(1) # Make sure that a tx with a duplicate pegin claim input gets rejected. raw_pegin = sidechain.createrawpegin(raw, proof)["hex"] raw_pegin = FromHex(CTransaction(), raw_pegin) raw_pegin.vin.append(raw_pegin.vin[0]) # duplicate the pegin input raw_pegin = sidechain.signrawtransactionwithwallet( raw_pegin.serialize().hex())["hex"] assert_raises_rpc_error(-26, "bad-txns-inputs-duplicate", sidechain.sendrawtransaction, raw_pegin) # Also try including this tx in a block manually and submitting it. doublespendblock = FromHex(CBlock(), sidechain.getnewblockhex()) doublespendblock.vtx.append(FromHex(CTransaction(), raw_pegin)) doublespendblock.hashMerkleRoot = doublespendblock.calc_merkle_root() add_witness_commitment(doublespendblock) doublespendblock.solve() block_hex = doublespendblock.serialize(True).hex() assert_raises_rpc_error(-25, "bad-txns-inputs-duplicate", sidechain.testproposedblock, block_hex, True) # Should succeed via wallet lookup for address match, and when given raw_pegin = sidechain.createrawpegin(raw, proof)['hex'] signed_pegin = sidechain.signrawtransactionwithwallet(raw_pegin) # Find the address that the peg-in used outputs = [] for pegin_vout in sidechain.decoderawtransaction(raw_pegin)['vout']: if pegin_vout['scriptPubKey']['type'] == 'witness_v0_keyhash': outputs.append({ pegin_vout['scriptPubKey']['addresses'][0]: pegin_vout['value'] }) elif pegin_vout['scriptPubKey']['type'] == 'fee': outputs.append({"fee": pegin_vout['value']}) # Check the createrawtransaction makes the same unsigned peg-in transaction raw_pegin2 = sidechain.createrawtransaction( [{ "txid": txid1, "vout": vout, "pegin_bitcoin_tx": raw, "pegin_txout_proof": proof, "pegin_claim_script": addrs["claim_script"] }], outputs) assert_equal(raw_pegin, raw_pegin2) # Check that createpsbt makes the correct unsigned peg-in pegin_psbt = sidechain.createpsbt( [{ "txid": txid1, "vout": vout, "pegin_bitcoin_tx": raw, "pegin_txout_proof": proof, "pegin_claim_script": addrs["claim_script"] }], outputs) decoded_psbt = sidechain.decodepsbt(pegin_psbt) # Check that pegin_bitcoin_tx == raw, but due to stripping witnesses, we need to compare their txids txid1 = parent.decoderawtransaction( decoded_psbt['inputs'][0]['pegin_bitcoin_tx'])['txid'] txid2 = parent.decoderawtransaction(raw)['txid'] assert_equal(txid1, txid2) # Check the rest assert_equal(decoded_psbt['inputs'][0]['pegin_claim_script'], addrs["claim_script"]) assert_equal(decoded_psbt['inputs'][0]['pegin_txout_proof'], proof) assert_equal(decoded_psbt['inputs'][0]['pegin_genesis_hash'], parent.getblockhash(0)) # Make a psbt without those peg-in data and merge them merge_pegin_psbt = sidechain.createpsbt([{ "txid": txid1, "vout": vout }], outputs) decoded_psbt = sidechain.decodepsbt(merge_pegin_psbt) assert 'pegin_bitcoin_tx' not in decoded_psbt['inputs'][0] assert 'pegin_claim_script' not in decoded_psbt['inputs'][0] assert 'pegin_txout_proof' not in decoded_psbt['inputs'][0] assert 'pegin_genesis_hash' not in decoded_psbt['inputs'][0] merged_pegin_psbt = sidechain.combinepsbt( [pegin_psbt, merge_pegin_psbt]) assert_equal(pegin_psbt, merged_pegin_psbt) # Now sign the psbt signed_psbt = sidechain.walletsignpsbt(pegin_psbt) # Finalize and extract and compare fin_psbt = sidechain.finalizepsbt(signed_psbt['psbt']) assert_equal(fin_psbt, signed_pegin) # Try funding a psbt with the peg-in assert_equal(sidechain.getbalance()['bitcoin'], 50) out_bal = 0 outputs.append({sidechain.getnewaddress(): 49.999}) for out in outputs: for val in out.values(): out_bal += Decimal(val) assert_greater_than(out_bal, 50) pegin_psbt = sidechain.walletcreatefundedpsbt( [{ "txid": txid1, "vout": vout, "pegin_bitcoin_tx": raw, "pegin_txout_proof": proof, "pegin_claim_script": addrs["claim_script"] }], outputs, 0, {'add_inputs': True}) signed_psbt = sidechain.walletsignpsbt(pegin_psbt['psbt']) fin_psbt = sidechain.finalizepsbt(signed_psbt['psbt']) assert fin_psbt['complete'] sample_pegin_struct = FromHex(CTransaction(), signed_pegin["hex"]) # Round-trip peg-in transaction using python serialization assert_equal(signed_pegin["hex"], sample_pegin_struct.serialize().hex()) # Store this for later (evil laugh) sample_pegin_witness = sample_pegin_struct.wit.vtxinwit[0].peginWitness pegtxid1 = sidechain.claimpegin(raw, proof) # Make sure a second pegin claim does not get accepted in the mempool when # another mempool tx already claims that pegin. assert_raises_rpc_error(-4, "txn-mempool-conflict", sidechain.claimpegin, raw, proof) # Will invalidate the block that confirms this transaction later for node_group in self.node_groups: self.sync_all(node_group) blockhash = sidechain2.generate(1) for node_group in self.node_groups: self.sync_all(node_group) sidechain.generate(5) tx1 = sidechain.gettransaction(pegtxid1) if "confirmations" in tx1 and tx1["confirmations"] == 6: print("Peg-in is confirmed: Success!") else: raise Exception("Peg-in confirmation has failed.") # Look at pegin fields decoded = sidechain.decoderawtransaction(tx1["hex"]) assert decoded["vin"][0]["is_pegin"] == True assert len(decoded["vin"][0]["pegin_witness"]) > 0 # Check that there's sufficient fee for the peg-in vsize = decoded["vsize"] fee_output = decoded["vout"][1] fallbackfee_pervbyte = Decimal("0.00001") / Decimal("1000") assert fee_output["scriptPubKey"]["type"] == "fee" assert fee_output["value"] >= fallbackfee_pervbyte * vsize # Quick reorg checks of pegs sidechain.invalidateblock(blockhash[0]) if sidechain.gettransaction(pegtxid1)["confirmations"] != 0: raise Exception( "Peg-in didn't unconfirm after invalidateblock call.") # Re-org causes peg-ins to get booted(wallet will resubmit in 10 minutes) assert_equal(sidechain.getrawmempool(), []) sidechain.sendrawtransaction(tx1["hex"]) # Create duplicate claim, put it in block along with current one in mempool # to test duplicate-in-block claims between two txs that are in the same block. raw_pegin = sidechain.createrawpegin(raw, proof)["hex"] raw_pegin = sidechain.signrawtransactionwithwallet(raw_pegin)["hex"] raw_pegin = FromHex(CTransaction(), raw_pegin) doublespendblock = FromHex(CBlock(), sidechain.getnewblockhex()) assert len(doublespendblock.vtx) == 2 # coinbase and pegin doublespendblock.vtx.append(raw_pegin) doublespendblock.hashMerkleRoot = doublespendblock.calc_merkle_root() add_witness_commitment(doublespendblock) doublespendblock.solve() block_hex = doublespendblock.serialize(True).hex() assert_raises_rpc_error(-25, "bad-txns-double-pegin", sidechain.testproposedblock, block_hex, True) # Re-enters block sidechain.generate(1) if sidechain.gettransaction(pegtxid1)["confirmations"] != 1: raise Exception("Peg-in should have one confirm on side block.") sidechain.reconsiderblock(blockhash[0]) if sidechain.gettransaction(pegtxid1)["confirmations"] != 6: raise Exception("Peg-in should be back to 6 confirms.") # Now the pegin is already claimed in a confirmed tx. # In that case, a duplicate claim should (1) not be accepted in the mempool # and (2) not be accepted in a block. assert_raises_rpc_error(-4, "pegin-already-claimed", sidechain.claimpegin, raw, proof) # For case (2), manually craft a block and include the tx. doublespendblock = FromHex(CBlock(), sidechain.getnewblockhex()) doublespendblock.vtx.append(raw_pegin) doublespendblock.hashMerkleRoot = doublespendblock.calc_merkle_root() add_witness_commitment(doublespendblock) doublespendblock.solve() block_hex = doublespendblock.serialize(True).hex() assert_raises_rpc_error(-25, "bad-txns-double-pegin", sidechain.testproposedblock, block_hex, True) # Do multiple claims in mempool n_claims = 6 print("Flooding mempool with a few claims") pegtxs = [] sidechain.generate(101) # Do mixture of raw peg-in and automatic peg-in tx construction # where raw creation is done on another node for i in range(n_claims): addrs = sidechain.getpeginaddress() txid = parent.sendtoaddress(addrs["mainchain_address"], 1) parent.generate(1) proof = parent.gettxoutproof([txid]) raw = parent.gettransaction(txid)["hex"] if i % 2 == 0: parent.generate(11) pegtxs += [sidechain.claimpegin(raw, proof)] else: # The raw API doesn't check for the additional 2 confirmation buffer # So we only get 10 confirms then send off. Miners will add to block anyways. # Don't mature whole way yet to test signing immature peg-in input parent.generate(8) # Wallet in sidechain2 gets funds instead of sidechain raw_pegin = sidechain2.createrawpegin( raw, proof, addrs["claim_script"])["hex"] # First node should also be able to make a valid transaction with or without 3rd arg # since this wallet originated the claim_script itself sidechain.createrawpegin(raw, proof, addrs["claim_script"]) sidechain.createrawpegin(raw, proof) signed_pegin = sidechain.signrawtransactionwithwallet( raw_pegin) assert signed_pegin["complete"] assert "warning" in signed_pegin # warning for immature peg-in # fully mature them now parent.generate(1) pegtxs += [sidechain.sendrawtransaction(signed_pegin["hex"])] for node_group in self.node_groups: self.sync_all(node_group) sidechain2.generate(1) for i, pegtxid in enumerate(pegtxs): if i % 2 == 0: tx = sidechain.gettransaction(pegtxid) else: tx = sidechain2.gettransaction(pegtxid) if "confirmations" not in tx or tx["confirmations"] == 0: raise Exception("Peg-in confirmation has failed.") print("Test pegouts") self.test_pegout(get_new_unconfidential_address(parent, "legacy"), sidechain) self.test_pegout(get_new_unconfidential_address(parent, "p2sh-segwit"), sidechain) self.test_pegout(get_new_unconfidential_address(parent, "bech32"), sidechain) print("Test pegout P2SH") parent_chain_addr = get_new_unconfidential_address(parent) parent_pubkey = parent.getaddressinfo(parent_chain_addr)["pubkey"] parent_chain_p2sh_addr = parent.createmultisig( 1, [parent_pubkey])["address"] self.test_pegout(parent_chain_p2sh_addr, sidechain) print("Test pegout Garbage") parent_chain_addr = "garbage" try: self.test_pegout(parent_chain_addr, sidechain) raise Exception("A garbage address should fail.") except JSONRPCException as e: assert "Invalid Bitcoin address" in e.error["message"] print("Test pegout Garbage valid") prev_txid = sidechain.sendtoaddress(sidechain.getnewaddress(), 1) sidechain.generate(1) pegout_chain = 'a' * 64 pegout_hex = 'b' * 500 inputs = [{"txid": prev_txid, "vout": 0}] outputs = {"vdata": [pegout_chain, pegout_hex]} rawtx = sidechain.createrawtransaction(inputs, outputs) raw_pegout = sidechain.decoderawtransaction(rawtx) assert 'vout' in raw_pegout and len(raw_pegout['vout']) > 0 pegout_tested = False for output in raw_pegout['vout']: scriptPubKey = output['scriptPubKey'] if 'type' in scriptPubKey and scriptPubKey['type'] == 'nulldata': assert 'pegout_hex' in scriptPubKey and 'pegout_asm' in scriptPubKey and 'pegout_type' in scriptPubKey assert 'pegout_chain' in scriptPubKey and 'pegout_reqSigs' not in scriptPubKey and 'pegout_addresses' not in scriptPubKey assert scriptPubKey['pegout_type'] == 'nonstandard' assert scriptPubKey['pegout_chain'] == pegout_chain assert scriptPubKey['pegout_hex'] == pegout_hex pegout_tested = True break assert pegout_tested print( "Now test failure to validate peg-ins based on intermittent bitcoind rpc failure" ) self.stop_node(1) txid = parent.sendtoaddress(addr, 1) parent.generate(12) proof = parent.gettxoutproof([txid]) raw = parent.gettransaction(txid)["hex"] sidechain.claimpegin(raw, proof) # stuck peg sidechain.generate(1) print("Waiting to ensure block is being rejected by sidechain2") time.sleep(5) assert sidechain.getblockcount() != sidechain2.getblockcount() print("Restarting parent2") self.start_node(1) self.connect_nodes(0, 1) # Don't make a block, race condition when pegin-invalid block # is awaiting further validation, nodes reject subsequent blocks # even ones they create print( "Now waiting for node to re-evaluate peg-in witness failed block... should take a few seconds" ) for node_group in self.node_groups: self.sync_all(node_group) print("Completed!\n") print("Now send funds out in two stages, partial, and full") some_btc_addr = get_new_unconfidential_address(parent) bal_1 = sidechain.getwalletinfo()["balance"]['bitcoin'] try: sidechain.sendtomainchain(some_btc_addr, bal_1 + 1) raise Exception("Sending out too much; should have failed") except JSONRPCException as e: assert "Insufficient funds" in e.error["message"] assert sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1 try: sidechain.sendtomainchain(some_btc_addr + "b", bal_1 - 1) raise Exception("Sending to invalid address; should have failed") except JSONRPCException as e: assert "Invalid Bitcoin address" in e.error["message"] assert sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1 try: sidechain.sendtomainchain("1Nro9WkpaKm9axmcfPVp79dAJU1Gx7VmMZ", bal_1 - 1) raise Exception( "Sending to mainchain address when should have been testnet; should have failed" ) except JSONRPCException as e: assert "Invalid Bitcoin address" in e.error["message"] assert sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1 # Test superfluous peg-in witness data on regular spend before we have no funds raw_spend = sidechain.createrawtransaction( [], {sidechain.getnewaddress(): 1}) fund_spend = sidechain.fundrawtransaction(raw_spend) sign_spend = sidechain.signrawtransactionwithwallet(fund_spend["hex"]) signed_struct = FromHex(CTransaction(), sign_spend["hex"]) # Non-witness tx has no witness serialized yet if len(signed_struct.wit.vtxinwit) == 0: signed_struct.wit.vtxinwit = [CTxInWitness()] signed_struct.wit.vtxinwit[ 0].peginWitness.stack = sample_pegin_witness.stack assert_equal( sidechain.testmempoolaccept([signed_struct.serialize().hex() ])[0]["allowed"], False) assert_equal( sidechain.testmempoolaccept([signed_struct.serialize().hex() ])[0]["reject-reason"], "extra-pegin-witness") signed_struct.wit.vtxinwit[0].peginWitness.stack = [b'\x00' * 100000 ] # lol assert_equal( sidechain.testmempoolaccept([signed_struct.serialize().hex() ])[0]["allowed"], False) assert_equal( sidechain.testmempoolaccept([signed_struct.serialize().hex() ])[0]["reject-reason"], "extra-pegin-witness") peg_out_txid = sidechain.sendtomainchain(some_btc_addr, 1) peg_out_details = sidechain.decoderawtransaction( sidechain.getrawtransaction(peg_out_txid)) # peg-out, change, fee assert len(peg_out_details["vout"]) == 3 found_pegout_value = False for output in peg_out_details["vout"]: if "value" in output and output["value"] == 1: found_pegout_value = True assert found_pegout_value bal_2 = sidechain.getwalletinfo()["balance"]["bitcoin"] # Make sure balance went down assert bal_2 + 1 < bal_1 # Send rest of coins using subtractfee from output arg sidechain.sendtomainchain(some_btc_addr, bal_2, True) assert sidechain.getwalletinfo()["balance"]['bitcoin'] == 0 print('Test coinbase peg-in maturity rules') # Have bitcoin output go directly into a claim output pegin_info = sidechain.getpeginaddress() mainchain_addr = pegin_info["mainchain_address"] # Watch the address so we can get tx without txindex parent.importaddress(mainchain_addr) claim_block = parent.generatetoaddress(50, mainchain_addr)[0] for node_group in self.node_groups: self.sync_all(node_group) block_coinbase = parent.getblock(claim_block, 2)["tx"][0] claim_txid = block_coinbase["txid"] claim_tx = block_coinbase["hex"] claim_proof = parent.gettxoutproof([claim_txid], claim_block) # Can't claim something even though it has 50 confirms since it's coinbase assert_raises_rpc_error( -8, "Peg-in Bitcoin transaction needs more confirmations to be sent.", sidechain.claimpegin, claim_tx, claim_proof) # If done via raw API, still doesn't work coinbase_pegin = sidechain.createrawpegin(claim_tx, claim_proof) assert_equal(coinbase_pegin["mature"], False) signed_pegin = sidechain.signrawtransactionwithwallet( coinbase_pegin["hex"])["hex"] assert_raises_rpc_error( -26, "bad-pegin-witness, Needs more confirmations.", sidechain.sendrawtransaction, signed_pegin) # 50 more blocks to allow wallet to make it succeed by relay and consensus parent.generatetoaddress(50, parent.getnewaddress()) for node_group in self.node_groups: self.sync_all(node_group) # Wallet still doesn't want to for 2 more confirms assert_equal( sidechain.createrawpegin(claim_tx, claim_proof)["mature"], False) # But we can just shoot it off claim_txid = sidechain.sendrawtransaction(signed_pegin) sidechain.generatetoaddress(1, sidechain.getnewaddress()) for node_group in self.node_groups: self.sync_all(node_group) assert_equal(sidechain.gettransaction(claim_txid)["confirmations"], 1) # Test a confidential pegin. print("Performing a confidential pegin.") # start pegin pegin_addrs = sidechain.getpeginaddress() assert_equal( sidechain.decodescript(pegin_addrs["claim_script"])["type"], "witness_v0_keyhash") pegin_addr = addrs["mainchain_address"] txid_fund = parent.sendtoaddress(pegin_addr, 10) # 10+2 confirms required to get into mempool and confirm parent.generate(11) for node_group in self.node_groups: self.sync_all(node_group) proof = parent.gettxoutproof([txid_fund]) assert_equal(sidechain.gettransaction(claim_txid)["confirmations"], 1) # Test a confidential pegin. print("Performing a confidential pegin.") # start pegin pegin_addrs = sidechain.getpeginaddress() assert_equal( sidechain.decodescript(pegin_addrs["claim_script"])["type"], "witness_v0_keyhash") pegin_addr = addrs["mainchain_address"] txid_fund = parent.sendtoaddress(pegin_addr, 10) # 10+2 confirms required to get into mempool and confirm parent.generate(11) for node_group in self.node_groups: self.sync_all(node_group) proof = parent.gettxoutproof([txid_fund]) raw = parent.gettransaction(txid_fund)["hex"] raw_pegin = sidechain.createrawpegin(raw, proof)['hex'] pegin = FromHex(CTransaction(), raw_pegin) # add new blinding pubkey for the pegin output pegin.vout[0].nNonce = CTxOutNonce( hex_str_to_bytes( sidechain.getaddressinfo(sidechain.getnewaddress( "", "blech32"))["confidential_key"])) # now add an extra input and output from listunspent; we need a blinded output for this blind_addr = sidechain.getnewaddress("", "blech32") sidechain.sendtoaddress(blind_addr, 15) sidechain.generate(6) # Make sure sidechain2 knows about the same input for node_group in self.node_groups: self.sync_all(node_group) unspent = [ u for u in sidechain.listunspent(6, 6) if u["amount"] == 15 ][0] assert (unspent["spendable"]) assert ("amountcommitment" in unspent) pegin.vin.append( CTxIn(COutPoint(int(unspent["txid"], 16), unspent["vout"]))) # insert corresponding output before fee output new_destination = sidechain.getaddressinfo( sidechain.getnewaddress("", "blech32")) new_dest_script_pk = hex_str_to_bytes(new_destination["scriptPubKey"]) new_dest_nonce = CTxOutNonce( hex_str_to_bytes(new_destination["confidential_key"])) new_dest_asset = pegin.vout[0].nAsset pegin.vout.insert( 1, CTxOut( int(unspent["amount"] * COIN) - 10000, new_dest_script_pk, new_dest_asset, new_dest_nonce)) # add the 10 ksat fee pegin.vout[2].nValue.setToAmount(pegin.vout[2].nValue.getAmount() + 10000) pegin_hex = ToHex(pegin) # test with both blindraw and rawblindraw raw_pegin_blinded1 = sidechain.blindrawtransaction(pegin_hex) raw_pegin_blinded2 = sidechain.rawblindrawtransaction( pegin_hex, ["", unspent["amountblinder"]], [10, 15], [unspent["asset"]] * 2, ["", unspent["assetblinder"]], "", False) pegin_signed1 = sidechain.signrawtransactionwithwallet( raw_pegin_blinded1) pegin_signed2 = sidechain.signrawtransactionwithwallet( raw_pegin_blinded2) for pegin_signed in [pegin_signed1, pegin_signed2]: final_decoded = sidechain.decoderawtransaction(pegin_signed["hex"]) assert (final_decoded["vin"][0]["is_pegin"]) assert (not final_decoded["vin"][1]["is_pegin"]) assert ("assetcommitment" in final_decoded["vout"][0]) assert ("valuecommitment" in final_decoded["vout"][0]) assert ("commitmentnonce" in final_decoded["vout"][0]) assert ("value" not in final_decoded["vout"][0]) assert ("asset" not in final_decoded["vout"][0]) assert (final_decoded["vout"][0]["commitmentnonce_fully_valid"]) assert ("assetcommitment" in final_decoded["vout"][1]) assert ("valuecommitment" in final_decoded["vout"][1]) assert ("commitmentnonce" in final_decoded["vout"][1]) assert ("value" not in final_decoded["vout"][1]) assert ("asset" not in final_decoded["vout"][1]) assert (final_decoded["vout"][1]["commitmentnonce_fully_valid"]) assert ("value" in final_decoded["vout"][2]) assert ("asset" in final_decoded["vout"][2]) # check that it is accepted in either mempool accepted = sidechain.testmempoolaccept([pegin_signed["hex"]])[0] if not accepted["allowed"]: raise Exception(accepted["reject-reason"]) accepted = sidechain2.testmempoolaccept([pegin_signed["hex"]])[0] if not accepted["allowed"]: raise Exception(accepted["reject-reason"]) print("Blinded transaction looks ok!" ) # need this print to distinguish failures in for loop print('Success!') # Manually stop sidechains first, then the parent chains. self.stop_node(2) self.stop_node(3) self.stop_node(0) self.stop_node(1)
def fund_and_test_wallet(scriptPubKey, is_standard, expected_in_std_wallet, amount=10000, spendfee=500, nonstd_error="scriptpubkey", sign_error=None): """ Get the nonstandard node to fund a transaction, test its standardness by trying to broadcast on the standard node, then mine it and see if it ended up in the standard node's wallet. Finally, it attempts to spend the coin. """ self.log.info("Trying script {}".format(scriptPubKey.hex(),)) # get nonstandard node to fund the script tx = CTransaction() tx.vout.append(CTxOut(max(amount, 10000), scriptPubKey)) rawtx = nonstd_node.fundrawtransaction( ToHex(tx), {'lockUnspents': True, 'changePosition': 1})['hex'] # fundrawtransaction doesn't like to fund dust outputs, so we # have to manually override the amount. FromHex(tx, rawtx) tx.vout[0].nValue = min(amount, 10000) rawtx = nonstd_node.signrawtransactionwithwallet(ToHex(tx))['hex'] # ensure signing process did not disturb scriptPubKey signedtx = FromHex(CTransaction(), rawtx) assert_equal(scriptPubKey, signedtx.vout[0].scriptPubKey) txid = signedtx.rehash() balance_initial = std_node.getbalance() # try broadcasting it on the standard node if is_standard: std_node.sendrawtransaction(rawtx) assert txid in std_node.getrawmempool() else: assert_raises_rpc_error(-26, nonstd_error, std_node.sendrawtransaction, rawtx) assert txid not in std_node.getrawmempool() # make sure it's in nonstandard node's mempool, then mine it nonstd_node.sendrawtransaction(rawtx) assert txid in nonstd_node.getrawmempool() [blockhash] = nonstd_node.generate(1) # make sure it was mined assert txid in nonstd_node.getblock(blockhash)["tx"] self.sync_blocks() wallet_outpoints = {(entry['txid'], entry['vout']) for entry in std_node.listunspent()} # calculate wallet balance change just as a double check balance_change = std_node.getbalance() - balance_initial if expected_in_std_wallet: assert (txid, 0) in wallet_outpoints assert balance_change == amount * SATOSHI else: assert (txid, 0) not in wallet_outpoints assert balance_change == 0 # try spending the funds using the wallet. outamount = (amount - spendfee) * SATOSHI if outamount < 546 * SATOSHI: # If the final amount would be too small, then just donate # to miner fees. outputs = [{"data": b"to miner, with love".hex()}] else: outputs = [{address_nonstd: outamount}] spendtx = std_node.createrawtransaction( [{'txid': txid, 'vout': 0}], outputs) signresult = std_node.signrawtransactionwithwallet(spendtx) if sign_error is None: assert_equal(signresult['complete'], True) txid = std_node.sendrawtransaction(signresult['hex']) [blockhash] = std_node.generate(1) # make sure it was mined assert txid in std_node.getblock(blockhash)["tx"] self.sync_blocks() else: assert_equal(signresult['complete'], False) assert_equal(signresult['errors'][0]['error'], sign_error)
def test_sequence_lock_unconfirmed_inputs(self): # Store height so we can easily reset the chain at the end of the test cur_height = self.nodes[0].getblockcount() # Create a mempool tx. txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2000000) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) tx1.rehash() # As the fees are calculated prior to the transaction being signed, # there is some uncertainty that calculate fee provides the correct # minimal fee. Since regtest coins are free, let's go ahead and # increase the fee by an order of magnitude to ensure this test # passes. fee_multiplier = 10 # Anyone-can-spend mempool tx. # Sequence lock of 0 should pass. tx2 = CTransaction() tx2.nVersion = 2 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [CTxOut(int(0), CScript([b'a']))] tx2.vout[0].nValue = tx1.vout[0].nValue - \ fee_multiplier * self.nodes[0].calculate_fee(tx2) tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) tx2.rehash() self.nodes[0].sendrawtransaction(tx2_raw) # Create a spend of the 0th output of orig_tx with a sequence lock # of 1, and test what happens when submitting. # orig_tx.vout[0] must be an anyone-can-spend output def test_nonzero_locks(orig_tx, node, use_height_lock): sequence_value = 1 if not use_height_lock: sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx = CTransaction() tx.nVersion = 2 tx.vin = [ CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value) ] tx.vout = [ CTxOut( int(orig_tx.vout[0].nValue - fee_multiplier * node.calculate_fee(tx)), CScript([b'a'])) ] pad_tx(tx) tx.rehash() if (orig_tx.hash in node.getrawmempool()): # sendrawtransaction should fail if the tx is in the mempool assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx)) else: # sendrawtransaction should succeed if the tx is not in the # mempool node.sendrawtransaction(ToHex(tx)) return tx test_nonzero_locks(tx2, self.nodes[0], use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], use_height_lock=False) # Now mine some blocks, but make sure tx2 doesn't get mined. # Use prioritisetransaction to lower the effective feerate to 0 self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=-fee_multiplier * self.nodes[0].calculate_fee(tx2)) cur_time = int(time.time()) for _ in range(10): self.nodes[0].setmocktime(cur_time + 600) self.nodes[0].generate(1) cur_time += 600 assert tx2.hash in self.nodes[0].getrawmempool() test_nonzero_locks(tx2, self.nodes[0], use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], use_height_lock=False) # Mine tx2, and then try again self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=fee_multiplier * self.nodes[0].calculate_fee(tx2)) # Advance the time on the node so that we can test timelocks self.nodes[0].setmocktime(cur_time + 600) # Save block template now to use for the reorg later tmpl = self.nodes[0].getblocktemplate() self.nodes[0].generate(1) assert tx2.hash not in self.nodes[0].getrawmempool() # Now that tx2 is not in the mempool, a sequence locked spend should # succeed tx3 = test_nonzero_locks(tx2, self.nodes[0], use_height_lock=False) assert tx3.hash in self.nodes[0].getrawmempool() self.nodes[0].generate(1) assert tx3.hash not in self.nodes[0].getrawmempool() # One more test, this time using height locks tx4 = test_nonzero_locks(tx3, self.nodes[0], use_height_lock=True) assert tx4.hash in self.nodes[0].getrawmempool() # Now try combining confirmed and unconfirmed inputs tx5 = test_nonzero_locks(tx4, self.nodes[0], use_height_lock=True) assert tx5.hash not in self.nodes[0].getrawmempool() utxos = self.nodes[0].listunspent() tx5.vin.append( CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1)) tx5.vout[0].nValue += int(utxos[0]["amount"] * XEC) raw_tx5 = self.nodes[0].signrawtransactionwithwallet(ToHex(tx5))["hex"] assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) # Test mempool-BIP68 consistency after reorg # # State of the transactions in the last blocks: # ... -> [ tx2 ] -> [ tx3 ] # tip-1 tip # And currently tx4 is in the mempool. # # If we invalidate the tip, tx3 should get added to the mempool, causing # tx4 to be removed (fails sequence-lock). self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) assert tx4.hash not in self.nodes[0].getrawmempool() assert tx3.hash in self.nodes[0].getrawmempool() # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in # diagram above). # This would cause tx2 to be added back to the mempool, which in turn causes # tx3 to be removed. for i in range(2): block = create_block(tmpl=tmpl, ntime=cur_time) block.rehash() block.solve() tip = block.sha256 assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(ToHex(block))) tmpl = self.nodes[0].getblocktemplate() tmpl['previousblockhash'] = f"{tip:x}" tmpl['transactions'] = [] cur_time += 1 mempool = self.nodes[0].getrawmempool() assert tx3.hash not in mempool assert tx2.hash in mempool # Reset the chain and get rid of the mocktimed-blocks self.nodes[0].setmocktime(0) self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height + 1)) self.nodes[0].generate(10)
def run_test(self): node = self.nodes[0] self.log.info('Start with empty mempool, and 200 blocks') self.mempool_size = 0 assert_equal(node.getblockcount(), 200) assert_equal(node.getmempoolinfo()['size'], self.mempool_size) coins = node.listunspent() self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error( -3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar')) assert_raises_rpc_error( -8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22'] * 26)) assert_raises_rpc_error( -8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=[])) assert_raises_rpc_error( -22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar'])) self.log.info('A transaction already in the blockchain') coin = coins.pop() # Pick a random coin(base) to spend raw_tx_in_block = node.signrawtransactionwithwallet( node.createrawtransaction( inputs=[{ 'txid': coin['txid'], 'vout': coin['vout'] }], outputs=[{ node.getnewaddress(): 0.3 }, { node.getnewaddress(): 49 }], ))['hex'] txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, maxfeerate=0) node.generate(1) self.mempool_size = 0 self.check_mempool_result( result_expected=[{ 'txid': txid_in_block, 'allowed': False, 'reject-reason': 'txn-already-known' }], rawtxs=[raw_tx_in_block], ) self.log.info('A transaction not in the mempool') fee = Decimal('0.000007') raw_tx_0 = node.signrawtransactionwithwallet( node.createrawtransaction( inputs=[{ "txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER }], # RBF is used later outputs=[{ node.getnewaddress(): Decimal('0.3') - fee }], ))['hex'] tx = tx_from_hex(raw_tx_0) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': fee } }], rawtxs=[raw_tx_0], ) self.log.info('A final transaction not in the mempool') coin = coins.pop() # Pick a random coin(base) to spend output_amount = Decimal('0.025') raw_tx_final = node.signrawtransactionwithwallet( node.createrawtransaction( inputs=[{ 'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff }], # SEQUENCE_FINAL outputs=[{ node.getnewaddress(): output_amount }], locktime=node.getblockcount() + 2000, # Can be anything ))['hex'] tx = tx_from_hex(raw_tx_final) fee_expected = coin['amount'] - output_amount self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': fee_expected } }], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0) self.mempool_size += 1 self.log.info('A transaction in the mempool') node.sendrawtransaction(hexstring=raw_tx_0) self.mempool_size += 1 self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': False, 'reject-reason': 'txn-already-in-mempool' }], rawtxs=[raw_tx_0], ) self.log.info('A transaction that replaces a mempool transaction') tx = tx_from_hex(raw_tx_0) tx.vout[0].nValue -= int(fee * COIN) # Double the fee tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF raw_tx_0 = node.signrawtransactionwithwallet( tx.serialize().hex())['hex'] tx = tx_from_hex(raw_tx_0) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': (2 * fee) } }], rawtxs=[raw_tx_0], ) self.log.info('A transaction that conflicts with an unconfirmed tx') # Send the transaction that replaces the mempool transaction and opts out of replaceability node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0) # take original raw_tx_0 tx = tx_from_hex(raw_tx_0) tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee # skip re-signing the tx self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'txn-mempool-conflict' }], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) self.log.info('A transaction with missing inputs, that never existed') tx = tx_from_hex(raw_tx_0) tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14) # skip re-signing the tx self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs' }], rawtxs=[tx.serialize().hex()], ) self.log.info( 'A transaction with missing inputs, that existed once in the past') tx = tx_from_hex(raw_tx_0) tx.vin[ 0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend raw_tx_1 = node.signrawtransactionwithwallet( tx.serialize().hex())['hex'] txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0) # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them raw_tx_spend_both = node.signrawtransactionwithwallet( node.createrawtransaction(inputs=[ { 'txid': txid_0, 'vout': 0 }, { 'txid': txid_1, 'vout': 0 }, ], outputs=[{ node.getnewaddress(): 0.1 }]))['hex'] txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, maxfeerate=0) node.generate(1) self.mempool_size = 0 # Now see if we can add the coins back to the utxo set by sending the exact txs again self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs' }], rawtxs=[raw_tx_0], ) self.check_mempool_result( result_expected=[{ 'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs' }], rawtxs=[raw_tx_1], ) self.log.info('Create a signed "reference" tx for later use') raw_tx_reference = node.signrawtransactionwithwallet( node.createrawtransaction( inputs=[{ 'txid': txid_spend_both, 'vout': 0 }], outputs=[{ node.getnewaddress(): 0.05 }], ))['hex'] tx = tx_from_hex(raw_tx_reference) # Reference tx should be valid on itself self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05') } }], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) self.log.info('A transaction with no outputs') tx = tx_from_hex(raw_tx_reference) tx.vout = [] # Skip re-signing the transaction for context independent checks from now on # tx = tx_from_hex(node.signrawtransactionwithwallet(tx.serialize().hex())['hex']) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-empty' }], rawtxs=[tx.serialize().hex()], ) self.log.info('A really large transaction') tx = tx_from_hex(raw_tx_reference) tx.vin = [tx.vin[0]] * math.ceil( MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize())) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-oversize' }], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with negative output value') tx = tx_from_hex(raw_tx_reference) tx.vout[0].nValue *= -1 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-negative' }], rawtxs=[tx.serialize().hex()], ) # The following two validations prevent overflow of the output amounts (see CVE-2010-5139). self.log.info('A transaction with too large output value') tx = tx_from_hex(raw_tx_reference) tx.vout[0].nValue = MAX_MONEY + 1 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-toolarge' }], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with too large sum of output values') tx = tx_from_hex(raw_tx_reference) tx.vout = [tx.vout[0]] * 2 tx.vout[0].nValue = MAX_MONEY self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-txouttotal-toolarge' }], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with duplicate inputs') tx = tx_from_hex(raw_tx_reference) tx.vin = [tx.vin[0]] * 2 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-inputs-duplicate' }], rawtxs=[tx.serialize().hex()], ) self.log.info('A coinbase transaction') # Pick the input of the first tx we signed, so it has to be a coinbase tx raw_tx_coinbase_spent = node.getrawtransaction( txid=node.decoderawtransaction( hexstring=raw_tx_in_block)['vin'][0]['txid']) tx = tx_from_hex(raw_tx_coinbase_spent) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'coinbase' }], rawtxs=[tx.serialize().hex()], ) self.log.info('Some nonstandard transactions') tx = tx_from_hex(raw_tx_reference) tx.nVersion = 3 # A version currently non-standard self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'version' }], rawtxs=[tx.serialize().hex()], ) tx = tx_from_hex(raw_tx_reference) tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptpubkey' }], rawtxs=[tx.serialize().hex()], ) tx = tx_from_hex(raw_tx_reference) key = ECKey() key.generate() pubkey = key.get_pubkey().get_bytes() tx.vout[0].scriptPubKey = CScript( [OP_2, pubkey, pubkey, pubkey, OP_3, OP_CHECKMULTISIG]) # Some bare multisig script (2-of-3) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bare-multisig' }], rawtxs=[tx.serialize().hex()], ) tx = tx_from_hex(raw_tx_reference) tx.vin[0].scriptSig = CScript([OP_HASH160 ]) # Some not-pushonly scriptSig self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptsig-not-pushonly' }], rawtxs=[tx.serialize().hex()], ) tx = tx_from_hex(raw_tx_reference) tx.vin[0].scriptSig = CScript( [b'a' * 1648]) # Some too large scriptSig (>1650 bytes) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptsig-size' }], rawtxs=[tx.serialize().hex()], ) tx = tx_from_hex(raw_tx_reference) output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript( [OP_HASH160, hash160(b'burn'), OP_EQUAL])) num_scripts = 100000 // len(output_p2sh_burn.serialize( )) # Use enough outputs to make the tx too large for our policy tx.vout = [output_p2sh_burn] * num_scripts self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'tx-size' }], rawtxs=[tx.serialize().hex()], ) tx = tx_from_hex(raw_tx_reference) tx.vout[0] = output_p2sh_burn tx.vout[ 0].nValue -= 1 # Make output smaller, such that it is dust for our policy self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'dust' }], rawtxs=[tx.serialize().hex()], ) tx = tx_from_hex(raw_tx_reference) tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff']) tx.vout = [tx.vout[0]] * 2 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'multi-op-return' }], rawtxs=[tx.serialize().hex()], ) self.log.info('A timelocked transaction') tx = tx_from_hex(raw_tx_reference) tx.vin[ 0].nSequence -= 1 # Should be non-max, so locktime is not ignored tx.nLockTime = node.getblockcount() + 1 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'non-final' }], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction that is locked by BIP68 sequence logic') tx = tx_from_hex(raw_tx_reference) tx.vin[ 0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one # Can skip re-signing the tx because of early rejection self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'non-BIP68-final' }], rawtxs=[tx.serialize().hex()], maxfeerate=0, )
def fund_and_test_wallet(scriptPubKey, shouldBeStandard, shouldBeInWallet, amount=10000, spendfee=500, nonstd_error="scriptpubkey (code 64)"): """ Get the nonstandard node to fund a transaction, test its standardness by trying to broadcast on the standard node, then mine it and see if it ended up in the standard node's wallet. Finally, it attempts to spend the coin. """ self.log.info("Trying script {}".format(scriptPubKey.hex(), )) # get nonstandard node to fund the script tx = CTransaction() tx.vout.append(CTxOut(max(amount, 10000), scriptPubKey)) rawtx = nonstd_node.fundrawtransaction(ToHex(tx), { 'lockUnspents': True, 'changePosition': 1 })['hex'] # fundrawtransaction doesn't like to fund dust outputs, so we # have to manually override the amount. FromHex(tx, rawtx) tx.vout[0].nValue = min(amount, 10000) rawtx = nonstd_node.signrawtransactionwithwallet(ToHex(tx))['hex'] # ensure signing process did not disturb scriptPubKey signedtx = FromHex(CTransaction(), rawtx) assert_equal(scriptPubKey, signedtx.vout[0].scriptPubKey) txid = signedtx.rehash() balance_initial = std_node.getbalance() # try broadcasting it on the standard node if shouldBeStandard: std_node.sendrawtransaction(rawtx) else: assert_raises_rpc_error(-26, nonstd_error, std_node.sendrawtransaction, rawtx) # make sure it's in nonstandard node's mempool, then mine it nonstd_node.sendrawtransaction(rawtx) assert txid in nonstd_node.getrawmempool() [blockhash] = nonstd_node.generate(1) # make sure it was mined assert txid in nonstd_node.getblock(blockhash)["tx"] sync_blocks(self.nodes) wallet_outpoints = {(entry['txid'], entry['vout']) for entry in std_node.listunspent()} # calculate wallet balance change just as a double check balance_change = std_node.getbalance() - balance_initial # try spending the funds using the wallet. outamount = (amount - spendfee) * SATOSHI if outamount < 546 * SATOSHI: # If the final amount would be too small, then just donate # to miner fees. outputs = [{"data": b"to miner, with love".hex()}] else: outputs = [{address_nonstd: outamount}] spendtx = std_node.createrawtransaction([{ 'txid': txid, 'vout': 0 }], outputs) signresult = std_node.signrawtransactionwithwallet(spendtx) if shouldBeInWallet: assert (txid, 0) in wallet_outpoints assert balance_change == amount * SATOSHI assert_equal(signresult['complete'], True) txid = std_node.sendrawtransaction(signresult['hex']) [blockhash] = std_node.generate(1) # make sure it was mined assert txid in std_node.getblock(blockhash)["tx"] sync_blocks(self.nodes) else: assert (txid, 0) not in wallet_outpoints assert balance_change == 0 # signresult['errors'] will vary depending on input script. What # occurs is that in sign.cpp, ProduceSignature gets back # solved=false since SignStep sees a nonstandard input. Then, # an empty SignatureData results. Back in rawtransaction.cpp's # SignTransaction, it will then attempt to execute the # scriptPubKey with an empty scriptSig. A P2PKH script will thus # fail at OP_DUP with stack error, and P2PK/Multisig will fail # once they hit a nonminimal push. The error message is just an # artifact of the script type, basically. assert_equal(signresult['complete'], False)
def run_test(self): node = self.nodes[0] node.add_p2p_connection(P2PDataStore()) # Set the blocksize to 2MB as initial condition node.setexcessiveblock(self.excessive_block_size) self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # move the tip back to a previous block def tip(number): self.tip = self.blocks[number] # adds transactions to the block and updates state def update_block(block_number, new_transactions): block = self.blocks[block_number] self.add_transactions_to_block(block, new_transactions) old_sha256 = block.sha256 make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Update the internal state just like in next_block self.tip = block if block.sha256 != old_sha256: self.block_heights[ block.sha256] = self.block_heights[old_sha256] del self.block_heights[old_sha256] self.blocks[block_number] = block return block # shorthand for functions block = self.next_block # Create a new block block(0) save_spendable_output() node.p2p.send_blocks_and_test([self.tip], node) # Now we need that block to mature so we can spend the coinbase. maturity_blocks = [] for i in range(105): block(5000 + i) maturity_blocks.append(self.tip) save_spendable_output() node.p2p.send_blocks_and_test(maturity_blocks, node) # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Accept many sigops lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) block(19, spend=out[0], script=lots_of_checksigs, block_size=ONE_MEGABYTE) node.p2p.send_blocks_and_test([self.tip], node) block(20, spend=out[1], script=lots_of_checksigs, block_size=ONE_MEGABYTE, extra_sigops=1) node.p2p.send_blocks_and_test([self.tip], node, success=False, reject_reason='bad-blk-sigops') # Rewind bad block tip(19) # Accept 40k sigops per block > 1MB and <= 2MB block(21, spend=out[1], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=ONE_MEGABYTE + 1) node.p2p.send_blocks_and_test([self.tip], node) # Accept 40k sigops per block > 1MB and <= 2MB block(22, spend=out[2], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE) node.p2p.send_blocks_and_test([self.tip], node) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(23, spend=out[3], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=ONE_MEGABYTE + 1) node.p2p.send_blocks_and_test([self.tip], node, success=False, reject_reason='bad-blk-sigops') # Rewind bad block tip(22) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(24, spend=out[3], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE) node.p2p.send_blocks_and_test([self.tip], node, success=False, reject_reason='bad-blk-sigops') # Rewind bad block tip(22) # Accept 60k sigops per block > 2MB and <= 3MB block(25, spend=out[3], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE + 1) node.p2p.send_blocks_and_test([self.tip], node) # Accept 60k sigops per block > 2MB and <= 3MB block(26, spend=out[4], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=3 * ONE_MEGABYTE) node.p2p.send_blocks_and_test([self.tip], node) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(27, spend=out[5], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE + 1) node.p2p.send_blocks_and_test([self.tip], node, success=False, reject_reason='bad-blk-sigops') # Rewind bad block tip(26) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(28, spend=out[5], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=3 * ONE_MEGABYTE) node.p2p.send_blocks_and_test([self.tip], node, success=False, reject_reason='bad-blk-sigops') # Rewind bad block tip(26) # Too many sigops in one txn too_many_tx_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB + 1)) block(29, spend=out[6], script=too_many_tx_checksigs, block_size=ONE_MEGABYTE + 1) node.p2p.send_blocks_and_test([self.tip], node, success=False, reject_reason='bad-txn-sigops') # Rewind bad block tip(26) # Generate a key pair to test P2SH sigops count private_key = ECKey() private_key.generate() public_key = private_key.get_pubkey().get_bytes() # P2SH # Build the redeem script, hash it, use hash to create the p2sh script redeem_script = CScript([public_key] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) redeem_script_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) # Create a p2sh transaction p2sh_tx = self.create_tx(out[6], 1, p2sh_script) # Add the transaction to the block block(30) update_block(30, [p2sh_tx]) node.p2p.send_blocks_and_test([self.tip], node) # Creates a new transaction using the p2sh transaction included in the # last block def spend_p2sh_tx(output_script=CScript([OP_TRUE])): # Create the transaction spent_p2sh_tx = CTransaction() spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b'')) spent_p2sh_tx.vout.append(CTxOut(1, output_script)) # Sign the transaction using the redeem script sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx.vout[0].nValue) sig = private_key.sign_ecdsa(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) spent_p2sh_tx.rehash() return spent_p2sh_tx # Sigops p2sh limit p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \ redeem_script.GetSigOpCount(True) # Too many sigops in one p2sh txn too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1)) block(31, spend=out[7], block_size=ONE_MEGABYTE + 1) update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)]) node.p2p.send_blocks_and_test([self.tip], node, success=False, reject_reason='bad-txn-sigops') # Rewind bad block tip(30) # Max sigops in one p2sh txn max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit)) block(32, spend=out[8], block_size=ONE_MEGABYTE + 1) update_block(32, [spend_p2sh_tx(max_p2sh_sigops)]) node.p2p.send_blocks_and_test([self.tip], node) # Ensure that a coinbase with too many sigops is forbidden, even if it # doesn't push the total block count over the limit. b33 = block(33, spend=out[9], block_size=2 * ONE_MEGABYTE) # 20001 sigops b33.vtx[0].vout.append( CTxOut(0, CScript([OP_CHECKMULTISIG] * 1000 + [OP_CHECKDATASIG]))) update_block(33, []) node.p2p.send_blocks_and_test([b33], node, success=False, reject_reason='bad-txn-sigops') # 20000 sigops b33.vtx[0].vout[-1].scriptPubKey = CScript([OP_CHECKMULTISIG] * 1000) update_block(33, []) node.p2p.send_blocks_and_test([b33], node)
def get_tx(self): tx = CTransaction() tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE] * 100))) tx.calc_sha256() return tx
def run_test(self): p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) # Build the blockchain self.tip = int(self.nodes[0].getbestblockhash(), 16) self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1 self.blocks = [] # Get a pubkey for the coinbase TXO coinbase_key = ECKey() coinbase_key.generate() coinbase_pubkey = coinbase_key.get_pubkey().get_bytes() # Create the first block with a coinbase output to our key height = 1 block = create_block(self.tip, create_coinbase(height, coinbase_pubkey), self.block_time) self.blocks.append(block) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 height += 1 # Bury the block 100 deep so the coinbase output is spendable for i in range(100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 height += 1 # Create a transaction spending the coinbase output with an invalid (null) signature tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b"")) tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE]))) tx.calc_sha256() block102 = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block102.vtx.extend([tx]) block102.hashMerkleRoot = block102.calc_merkle_root() block102.rehash() block102.solve() self.blocks.append(block102) self.tip = block102.sha256 self.block_time += 1 height += 1 # Bury the assumed valid block 2100 deep for i in range(2100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.nVersion = 4 block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 height += 1 self.nodes[0].disconnect_p2ps() # Start node1 and node2 with assumevalid so they accept a block with a bad signature. self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)]) p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) p2p2 = self.nodes[2].add_p2p_connection(BaseNode()) # send header lists to all three nodes p2p0.send_header_for_blocks(self.blocks[0:2000]) p2p0.send_header_for_blocks(self.blocks[2000:]) p2p1.send_header_for_blocks(self.blocks[0:2000]) p2p1.send_header_for_blocks(self.blocks[2000:]) p2p2.send_header_for_blocks(self.blocks[0:200]) # Send blocks to node0. Block 102 will be rejected. self.send_blocks_until_disconnected(p2p0) self.assert_blockchain_height(self.nodes[0], 101) # Send all blocks to node1. All blocks will be accepted. for i in range(2202): p2p1.send_message(msg_block(self.blocks[i])) # Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync. p2p1.sync_with_ping(200) assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202) # Send blocks to node2. Block 102 will be rejected. self.send_blocks_until_disconnected(p2p2) self.assert_blockchain_height(self.nodes[2], 101)
rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL])) num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy tx.vout = [output_p2sh_burn] * num_scripts self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0] = output_p2sh_burn tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff'])
def test_sequence_lock_confirmed_inputs(self): # Create lots of confirmed utxos, and use them to generate lots of random # transactions. max_outputs = 50 addresses = [] while len(addresses) < max_outputs: addresses.append(self.nodes[0].getnewaddress()) while len(self.nodes[0].listunspent()) < 200: import random random.shuffle(addresses) num_outputs = random.randint(1, max_outputs) outputs = {} for i in range(num_outputs): outputs[addresses[i]] = random.randint(1, 20) * 0.01 self.nodes[0].sendmany("", outputs) self.nodes[0].generate(1) utxos = self.nodes[0].listunspent() # Try creating a lot of random transactions. # Each time, choose a random number of inputs, and randomly set # some of those inputs to be sequence locked (and randomly choose # between height/time locking). Small random chance of making the locks # all pass. for i in range(400): # Randomly choose up to 10 inputs num_inputs = random.randint(1, 10) random.shuffle(utxos) # Track whether any sequence locks used should fail should_pass = True # Track whether this transaction was built with sequence locks using_sequence_locks = False tx = CTransaction() tx.nVersion = 2 value = 0 for j in range(num_inputs): sequence_value = 0xfffffffe # this disables sequence locks # 50% chance we enable sequence locks if random.randint(0, 1): using_sequence_locks = True # 10% of the time, make the input sequence value pass input_will_pass = (random.randint(1, 10) == 1) sequence_value = utxos[j]["confirmations"] if not input_will_pass: sequence_value += 1 should_pass = False # Figure out what the median-time-past was for the confirmed input # Note that if an input has N confirmations, we're going back N blocks # from the tip so that we're looking up MTP of the block # PRIOR to the one the input appears in, as per the BIP68 spec. orig_time = self.get_median_time_past( utxos[j]["confirmations"]) cur_time = self.get_median_time_past(0) # MTP of the tip # can only timelock this input if it's not too old -- otherwise use height can_time_lock = True if ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY ) >= SEQUENCE_LOCKTIME_MASK: can_time_lock = False # if time-lockable, then 50% chance we make this a time lock if random.randint(0, 1) and can_time_lock: # Find first time-lock value that fails, or latest one that succeeds time_delta = sequence_value << SEQUENCE_LOCKTIME_GRANULARITY if input_will_pass and time_delta > cur_time - orig_time: sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY) elif (not input_will_pass and time_delta <= cur_time - orig_time): sequence_value = ( (cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY) + 1 sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx.vin.append( CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value)) value += utxos[j]["amount"] * COIN # Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output tx_size = len(ToHex(tx)) // 2 + 120 * num_inputs + 50 tx.vout.append( CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), DUMMY_P2WPKH_SCRIPT)) rawtx = self.nodes[0].signrawtransactionwithwallet( ToHex(tx))["hex"] if (using_sequence_locks and not should_pass): # This transaction should be rejected assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx) else: # This raw transaction should be accepted self.nodes[0].sendrawtransaction(rawtx) utxos = self.nodes[0].listunspent()
def get_tx(self): tx = CTransaction() tx.vin.append(self.valid_txin) tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx.calc_sha256() return tx
def run_test(self): node = self.nodes[0] self.log.info('Start with empty mempool, and 200 blocks') self.mempool_size = 0 wait_until(lambda: node.getblockcount() == 200) assert_equal(node.getmempoolinfo()['size'], self.mempool_size) self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error( -3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar')) assert_raises_rpc_error( -8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22'])) assert_raises_rpc_error( -22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar'])) self.log.info('A transaction already in the blockchain') coin = node.listunspent()[0] # Pick a random coin(base) to spend raw_tx_in_block = node.signrawtransactionwithwallet( node.createrawtransaction( inputs=[{ 'txid': coin['txid'], 'vout': coin['vout'] }], outputs=[{ node.getnewaddress(): 0.3 }, { node.getnewaddress(): 49 }], ))['hex'] txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True) node.generate(1) self.check_mempool_result( result_expected=[{ 'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known' }], rawtxs=[raw_tx_in_block], ) self.log.info('A transaction not in the mempool') fee = 0.00000700 raw_tx_0 = node.signrawtransactionwithwallet( node.createrawtransaction( inputs=[{ "txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER }], # RBF is used later outputs=[{ node.getnewaddress(): 0.3 - fee }], ))['hex'] tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': True }], rawtxs=[raw_tx_0], ) self.log.info('A transaction in the mempool') node.sendrawtransaction(hexstring=raw_tx_0) self.mempool_size = 1 self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': False, 'reject-reason': '18: txn-already-in-mempool' }], rawtxs=[raw_tx_0], ) self.log.info('A transaction that replaces a mempool transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(fee * COIN) # Double the fee tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF raw_tx_0 = node.signrawtransactionwithwallet( bytes_to_hex_str(tx.serialize()))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': True }], rawtxs=[raw_tx_0], ) self.log.info('A transaction that conflicts with an unconfirmed tx') # Send the transaction that replaces the mempool transaction and opts out of replaceability node.sendrawtransaction(hexstring=bytes_to_hex_str(tx.serialize()), allowhighfees=True) # take original raw_tx_0 tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee # skip re-signing the tx self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict' }], rawtxs=[bytes_to_hex_str(tx.serialize())], allowhighfees=True, ) self.log.info('A transaction with missing inputs, that never existed') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14) # skip re-signing the tx self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info( 'A transaction with missing inputs, that existed once in the past') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[ 0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend raw_tx_1 = node.signrawtransactionwithwallet( bytes_to_hex_str(tx.serialize()))['hex'] txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, allowhighfees=True) # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them raw_tx_spend_both = node.signrawtransactionwithwallet( node.createrawtransaction(inputs=[ { 'txid': txid_0, 'vout': 0 }, { 'txid': txid_1, 'vout': 0 }, ], outputs=[{ node.getnewaddress(): 0.1 }]))['hex'] txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, allowhighfees=True) node.generate(1) self.mempool_size = 0 # Now see if we can add the coins back to the utxo set by sending the exact txs again self.check_mempool_result( result_expected=[{ 'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs' }], rawtxs=[raw_tx_0], ) self.check_mempool_result( result_expected=[{ 'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs' }], rawtxs=[raw_tx_1], ) self.log.info('Create a signed "reference" tx for later use') raw_tx_reference = node.signrawtransactionwithwallet( node.createrawtransaction( inputs=[{ 'txid': txid_spend_both, 'vout': 0 }], outputs=[{ node.getnewaddress(): 0.05 }], ))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) # Reference tx should be valid on itself self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': True }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A transaction with no outputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [] # Skip re-signing the transaction for context independent checks from now on # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))['hex']))) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-empty' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A really large transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0] ] * (MAX_BLOCK_BASE_SIZE // len(tx.vin[0].serialize())) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A transaction with negative output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue *= -1 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-negative' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A transaction with too large output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue = 21000000 * COIN + 1 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-toolarge' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A transaction with too large sum of output values') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [tx.vout[0]] * 2 tx.vout[0].nValue = 21000000 * COIN self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-txouttotal-toolarge' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A transaction with duplicate inputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0]] * 2 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-inputs-duplicate' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A coinbase transaction') # Pick the input of the first tx we signed, so it has to be a coinbase tx raw_tx_coinbase_spent = node.getrawtransaction( txid=node.decoderawtransaction( hexstring=raw_tx_in_block)['vin'][0]['txid']) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent))) self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: coinbase' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('Some nonstandard transactions') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.nVersion = 3 # A version currently non-standard self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: version' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].scriptSig = CScript([OP_HASH160 ]) # Some not-pushonly scriptSig self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript( [OP_HASH160, hash160(b'burn'), OP_EQUAL])) num_scripts = 100000 // len(output_p2sh_burn.serialize( )) # Use enough outputs to make the tx too large for our policy tx.vout = [output_p2sh_burn] * num_scripts self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0] = output_p2sh_burn tx.vout[ 0].nValue -= 1 # Make output smaller, such that it is dust for our policy self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff']) tx.vout = [tx.vout[0]] * 2 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: multi-op-return' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A timelocked transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[ 0].nSequence -= 1 # Should be non-max, so locktime is not ignored tx.nLockTime = node.getblockcount() + 1 self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-final' }], rawtxs=[bytes_to_hex_str(tx.serialize())], ) self.log.info('A transaction that is locked by BIP68 sequence logic') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[ 0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one # Can skip re-signing the tx because of early rejection self.check_mempool_result( result_expected=[{ 'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final' }], rawtxs=[bytes_to_hex_str(tx.serialize())], allowhighfees=True, )
def _test_coin_stats_index(self): node = self.nodes[0] index_node = self.nodes[1] # Both none and muhash options allow the usage of the index index_hash_options = ['none', 'muhash'] # Generate a normal transaction and mine it node.generate(COINBASE_MATURITY + 1) address = self.nodes[0].get_deterministic_priv_key().address node.sendtoaddress(address=address, amount=10, subtractfeefromamount=True) node.generate(1) self.sync_blocks(timeout=120) self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option") self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", node.gettxoutsetinfo)) res0 = node.gettxoutsetinfo('none') # The fields 'disk_size' and 'transactions' do not exist on the index del res0['disk_size'], res0['transactions'] self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash')) for hash_option in index_hash_options: res1 = index_node.gettxoutsetinfo(hash_option) # The fields 'block_info' and 'total_unspendable_amount' only exist on the index del res1['block_info'], res1['total_unspendable_amount'] res1.pop('muhash', None) # Everything left should be the same assert_equal(res1, res0) self.log.info("Test that gettxoutsetinfo() can get fetch data on specific heights with index") # Generate a new tip node.generate(5) self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash')) for hash_option in index_hash_options: # Fetch old stats by height res2 = index_node.gettxoutsetinfo(hash_option, 102) del res2['block_info'], res2['total_unspendable_amount'] res2.pop('muhash', None) assert_equal(res0, res2) # Fetch old stats by hash res3 = index_node.gettxoutsetinfo(hash_option, res0['bestblock']) del res3['block_info'], res3['total_unspendable_amount'] res3.pop('muhash', None) assert_equal(res0, res3) # It does not work without coinstatsindex assert_raises_rpc_error(-8, "Querying specific block heights requires coinstatsindex", node.gettxoutsetinfo, hash_option, 102) self.log.info("Test gettxoutsetinfo() with index and verbose flag") for hash_option in index_hash_options: # Genesis block is unspendable res4 = index_node.gettxoutsetinfo(hash_option, 0) assert_equal(res4['total_unspendable_amount'], 50) assert_equal(res4['block_info'], { 'unspendable': 50, 'prevout_spent': 0, 'new_outputs_ex_coinbase': 0, 'coinbase': 0, 'unspendables': { 'genesis_block': 50, 'bip30': 0, 'scripts': 0, 'unclaimed_rewards': 0 } }) self.block_sanity_check(res4['block_info']) # Test an older block height that included a normal tx res5 = index_node.gettxoutsetinfo(hash_option, 102) assert_equal(res5['total_unspendable_amount'], 50) assert_equal(res5['block_info'], { 'unspendable': 0, 'prevout_spent': 50, 'new_outputs_ex_coinbase': Decimal('49.99995560'), 'coinbase': Decimal('50.00004440'), 'unspendables': { 'genesis_block': 0, 'bip30': 0, 'scripts': 0, 'unclaimed_rewards': 0 } }) self.block_sanity_check(res5['block_info']) # Generate and send a normal tx with two outputs tx1_inputs = [] tx1_outputs = {self.nodes[0].getnewaddress(): 21, self.nodes[0].getnewaddress(): 42} raw_tx1 = self.nodes[0].createrawtransaction(tx1_inputs, tx1_outputs) funded_tx1 = self.nodes[0].fundrawtransaction(raw_tx1) signed_tx1 = self.nodes[0].signrawtransactionwithwallet(funded_tx1['hex']) tx1_txid = self.nodes[0].sendrawtransaction(signed_tx1['hex']) # Find the right position of the 21 MBC output tx1_final = self.nodes[0].gettransaction(tx1_txid) for output in tx1_final['details']: if output['amount'] == Decimal('21.00000000') and output['category'] == 'receive': n = output['vout'] # Generate and send another tx with an OP_RETURN output (which is unspendable) tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(int(tx1_txid, 16), n), b'')) tx2.vout.append(CTxOut(int(20.99 * COIN), CScript([OP_RETURN] + [OP_FALSE]*30))) tx2_hex = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())['hex'] self.nodes[0].sendrawtransaction(tx2_hex) # Include both txs in a block self.nodes[0].generate(1) self.sync_all() self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash')) for hash_option in index_hash_options: # Check all amounts were registered correctly res6 = index_node.gettxoutsetinfo(hash_option, 108) assert_equal(res6['total_unspendable_amount'], Decimal('70.98999999')) assert_equal(res6['block_info'], { 'unspendable': Decimal('20.98999999'), 'prevout_spent': 111, 'new_outputs_ex_coinbase': Decimal('89.99993620'), 'coinbase': Decimal('50.01006381'), 'unspendables': { 'genesis_block': 0, 'bip30': 0, 'scripts': Decimal('20.98999999'), 'unclaimed_rewards': 0 } }) self.block_sanity_check(res6['block_info']) # Create a coinbase that does not claim full subsidy and also # has two outputs cb = create_coinbase(109, nValue=35) cb.vout.append(CTxOut(5 * COIN, CScript([OP_FALSE]))) cb.rehash() # Generate a block that includes previous coinbase tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblock(tip)['time'] + 1 block = create_block(int(tip, 16), cb, block_time) block.solve() self.nodes[0].submitblock(block.serialize().hex()) self.sync_all() self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash')) for hash_option in index_hash_options: res7 = index_node.gettxoutsetinfo(hash_option, 109) assert_equal(res7['total_unspendable_amount'], Decimal('80.98999999')) assert_equal(res7['block_info'], { 'unspendable': 10, 'prevout_spent': 0, 'new_outputs_ex_coinbase': 0, 'coinbase': 40, 'unspendables': { 'genesis_block': 0, 'bip30': 0, 'scripts': 0, 'unclaimed_rewards': 10 } }) self.block_sanity_check(res7['block_info']) self.log.info("Test that the index is robust across restarts") res8 = index_node.gettxoutsetinfo('muhash') self.restart_node(1, extra_args=self.extra_args[1]) res9 = index_node.gettxoutsetinfo('muhash') assert_equal(res8, res9) index_node.generate(1) self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash')) res10 = index_node.gettxoutsetinfo('muhash') assert(res8['txouts'] < res10['txouts'])
def test_doublespend_tree(self): """Doublespend of a big tree of transactions""" initial_nValue = 50 * COIN tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _total_txs=None): if _total_txs is None: _total_txs = [0] if _total_txs[0] >= max_txs: return txout_value = (initial_value - fee) // tree_width if txout_value < fee: return vout = [ CTxOut(txout_value, CScript([i + 1])) for i in range(tree_width) ] tx = CTransaction() tx.vin = [CTxIn(prevout, nSequence=0)] tx.vout = vout tx_hex = txToHex(tx) assert (len(tx.serialize()) < 100000) txid = self.nodes[0].sendrawtransaction(tx_hex, True) yield tx _total_txs[0] += 1 txid = int(txid, 16) for i, txout in enumerate(tx.vout): for x in branch(COutPoint(txid, i), txout_value, max_txs, tree_width=tree_width, fee=fee, _total_txs=_total_txs): yield x fee = int(0.0001 * COIN) n = MAX_REPLACEMENT_LIMIT tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) assert_equal(len(tree_txs), n) # Attempt double-spend, will fail because too little fee paid dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] dbl_tx.vout = [CTxOut(initial_nValue - fee * n, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) # 1 BTC fee is enough dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] dbl_tx.vout = [ CTxOut(initial_nValue - fee * n - 1 * COIN, CScript([1] * 35)) ] dbl_tx_hex = txToHex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) mempool = self.nodes[0].getrawmempool() for tx in tree_txs: tx.rehash() assert (tx.hash not in mempool) # Try again, but with more total transactions than the "max txs # double-spent at once" anti-DoS limit. for n in (MAX_REPLACEMENT_LIMIT + 1, MAX_REPLACEMENT_LIMIT * 2): fee = int(0.0001 * COIN) tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) assert_equal(len(tree_txs), n) dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] dbl_tx.vout = [ CTxOut(initial_nValue - 2 * fee * n, CScript([1] * 35)) ] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) for tx in tree_txs: tx.rehash() self.nodes[0].getrawtransaction(tx.hash)
def run_test(self): self.nodes[0].generate(161) #block 161 self.log.info( "Verify sigops are counted in GBT with pre-BIP141 rules before the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({}) assert (tmpl['sizelimit'] == 1000000) assert ('weightlimit' not in tmpl) assert (tmpl['sigoplimit'] == 20000) assert (tmpl['transactions'][0]['hash'] == txid) assert (tmpl['transactions'][0]['sigops'] == 2) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert (tmpl['sizelimit'] == 1000000) assert ('weightlimit' not in tmpl) assert (tmpl['sigoplimit'] == 20000) assert (tmpl['transactions'][0]['hash'] == txid) assert (tmpl['transactions'][0]['sigops'] == 2) self.nodes[0].generate(1) #block 162 balance_presetup = self.nodes[0].getbalance() self.pubkey = [] p2sh_ids = [ ] # p2sh_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE embedded in p2sh wit_ids = [ ] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness for i in range(3): newaddress = self.nodes[i].getnewaddress() self.pubkey.append( self.nodes[i].getaddressinfo(newaddress)["pubkey"]) multiscript = CScript([ OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG ]) p2sh_addr = self.nodes[i].addwitnessaddress(newaddress) bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False) p2sh_ms_addr = self.nodes[i].addmultisigaddress( 1, [self.pubkey[-1]], '', 'p2sh-segwit')['address'] bip173_ms_addr = self.nodes[i].addmultisigaddress( 1, [self.pubkey[-1]], '', 'bech32')['address'] assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1])) assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1])) assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript)) assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript)) p2sh_ids.append([]) wit_ids.append([]) for v in range(2): p2sh_ids[i].append([]) wit_ids[i].append([]) for i in range(5): for n in range(3): for v in range(2): wit_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999"))) p2sh_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999"))) self.nodes[0].generate(1) #block 163 sync_blocks(self.nodes) # Make sure all nodes recognize the transactions as theirs assert_equal(self.nodes[0].getbalance(), balance_presetup - 60 * 50 + 20 * Decimal("49.999") + 50) assert_equal(self.nodes[1].getbalance(), 20 * Decimal("49.999")) assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999")) self.nodes[0].generate(260) #block 423 sync_blocks(self.nodes) self.log.info( "Verify witness txs are skipped for mining before the fork") self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) #block 424 self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) #block 425 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) #block 426 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) #block 427 self.log.info( "Verify unsigned p2sh witness txs without a redeem script are invalid" ) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V0][1], False) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V1][1], False) self.nodes[2].generate(4) # blocks 428-431 self.log.info( "Verify previous witness txs skipped for mining can now be mined") assert_equal(len(self.nodes[2].getrawmempool()), 4) block = self.nodes[2].generate( 1) #block 432 (first block with new rules; 432 = 144 * 3) sync_blocks(self.nodes) assert_equal(len(self.nodes[2].getrawmempool()), 0) segwit_tx_list = self.nodes[2].getblock(block[0])["tx"] assert_equal(len(segwit_tx_list), 5) self.log.info( "Verify default node can't accept txs with missing witness") # unsigned, no scriptsig self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V0][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V1][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False) # unsigned with redeem script self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False, witness_script(False, self.pubkey[0])) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False, witness_script(True, self.pubkey[0])) self.log.info( "Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag" ) assert (self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False)) assert (self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock( block[0], False)) for i in range(len(segwit_tx_list)): tx = FromHex( CTransaction(), self.nodes[2].gettransaction(segwit_tx_list[i])["hex"]) assert (self.nodes[2].getrawtransaction(segwit_tx_list[i]) != self.nodes[0].getrawtransaction(segwit_tx_list[i])) assert (self.nodes[1].getrawtransaction( segwit_tx_list[i], 0) == self.nodes[2].getrawtransaction(segwit_tx_list[i])) assert (self.nodes[0].getrawtransaction(segwit_tx_list[i]) != self.nodes[2].gettransaction(segwit_tx_list[i])["hex"]) assert (self.nodes[1].getrawtransaction( segwit_tx_list[i]) == self.nodes[2].gettransaction( segwit_tx_list[i])["hex"]) assert (self.nodes[0].getrawtransaction( segwit_tx_list[i]) == bytes_to_hex_str( tx.serialize_without_witness())) self.log.info( "Verify witness txs without witness data are invalid after the fork" ) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch) (code 64)', wit_ids[NODE_2][WIT_V0][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness) (code 64)', wit_ids[NODE_2][WIT_V1][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch) (code 64)', p2sh_ids[NODE_2][WIT_V0][2], sign=False, redeem_script=witness_script(False, self.pubkey[2])) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness) (code 64)', p2sh_ids[NODE_2][WIT_V1][2], sign=False, redeem_script=witness_script(True, self.pubkey[2])) self.log.info("Verify default node can now use witness txs") self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) #block 432 self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) #block 433 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) #block 434 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) #block 435 self.log.info( "Verify sigops are counted in GBT with BIP141 rules after the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert ( tmpl['sizelimit'] >= 3999577 ) # actual maximum size is lower due to minimum mandatory non-witness data assert (tmpl['weightlimit'] == 4000000) assert (tmpl['sigoplimit'] == 80000) assert (tmpl['transactions'][0]['txid'] == txid) assert (tmpl['transactions'][0]['sigops'] == 8) self.nodes[0].generate(1) # Mine a block to clear the gbt cache self.log.info( "Non-segwit miners are able to use GBT response after activation.") # Create a 3-tx chain: tx1 (non-segwit input, paying to a segwit output) -> # tx2 (segwit input, paying to a non-segwit output) -> # tx3 (non-segwit input, paying to a non-segwit output). # tx1 is allowed to appear in the block, but no others. txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996")) hex_tx = self.nodes[0].gettransaction(txid)['hex'] tx = FromHex(CTransaction(), hex_tx) assert (tx.wit.is_null()) # This should not be a segwit input assert (txid1 in self.nodes[0].getrawmempool()) # Now create tx2, which will spend from txid1. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) tx.vout.append( CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = FromHex(CTransaction(), tx2_hex) assert (not tx.wit.is_null()) # Now create tx3, which will spend from txid2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b"")) tx.vout.append( CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee tx.calc_sha256() txid3 = self.nodes[0].sendrawtransaction(ToHex(tx)) assert (tx.wit.is_null()) assert (txid3 in self.nodes[0].getrawmempool()) # Now try calling getblocktemplate() without segwit support. template = self.nodes[0].getblocktemplate() # Check that tx1 is the only transaction of the 3 in the template. template_txids = [t['txid'] for t in template['transactions']] assert (txid2 not in template_txids and txid3 not in template_txids) assert (txid1 in template_txids) # Check that running with segwit support results in all 3 being included. template = self.nodes[0].getblocktemplate({"rules": ["segwit"]}) template_txids = [t['txid'] for t in template['transactions']] assert (txid1 in template_txids) assert (txid2 in template_txids) assert (txid3 in template_txids) # Check that wtxid is properly reported in mempool entry assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True)) # Mine a block to clear the gbt cache again. self.nodes[0].generate(1) self.log.info( "Verify behaviour of importaddress, addwitnessaddress and listunspent" ) # Some public keys to be used later pubkeys = [ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97 "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66 "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ ] # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn") uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"] self.nodes[0].importprivkey( "cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR") compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"] assert ((self.nodes[0].getaddressinfo( uncompressed_spendable_address[0])['iscompressed'] == False)) assert ((self.nodes[0].getaddressinfo( compressed_spendable_address[0])['iscompressed'] == True)) self.nodes[0].importpubkey(pubkeys[0]) compressed_solvable_address = [key_to_p2pkh(pubkeys[0])] self.nodes[0].importpubkey(pubkeys[1]) compressed_solvable_address.append(key_to_p2pkh(pubkeys[1])) self.nodes[0].importpubkey(pubkeys[2]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])] spendable_anytime = [ ] # These outputs should be seen anytime after importprivkey and addmultisigaddress spendable_after_importaddress = [ ] # These outputs should be seen after importaddress solvable_after_importaddress = [ ] # These outputs should be seen after importaddress but not spendable unsolvable_after_importaddress = [ ] # These outputs should be unsolvable after importaddress solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [ compressed_spendable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_solvable_address[0], compressed_solvable_address[1] ])['address']) unknown_address = [ "mtKKyoHabkk6e4ppT7NaM7THqPUt7AzPrT", "2NDP3jLWAFT8NDAiUa9qiE6oBt2awmMq7Dx" ] # Test multisig_without_privkey # We have 2 public keys without private keys, use addmultisigaddress to add to wallet. # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address. multisig_without_privkey_address = self.nodes[0].addmultisigaddress( 2, [pubkeys[3], pubkeys[4]])['address'] script = CScript([ OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG ]) solvable_after_importaddress.append( CScript([OP_HASH160, hash160(script), OP_EQUAL])) for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with compressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with compressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with uncompressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): # Multisig without private is not seen after addmultisigaddress, but seen after importaddress [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) solvable_after_importaddress.extend( [bare, p2sh, p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress solvable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress solvable_after_importaddress.extend([bare, p2sh]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) op1 = CScript([OP_1]) op0 = CScript([OP_0]) # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V unsolvable_address = [ "mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V", "2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe", script_to_p2sh(op1), script_to_p2sh(op0) ] unsolvable_address_key = hex_str_to_bytes( "02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D" ) unsolvablep2pkh = CScript([ OP_DUP, OP_HASH160, hash160(unsolvable_address_key), OP_EQUALVERIFY, OP_CHECKSIG ]) unsolvablep2wshp2pkh = CScript([OP_0, sha256(unsolvablep2pkh)]) p2shop0 = CScript([OP_HASH160, hash160(op0), OP_EQUAL]) p2wshop1 = CScript([OP_0, sha256(op1)]) unsolvable_after_importaddress.append(unsolvablep2pkh) unsolvable_after_importaddress.append(unsolvablep2wshp2pkh) unsolvable_after_importaddress.append( op1) # OP_1 will be imported as script unsolvable_after_importaddress.append(p2wshop1) unseen_anytime.append( op0 ) # OP_0 will be imported as P2SH address with no script provided unsolvable_after_importaddress.append(p2shop0) spendable_txid = [] solvable_txid = [] spendable_txid.append( self.mine_and_test_listunspent(spendable_anytime, 2)) solvable_txid.append( self.mine_and_test_listunspent(solvable_anytime, 1)) self.mine_and_test_listunspent( spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0) importlist = [] for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): bare = hex_str_to_bytes(v['hex']) importlist.append(bytes_to_hex_str(bare)) importlist.append( bytes_to_hex_str(CScript([OP_0, sha256(bare)]))) else: pubkey = hex_str_to_bytes(v['pubkey']) p2pk = CScript([pubkey, OP_CHECKSIG]) p2pkh = CScript([ OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG ]) importlist.append(bytes_to_hex_str(p2pk)) importlist.append(bytes_to_hex_str(p2pkh)) importlist.append( bytes_to_hex_str(CScript([OP_0, hash160(pubkey)]))) importlist.append( bytes_to_hex_str(CScript([OP_0, sha256(p2pk)]))) importlist.append( bytes_to_hex_str(CScript([OP_0, sha256(p2pkh)]))) importlist.append(bytes_to_hex_str(unsolvablep2pkh)) importlist.append(bytes_to_hex_str(unsolvablep2wshp2pkh)) importlist.append(bytes_to_hex_str(op1)) importlist.append(bytes_to_hex_str(p2wshop1)) for i in importlist: # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC # exceptions and continue. try_rpc( -4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True) self.nodes[0].importaddress( script_to_p2sh(op0)) # import OP_0 as address only self.nodes[0].importaddress( multisig_without_privkey_address) # Test multisig_without_privkey spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # addwitnessaddress should refuse to return a witness address if an uncompressed key is used # note that no witness address should be returned by unsolvable addresses for i in uncompressed_spendable_address + uncompressed_solvable_address + unknown_address + unsolvable_address: assert_raises_rpc_error( -4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) # addwitnessaddress should return a witness addresses even if keys are not in the wallet self.nodes[0].addwitnessaddress(multisig_without_privkey_address) for i in compressed_spendable_address + compressed_solvable_address: witaddress = self.nodes[0].addwitnessaddress(i) # addwitnessaddress should return the same address if it is a known P2SH-witness address assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress)) spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # Repeat some tests. This time we don't add witness scripts with importaddress # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH") uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"] self.nodes[0].importprivkey( "cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw") compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"] self.nodes[0].importpubkey(pubkeys[5]) compressed_solvable_address = [key_to_p2pkh(pubkeys[5])] self.nodes[0].importpubkey(pubkeys[6]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])] spendable_after_addwitnessaddress = [ ] # These outputs should be seen after importaddress solvable_after_addwitnessaddress = [ ] # These outputs should be seen after importaddress but not spendable unseen_anytime = [] # These outputs should never be seen solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_solvable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) premature_witaddress = [] for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after addwitnessaddress spendable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh]) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH are always spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen unseen_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): # P2WSH multisig without private key are seen after addwitnessaddress [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) solvable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh]) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable solvable_anytime.extend([p2wpkh, p2sh_p2wpkh]) self.mine_and_test_listunspent(spendable_anytime, 2) self.mine_and_test_listunspent(solvable_anytime, 1) self.mine_and_test_listunspent( spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0) # addwitnessaddress should refuse to return a witness address if an uncompressed key is used # note that a multisig address returned by addmultisigaddress is not solvable until it is added with importaddress # premature_witaddress are not accepted until the script is added with addwitnessaddress first for i in uncompressed_spendable_address + uncompressed_solvable_address + premature_witaddress: # This will raise an exception assert_raises_rpc_error( -4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) # after importaddress it should pass addwitnessaddress v = self.nodes[0].getaddressinfo(compressed_solvable_address[1]) self.nodes[0].importaddress(v['hex'], "", False, True) for i in compressed_spendable_address + compressed_solvable_address + premature_witaddress: witaddress = self.nodes[0].addwitnessaddress(i) assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress)) spendable_txid.append( self.mine_and_test_listunspent( spendable_after_addwitnessaddress + spendable_anytime, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_after_addwitnessaddress + solvable_anytime, 1)) self.mine_and_test_listunspent(unseen_anytime, 0) # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works v1_addr = program_to_witness(1, [3, 5]) v1_tx = self.nodes[0].createrawtransaction( [getutxo(spendable_txid[0])], {v1_addr: 1}) v1_decoded = self.nodes[1].decoderawtransaction(v1_tx) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305") # Check that spendable outputs are really spendable self.create_and_mine_tx_from_txids(spendable_txid) # import all the private keys so solvable addresses become spendable self.nodes[0].importprivkey( "cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb") self.nodes[0].importprivkey( "cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97") self.nodes[0].importprivkey( "91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV") self.nodes[0].importprivkey( "cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd") self.nodes[0].importprivkey( "cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66") self.nodes[0].importprivkey( "cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K") self.create_and_mine_tx_from_txids(solvable_txid) # Test that importing native P2WPKH/P2WSH scripts works for use_p2wsh in [False, True]: if use_p2wsh: scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a" transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000" else: scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87" transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000" self.nodes[1].importaddress(scriptPubKey, "", False) rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex'] rawtxfund = self.nodes[1].signrawtransactionwithwallet( rawtxfund)["hex"] txid = self.nodes[1].sendrawtransaction(rawtxfund) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) # Assert it is properly saved self.stop_node(1) self.start_node(1) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
def run_test(self): node = self.nodes[0] self.log.info('Start with empty mempool, and 200 blocks') self.mempool_size = 0 assert_equal(node.getblockcount(), 200) assert_equal(node.getmempoolinfo()['size'], self.mempool_size) coins = node.listunspent() self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar')) assert_raises_rpc_error(-8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22'])) assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar'])) self.log.info('A transaction already in the blockchain') coin = coins.pop() # Pick a random coin(base) to spend raw_tx_in_block = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout']}], outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}], ))['hex'] txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, maxfeerate=0) node.generate(1) self.mempool_size = 0 self.check_mempool_result( result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known'}], rawtxs=[raw_tx_in_block], ) self.log.info('A transaction not in the mempool') fee = 0.00000700 raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{"txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}], # RBF is used later outputs=[{node.getnewaddress(): 0.3 - fee}], ))['hex'] tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': True}], rawtxs=[raw_tx_0], ) self.log.info('A final transaction not in the mempool') coin = coins.pop() # Pick a random coin(base) to spend raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL outputs=[{node.getnewaddress(): 0.025}], locktime=node.getblockcount() + 2000, # Can be anything ))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': True}], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0) self.mempool_size += 1 self.log.info('A transaction in the mempool') node.sendrawtransaction(hexstring=raw_tx_0) self.mempool_size += 1 self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': '18: txn-already-in-mempool'}], rawtxs=[raw_tx_0], ) self.log.info('A transaction that replaces a mempool transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(fee * COIN) # Double the fee tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF raw_tx_0 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': True}], rawtxs=[raw_tx_0], ) self.log.info('A transaction that conflicts with an unconfirmed tx') # Send the transaction that replaces the mempool transaction and opts out of replaceability node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0) # take original raw_tx_0 tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee # skip re-signing the tx self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict'}], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) self.log.info('A transaction with missing inputs, that never existed') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14) # skip re-signing the tx self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with missing inputs, that existed once in the past') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend raw_tx_1 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex'] txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0) # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[ {'txid': txid_0, 'vout': 0}, {'txid': txid_1, 'vout': 0}, ], outputs=[{node.getnewaddress(): 0.1}] ))['hex'] txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, maxfeerate=0) node.generate(1) self.mempool_size = 0 # Now see if we can add the coins back to the utxo set by sending the exact txs again self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[raw_tx_0], ) self.check_mempool_result( result_expected=[{'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[raw_tx_1], ) self.log.info('Create a signed "reference" tx for later use') raw_tx_reference = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': txid_spend_both, 'vout': 0}], outputs=[{node.getnewaddress(): 0.05}], ))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) # Reference tx should be valid on itself self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': True}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with no outputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [] # Skip re-signing the transaction for context independent checks from now on # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(tx.serialize().hex())['hex']))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-empty'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A really large transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize())) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with negative output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue *= -1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-negative'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with too large output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue = 21000000 * COIN + 1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-toolarge'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with too large sum of output values') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [tx.vout[0]] * 2 tx.vout[0].nValue = 21000000 * COIN self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-txouttotal-toolarge'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with duplicate inputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0]] * 2 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-inputs-duplicate'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A coinbase transaction') # Pick the input of the first tx we signed, so it has to be a coinbase tx raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid']) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: coinbase'}], rawtxs=[tx.serialize().hex()], ) self.log.info('Some nonstandard transactions') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.nVersion = 3 # A version currently non-standard self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: version'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL])) num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy tx.vout = [output_p2sh_burn] * num_scripts self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0] = output_p2sh_burn tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff']) tx.vout = [tx.vout[0]] * 2 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: multi-op-return'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A timelocked transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].nSequence -= 1 # Should be non-max, so locktime is not ignored tx.nLockTime = node.getblockcount() + 1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-final'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction that is locked by BIP68 sequence logic') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one # Can skip re-signing the tx because of early rejection self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}], rawtxs=[tx.serialize().hex()], maxfeerate=0, )
def create_block(self, prev_hash, staking_prevouts, height, node_n, s_address, fInvalid=0): api = self.nodes[node_n] # Get current time current_time = int(time.time()) nTime = current_time & 0xfffffff0 # Create coinbase TX coinbase = create_coinbase(height) coinbase.vout[0].nValue = 0 coinbase.vout[0].scriptPubKey = b"" coinbase.nTime = nTime coinbase.rehash() # Create Block with coinbase block = create_block(int(prev_hash, 16), coinbase, nTime) # Find valid kernel hash - Create a new private key used for block signing. if not block.solve_stake(staking_prevouts): raise Exception("Not able to solve for any prev_outpoint") # Create coinstake TX amount, prev_time, prevScript = staking_prevouts[block.prevoutStake] outNValue = int(amount + 250 * COIN) stake_tx_unsigned = CTransaction() stake_tx_unsigned.nTime = block.nTime stake_tx_unsigned.vin.append(CTxIn(block.prevoutStake)) stake_tx_unsigned.vin[0].nSequence = 0xffffffff stake_tx_unsigned.vout.append(CTxOut()) stake_tx_unsigned.vout.append( CTxOut(outNValue, hex_str_to_bytes(prevScript))) if fInvalid == 1: # Create a new private key and get the corresponding public key block_sig_key = CECKey() block_sig_key.set_secretbytes(hash256(pack('<I', 0xffff))) pubkey = block_sig_key.get_pubkey() stake_tx_unsigned.vout[1].scriptPubKey = CScript( [pubkey, OP_CHECKSIG]) else: # Export the staking private key to sign the block with it privKey, compressed = wif_to_privkey(api.dumpprivkey(s_address)) block_sig_key = CECKey() block_sig_key.set_compressed(compressed) block_sig_key.set_secretbytes(bytes.fromhex(privKey)) # check the address addy = key_to_p2pkh(bytes_to_hex_str(block_sig_key.get_pubkey()), False, True) assert (addy == s_address) if fInvalid == 2: # add a new output with 100 coins from the pot new_key = CECKey() new_key.set_secretbytes(hash256(pack('<I', 0xffff))) pubkey = new_key.get_pubkey() stake_tx_unsigned.vout.append( CTxOut(100 * COIN, CScript([pubkey, OP_CHECKSIG]))) stake_tx_unsigned.vout[1].nValue = outNValue - 100 * COIN # Sign coinstake TX and add it to the block stake_tx_signed_raw_hex = api.signrawtransaction( bytes_to_hex_str(stake_tx_unsigned.serialize()))['hex'] stake_tx_signed = CTransaction() stake_tx_signed.deserialize( BytesIO(hex_str_to_bytes(stake_tx_signed_raw_hex))) block.vtx.append(stake_tx_signed) # Get correct MerkleRoot and rehash block block.hashMerkleRoot = block.calc_merkle_root() block.rehash() # sign block with block signing key and return it block.sign_block(block_sig_key) return block