def get_tx(self): tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256 + 1, 0), b"", 0xffffffff)) tx.vin.append(self.valid_txin) tx.vout.append(CTxOut(1, basic_p2sh)) tx.calc_sha256() return tx
def get_tx(self): tx = CTransaction() tx.vin.append(self.valid_txin) tx.vin.append(self.valid_txin) tx.vout.append(CTxOut(1, basic_p2sh)) tx.calc_sha256() return tx
def get_tx(self): num_indices = len(self.spend_tx.vin) bad_idx = num_indices + 100 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256, bad_idx), b"", 0xffffffff)) tx.vout.append(CTxOut(0, basic_p2sh)) tx.calc_sha256() return tx
def _zmq_test(self): num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = self.hashtx.receive() # Should receive the coinbase raw transaction. hex = self.rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated block hash. hash = self.hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) # Should receive the generated raw block. block = self.rawblock.receive() assert_equal(genhashes[x], hash256(block[:80]).hex()) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = self.hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = self.rawtx.receive() assert_equal(payment_txid, hash256(hex).hex()) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ {"type": "pubhashblock", "address": ADDRESS, "hwm": 1000}, {"type": "pubhashtx", "address": ADDRESS, "hwm": 1000}, {"type": "pubrawblock", "address": ADDRESS, "hwm": 1000}, {"type": "pubrawtx", "address": ADDRESS, "hwm": 1000}, ]) assert_equal(self.nodes[1].getzmqnotifications(), [])
def _zmq_test(self): num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generate(num_blocks) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = self.hashtx.receive() # Should receive the coinbase raw transaction. hex = self.rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, bytes_to_hex_str(txid)) # Should receive the generated block hash. hash = bytes_to_hex_str(self.hashblock.receive()) assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([bytes_to_hex_str(txid)], self.nodes[1].getblock(hash)["tx"]) # Should receive the generated raw block. block = self.rawblock.receive() # 79 bytes, last byte is saying block solution is "", ellide this for hash assert_equal(genhashes[x], bytes_to_hex_str(hash256(block[:78]))) self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = self.hashtx.receive() assert_equal(payment_txid, bytes_to_hex_str(txid)) # Should receive the broadcasted raw transaction. hex = self.rawtx.receive() assert_equal(payment_txid, bytes_to_hex_str(hash256(hex)))
def assert_tx_format_also_signed(self, utxo, segwit): raw = self.nodes[0].createrawtransaction( [{"txid": utxo["txid"], "vout": utxo["vout"]}], [{self.unknown_addr: "49.9"}, {"fee": "0.1"}] ) unsigned_decoded = self.nodes[0].decoderawtransaction(raw) assert_equal(len(unsigned_decoded["vin"]), 1) assert('txinwitness' not in unsigned_decoded["vin"][0]) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw))) assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0)) assert_equal(len(tx.vin), len(unsigned_decoded["vin"])) assert_equal(len(tx.vout), len(unsigned_decoded["vout"])) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, raw) # Now sign and repeat tests signed_raw = self.nodes[0].signrawtransactionwithwallet(raw)["hex"] signed_decoded = self.nodes[0].decoderawtransaction(signed_raw) assert_equal(len(signed_decoded["vin"]), 1) assert(("txinwitness" in signed_decoded["vin"][0]) == segwit) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(signed_raw))) assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0)) assert_equal(bytes_to_hex_str(tx.vin[0].scriptSig), signed_decoded["vin"][0]["scriptSig"]["hex"]) # test witness if segwit: wit_decoded = signed_decoded["vin"][0]["txinwitness"] for i in range(len(wit_decoded)): assert_equal(bytes_to_hex_str(tx.wit.vtxinwit[0].scriptWitness.stack[i]), wit_decoded[i]) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, signed_raw) txid = self.nodes[0].sendrawtransaction(serialized) nodetx = self.nodes[0].getrawtransaction(txid, 1) assert_equal(nodetx["txid"], tx.rehash()) # cross-check wtxid report from node wtxid = bytes_to_hex_str(ser_uint256(tx.calc_sha256(True))[::-1]) assert_equal(nodetx["wtxid"], wtxid) assert_equal(nodetx["hash"], wtxid) # witness hash stuff assert_equal(nodetx["withash"], tx.calc_witness_hash()) return (txid, wtxid)
def get_tx(self): tx = CTransaction() tx.calc_sha256() return tx
def get_tx(self): tx = CTransaction() tx.vin.append(self.valid_txin) tx.calc_sha256() return tx
def get_tx(self): tx = CTransaction() tx.vin.append(self.valid_txin) tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE]))) tx.calc_sha256() return tx
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( 100, self.nodes[0].get_deterministic_priv_key().address) # 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 withold until all dependend transactions # are sent out and in the orphan cache 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=b'\x51')) pad_tx(tx_withhold) 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=b'\x51')] * 3 pad_tx(tx_orphan_1) 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=b'\x51')) pad_tx(tx_orphan_2_no_fee) # 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=b'\x51')) tx_orphan_2_valid.calc_sha256() pad_tx(tx_orphan_2_valid) # 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=b'\x51')) pad_tx(tx_orphan_2_invalid) 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) # Mempool should be empty assert_equal(0, node.getmempoolinfo()['size']) # p2ps[1] is still connected assert_equal(2, len(node.getpeerinfo())) 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 # The valid transaction (with sufficient fee) tx_orphan_2_valid, ] } # 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) # p2ps[1] is no longer connected wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) 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, self.extra_args[0] + ['-enablebip61=0', '-persistmempool=0']) self.reconnect_p2p(num_connections=1) node.p2p.send_txs_and_test( [tx1], node, success=False, reject_reason= "{} from peer=0 was not accepted: mandatory-script-verify-flag-failed (Invalid OP_IF construction) (code 16)" .format(tx1.hash), expect_disconnect=True)
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.nVersion = 0x20000000 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) with node.assert_debug_log(expected_msgs=[ "{} from peer=0 was not accepted: mandatory-script-verify-flag-failed (Invalid OP_IF construction) (code 16)".format(tx1.hash), "disconnecting peer=0", ]): 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 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)]) 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]) 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 _zmq_test(self): num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = self.hashtx.receive() # Should receive the coinbase raw transaction. hex = self.rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated block hash. hash = self.hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) # Should receive the generated raw block. block = self.rawblock.receive() assert_equal(genhashes[x], hash256(block[:80]).hex()) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = self.hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = self.rawtx.receive() assert_equal(payment_txid, hash256(hex).hex()) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ { "type": "pubhashblock", "address": ADDRESS, "hwm": 1000 }, { "type": "pubhashtx", "address": ADDRESS, "hwm": 1000 }, { "type": "pubrawblock", "address": ADDRESS, "hwm": 1000 }, { "type": "pubrawtx", "address": ADDRESS, "hwm": 1000 }, ]) assert_equal(self.nodes[1].getzmqnotifications(), [])
def test_basic(self): # Invalid zmq arguments don't take down the node, see #17185. self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"]) address = 'tcp://127.0.0.1:28332' services = ["hashblock", "hashtx", "rawblock", "rawtx"] if self.is_wallet_compiled(): services += ["hashwallettx", "rawwallettx"] subs = self.setup_zmq_test([(topic, address) for topic in services]) hashblock = subs[0] hashtx = subs[1] rawblock = subs[2] rawtx = subs[3] if self.is_wallet_compiled(): hashwallettx = subs[-2] rawwallettx = subs[-1] if self.is_wallet_compiled(): self.sync_all() # Flush initial wallettx events before we begin while True: try: topic, body, seq = hashwallettx.socket.recv_multipart() except zmq.ZMQError: break subscriber = { b'hashwallettx-block': hashwallettx, b'rawwallettx-block': rawwallettx }[topic] assert_equal(struct.unpack('<I', seq)[-1], subscriber.sequence) subscriber.sequence += 1 num_blocks = 5 self.log.info( f"Generate {num_blocks} blocks (and {num_blocks} coinbase txes)") if self.is_wallet_compiled(): genhashes = self.generate(self.nodes[0], num_blocks) else: genhashes = self.generatetoaddress(self.nodes[0], num_blocks, ADDRESS_BCRT1_UNSPENDABLE) for x in range(num_blocks): # Should receive the coinbase txid. txid = hashtx.receive() # Should receive the coinbase raw transaction. hex = rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated raw block. block = rawblock.receive() assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) if self.is_wallet_compiled(): # Should receive wallet tx wallettxid = hashwallettx.receive(b"hashwallettx-block") wallethex = rawwallettx.receive(b"rawwallettx-block") wallettx = CTransaction() wallettx.deserialize(BytesIO(wallethex)) wallettx.calc_sha256() assert_equal(wallettx.hash, wallettxid.hex()) # Should receive the generated block hash. hash = hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = rawtx.receive() assert_equal(payment_txid, hash256_reversed(hex).hex()) # Mining the block with this tx should result in second notification # after coinbase tx notification self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE) hashtx.receive() txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) if self.is_wallet_compiled(): wallettxid = hashwallettx.receive(b"hashwallettx-mempool") wallethex = rawwallettx.receive(b"rawwallettx-mempool") assert_equal(hash256_reversed(wallethex), wallettxid) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ { "type": "pubhashblock", "address": address, "hwm": 1000 }, { "type": "pubhashtx", "address": address, "hwm": 1000 }, ] + ([{ "type": "pubhashwallettx", "address": address, "hwm": 1000 }] if self.is_wallet_compiled() else []) + [ { "type": "pubrawblock", "address": address, "hwm": 1000 }, { "type": "pubrawtx", "address": address, "hwm": 1000 }, ] + ([{ "type": "pubrawwallettx", "address": address, "hwm": 1000 }] if self.is_wallet_compiled() else []) + []) assert_equal(self.nodes[1].getzmqnotifications(), [])
def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node self.block_heights = {} self.coinbase_key = ECKey() self.coinbase_key.generate() self.coinbase_pubkey = self.coinbase_key.get_pubkey().get_bytes() self.tip = None self.blocks = {} self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 self.spendable_outputs = [] # Create a new block b0 = self.next_block(0) self.save_spendable_output() self.send_blocks([b0]) # Allow the block to mature blocks = [] for i in range(99): blocks.append(self.next_block(5000 + i)) self.save_spendable_output() self.send_blocks(blocks) # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(33): out.append(self.get_spendable_output()) # Start by building a couple of blocks on top (which output is spent is # in parentheses): # genesis -> b1 (0) -> b2 (1) b1 = self.next_block(1, spend=out[0]) self.save_spendable_output() b2 = self.next_block(2, spend=out[1]) self.save_spendable_output() self.send_blocks([b1, b2]) # Fork like this: # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) # # Nothing should happen at this point. We saw b2 first so it takes # priority. self.log.info("Don't reorg to a chain of the same length") self.move_tip(1) b3 = self.next_block(3, spend=out[1]) txout_b3 = b3.vtx[1] self.send_blocks([b3], False) # Now we add another block to make the alternative chain longer. # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) -> b4 (2) self.log.info("Reorg to a longer chain") b4 = self.next_block(4, spend=out[2]) self.send_blocks([b4]) # ... and back to the first chain. # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b3 (1) -> b4 (2) self.move_tip(2) b5 = self.next_block(5, spend=out[2]) self.save_spendable_output() self.send_blocks([b5], False) self.log.info("Reorg back to the original chain") b6 = self.next_block(6, spend=out[3]) self.send_blocks([b6], True) # Try to create a fork that double-spends # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b7 (2) -> b8 (4) # \-> b3 (1) -> b4 (2) self.log.info( "Reject a chain with a double spend, even if it is longer") self.move_tip(5) b7 = self.next_block(7, spend=out[2]) self.send_blocks([b7], False) b8 = self.next_block(8, spend=out[4]) self.send_blocks([b8], False, reconnect=True) # Try to create a block that has too much fee # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b9 (4) # \-> b3 (1) -> b4 (2) self.log.info( "Reject a block where the miner creates too much coinbase reward") self.move_tip(6) b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1) self.send_blocks([b9], success=False, reject_reason='bad-cb-amount', reconnect=True) # Create a fork that ends in a block with too much fee (the one that causes the reorg) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b10 (3) -> b11 (4) # \-> b3 (1) -> b4 (2) self.log.info( "Reject a chain where the miner creates too much coinbase reward, even if the chain is longer") self.move_tip(5) b10 = self.next_block(10, spend=out[3]) self.send_blocks([b10], False) b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1) self.send_blocks([b11], success=False, reject_reason='bad-cb-amount', reconnect=True) # Try again, but with a valid fork first # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b14 (5) # \-> b3 (1) -> b4 (2) self.log.info( "Reject a chain where the miner creates too much coinbase reward, even if the chain is longer (on a forked chain)") self.move_tip(5) b12 = self.next_block(12, spend=out[3]) self.save_spendable_output() b13 = self.next_block(13, spend=out[4]) self.save_spendable_output() b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1) self.send_blocks([b12, b13, b14], success=False, reject_reason='bad-cb-amount', reconnect=True) # New tip should be b13. assert_equal(node.getbestblockhash(), b13.hash) self.log.info("Skipped sigops tests") # tests were moved to feature_block_sigops.py self.move_tip(13) b15 = self.next_block(15) self.save_spendable_output() self.send_blocks([b15], True) # Attempt to spend a transaction created on a different fork # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1]) # \-> b3 (1) -> b4 (2) self.log.info("Reject a block with a spend from a re-org'ed out tx") self.move_tip(15) b17 = self.next_block(17, spend=txout_b3) self.send_blocks([b17], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Attempt to spend a transaction created on a different fork (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) # \-> b18 (b3.vtx[1]) -> b19 (6) # \-> b3 (1) -> b4 (2) self.log.info( "Reject a block with a spend from a re-org'ed out tx (on a forked chain)") self.move_tip(13) b18 = self.next_block(18, spend=txout_b3) self.send_blocks([b18], False) b19 = self.next_block(19, spend=out[6]) self.send_blocks([b19], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Attempt to spend a coinbase at depth too low # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7) # \-> b3 (1) -> b4 (2) self.log.info("Reject a block spending an immature coinbase.") self.move_tip(15) b20 = self.next_block(20, spend=out[7]) self.send_blocks([b20], success=False, reject_reason='bad-txns-premature-spend-of-coinbase') # Attempt to spend a coinbase at depth too low (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) # \-> b21 (6) -> b22 (5) # \-> b3 (1) -> b4 (2) self.log.info( "Reject a block spending an immature coinbase (on a forked chain)") self.move_tip(13) b21 = self.next_block(21, spend=out[6]) self.send_blocks([b21], False) b22 = self.next_block(22, spend=out[5]) self.send_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase') # Create a block on either side of LEGACY_MAX_BLOCK_SIZE and make sure its accepted/rejected # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) # \-> b24 (6) -> b25 (7) # \-> b3 (1) -> b4 (2) self.log.info("Accept a block of size LEGACY_MAX_BLOCK_SIZE") self.move_tip(15) b23 = self.next_block(23, spend=out[6]) tx = CTransaction() script_length = LEGACY_MAX_BLOCK_SIZE - len(b23.serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0))) b23 = self.update_block(23, [tx]) # Make sure the math above worked out to produce a max-sized block assert_equal(len(b23.serialize()), LEGACY_MAX_BLOCK_SIZE) self.send_blocks([b23], True) self.save_spendable_output() # Create blocks with a coinbase input script size out of range # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) # \-> ... (6) -> ... (7) # \-> b3 (1) -> b4 (2) self.log.info( "Reject a block with coinbase input script size out of range") self.move_tip(15) b26 = self.next_block(26, spend=out[6]) b26.vtx[0].vin[0].scriptSig = b'\x00' b26.vtx[0].rehash() # update_block causes the merkle root to get updated, even with no new # transactions, and updates the required state. b26 = self.update_block(26, []) self.send_blocks([b26], success=False, reject_reason='bad-cb-length', reconnect=True) # Extend the b26 chain to make sure bitcoind isn't accepting b26 b27 = self.next_block(27, spend=out[7]) self.send_blocks([b27], False) # Now try a too-large-coinbase script self.move_tip(15) b28 = self.next_block(28, spend=out[6]) b28.vtx[0].vin[0].scriptSig = b'\x00' * 101 b28.vtx[0].rehash() b28 = self.update_block(28, []) self.send_blocks([b28], success=False, reject_reason='bad-cb-length', reconnect=True) # Extend the b28 chain to make sure bitcoind isn't accepting b28 b29 = self.next_block(29, spend=out[7]) self.send_blocks([b29], False) # b30 has a max-sized coinbase scriptSig. self.move_tip(23) b30 = self.next_block(30) b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 b30.vtx[0].rehash() b30 = self.update_block(30, []) self.send_blocks([b30], True) self.save_spendable_output() self.log.info("Skipped sigops tests") # tests were moved to feature_block_sigops.py b31 = self.next_block(31) self.save_spendable_output() b33 = self.next_block(33) self.save_spendable_output() b35 = self.next_block(35) self.save_spendable_output() self.send_blocks([b31, b33, b35], True) # Check spending of a transaction in a block which failed to connect # # b6 (3) # b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) # \-> b37 (11) # \-> b38 (11/37) # # save 37's spendable output, but then double-spend out11 to invalidate # the block self.log.info( "Reject a block spending transaction from a block which failed to connect") self.move_tip(35) b37 = self.next_block(37, spend=out[11]) txout_b37 = b37.vtx[1] tx = self.create_and_sign_transaction(out[11], 0) b37 = self.update_block(37, [tx]) self.send_blocks([b37], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # attempt to spend b37's first non-coinbase tx, at which point b37 was # still considered valid self.move_tip(35) b38 = self.next_block(38, spend=txout_b37) self.send_blocks([b38], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) self.log.info("Skipped sigops tests") # tests were moved to feature_block_sigops.py self.move_tip(35) b39 = self.next_block(39) self.save_spendable_output() b41 = self.next_block(41) self.send_blocks([b39, b41], True) # Fork off of b39 to create a constant base again # # b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) # \-> b41 (12) # self.move_tip(39) b42 = self.next_block(42, spend=out[12]) self.save_spendable_output() b43 = self.next_block(43, spend=out[13]) self.save_spendable_output() self.send_blocks([b42, b43], True) # Test a number of really invalid scenarios # # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b44 (14) # \-> ??? (15) # The next few blocks are going to be created "by hand" since they'll do funky things, such as having # the first transaction be non-coinbase, etc. The purpose of b44 is to # make sure this works. self.log.info("Build block 44 manually") height = self.block_heights[self.tip.sha256] + 1 coinbase = create_coinbase(height, self.coinbase_pubkey) b44 = CBlock() b44.nTime = self.tip.nTime + 1 b44.hashPrevBlock = self.tip.sha256 b44.nBits = 0x207fffff b44.vtx.append(coinbase) b44.hashMerkleRoot = b44.calc_merkle_root() b44.solve() self.tip = b44 self.block_heights[b44.sha256] = height self.blocks[44] = b44 self.send_blocks([b44], True) self.log.info("Reject a block with a non-coinbase as the first tx") non_coinbase = self.create_tx(out[15], 0, 1) b45 = CBlock() b45.nTime = self.tip.nTime + 1 b45.hashPrevBlock = self.tip.sha256 b45.nBits = 0x207fffff b45.vtx.append(non_coinbase) b45.hashMerkleRoot = b45.calc_merkle_root() b45.calc_sha256() b45.solve() self.block_heights[b45.sha256] = self.block_heights[ self.tip.sha256] + 1 self.tip = b45 self.blocks[45] = b45 self.send_blocks([b45], success=False, reject_reason='bad-cb-missing', reconnect=True) self.log.info("Reject a block with no transactions") self.move_tip(44) b46 = CBlock() b46.nTime = b44.nTime + 1 b46.hashPrevBlock = b44.sha256 b46.nBits = 0x207fffff b46.vtx = [] b46.hashMerkleRoot = 0 b46.solve() self.block_heights[b46.sha256] = self.block_heights[b44.sha256] + 1 self.tip = b46 assert 46 not in self.blocks self.blocks[46] = b46 self.send_blocks([b46], success=False, reject_reason='bad-cb-missing', reconnect=True) self.log.info("Reject a block with invalid work") self.move_tip(44) b47 = self.next_block(47, solve=False) target = uint256_from_compact(b47.nBits) while b47.sha256 < target: b47.nNonce += 1 b47.rehash() self.send_blocks([b47], False, request_block=False) self.log.info("Reject a block with a timestamp >2 hours in the future") self.move_tip(44) b48 = self.next_block(48, solve=False) b48.nTime = int(time.time()) + 60 * 60 * 3 b48.solve() self.send_blocks([b48], False, request_block=False) self.log.info("Reject a block with invalid merkle hash") self.move_tip(44) b49 = self.next_block(49) b49.hashMerkleRoot += 1 b49.solve() self.send_blocks([b49], success=False, reject_reason='bad-txnmrklroot', reconnect=True) self.log.info("Reject a block with incorrect POW limit") self.move_tip(44) b50 = self.next_block(50) b50.nBits = b50.nBits - 1 b50.solve() self.send_blocks([b50], False, request_block=False, reconnect=True) self.log.info("Reject a block with two coinbase transactions") self.move_tip(44) b51 = self.next_block(51) cb2 = create_coinbase(51, self.coinbase_pubkey) b51 = self.update_block(51, [cb2]) self.send_blocks([b51], success=False, reject_reason='bad-tx-coinbase', reconnect=True) self.log.info("Reject a block with duplicate transactions") self.move_tip(44) b52 = self.next_block(52, spend=out[15]) b52 = self.update_block(52, [b52.vtx[1]]) self.send_blocks([b52], success=False, reject_reason='tx-duplicate', reconnect=True) # Test block timestamps # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) # \-> b54 (15) # self.move_tip(43) b53 = self.next_block(53, spend=out[14]) self.send_blocks([b53], False) self.save_spendable_output() self.log.info("Reject a block with timestamp before MedianTimePast") b54 = self.next_block(54, spend=out[15]) b54.nTime = b35.nTime - 1 b54.solve() self.send_blocks([b54], False, request_block=False) # valid timestamp self.move_tip(53) b55 = self.next_block(55, spend=out[15]) b55.nTime = b35.nTime self.update_block(55, []) self.send_blocks([b55], True) self.save_spendable_output() # Test Merkle tree malleability # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16) # \-> b57 (16) # \-> b56p2 (16) # \-> b56 (16) # # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without # affecting the merkle root of a block, while still invalidating it. # See: src/consensus/merkle.h # # b57 has three txns: coinbase, tx, tx1. The merkle root computation will duplicate tx. # Result: OK # # b56 copies b57 but duplicates tx1 and does not recalculate the block hash. So it has a valid merkle # root but duplicate transactions. # Result: Fails # # b57p2 has six transactions in its merkle tree: # - coinbase, tx, tx1, tx2, tx3, tx4 # Merkle root calculation will duplicate as necessary. # Result: OK. # # b56p2 copies b57p2 but adds both tx3 and tx4. The purpose of the test is to make sure the code catches # duplicate txns that are not next to one another with the "bad-txns-duplicate" error (which indicates # that the error was caught early, avoiding a DOS vulnerability.) # b57 - a good block with 2 txs, don't submit until end self.move_tip(55) b57 = self.next_block(57) tx = self.create_and_sign_transaction(out[16], 1) tx1 = self.create_tx(tx, 0, 1) b57 = self.update_block(57, [tx, tx1]) # b56 - copy b57, add a duplicate tx self.log.info( "Reject a block with a duplicate transaction in the Merkle Tree (but with a valid Merkle Root)") self.move_tip(55) b56 = copy.deepcopy(b57) self.blocks[56] = b56 assert_equal(len(b56.vtx), 3) b56 = self.update_block(56, [b57.vtx[2]]) assert_equal(b56.hash, b57.hash) self.send_blocks([b56], success=False, reject_reason='bad-txns-duplicate', reconnect=True) # b57p2 - a good block with 6 tx'es, don't submit until end self.move_tip(55) b57p2 = self.next_block("57p2") tx = self.create_and_sign_transaction(out[16], 1) tx1 = self.create_tx(tx, 0, 1) tx2 = self.create_tx(tx1, 0, 1) tx3 = self.create_tx(tx2, 0, 1) tx4 = self.create_tx(tx3, 0, 1) b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4]) # b56p2 - copy b57p2, duplicate two non-consecutive tx's self.log.info( "Reject a block with two duplicate transactions in the Merkle Tree (but with a valid Merkle Root)") self.move_tip(55) b56p2 = copy.deepcopy(b57p2) self.blocks["b56p2"] = b56p2 assert_equal(len(b56p2.vtx), 6) b56p2 = self.update_block("b56p2", b56p2.vtx[4:6], reorder=False) assert_equal(b56p2.hash, b57p2.hash) self.send_blocks([b56p2], success=False, reject_reason='bad-txns-duplicate', reconnect=True) self.move_tip("57p2") self.send_blocks([b57p2], True) self.move_tip(57) # The tip is not updated because 57p2 seen first self.send_blocks([b57], False) self.save_spendable_output() # Test a few invalid tx types # # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> ??? (17) # # tx with prevout.n out of range self.log.info( "Reject a block with a transaction with prevout.n out of range") self.move_tip(57) b58 = self.next_block(58, spend=out[17]) tx = CTransaction() assert(len(out[17].vout) < 42) tx.vin.append( CTxIn(COutPoint(out[17].sha256, 42), CScript([OP_TRUE]), 0xffffffff)) tx.vout.append(CTxOut(0, b"")) pad_tx(tx) tx.calc_sha256() b58 = self.update_block(58, [tx]) self.send_blocks([b58], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # tx with output value > input value self.log.info( "Reject a block with a transaction with outputs > inputs") self.move_tip(57) b59 = self.next_block(59) tx = self.create_and_sign_transaction(out[17], 51 * COIN) b59 = self.update_block(59, [tx]) self.send_blocks([b59], success=False, reject_reason='bad-txns-in-belowout', reconnect=True) # reset to good chain self.move_tip(57) b60 = self.next_block(60, spend=out[17]) self.send_blocks([b60], True) self.save_spendable_output() # Test BIP30 # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> b61 (18) # # Blocks are not allowed to contain a transaction whose id matches that of an earlier, # not-fully-spent transaction in the same chain. To test, make identical coinbases; # the second one should be rejected. # self.log.info( "Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)") self.move_tip(60) b61 = self.next_block(61, spend=out[18]) # Equalize the coinbases b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig b61.vtx[0].rehash() b61 = self.update_block(61, []) assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize()) self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True) # Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests) # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> b62 (18) # self.log.info( "Reject a block with a transaction with a nonfinal locktime") self.move_tip(60) b62 = self.next_block(62) tx = CTransaction() tx.nLockTime = 0xffffffff # this locktime is non-final # don't set nSequence tx.vin.append(CTxIn(COutPoint(out[18].sha256, 0))) tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) assert tx.vin[0].nSequence < 0xffffffff tx.calc_sha256() b62 = self.update_block(62, [tx]) self.send_blocks([b62], success=False, reject_reason='bad-txns-nonfinal') # Test a non-final coinbase is also rejected # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> b63 (-) # self.log.info( "Reject a block with a coinbase transaction with a nonfinal locktime") self.move_tip(60) b63 = self.next_block(63) b63.vtx[0].nLockTime = 0xffffffff b63.vtx[0].vin[0].nSequence = 0xDEADBEEF b63.vtx[0].rehash() b63 = self.update_block(63, []) self.send_blocks([b63], success=False, reject_reason='bad-txns-nonfinal') # This checks that a block with a bloated VARINT between the block_header and the array of tx such that # the block is > LEGACY_MAX_BLOCK_SIZE with the bloated varint, but <= LEGACY_MAX_BLOCK_SIZE without the bloated varint, # does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not # care whether the bloated block is accepted or rejected; it only cares that the second block is accepted. # # What matters is that the receiving node should not reject the bloated block, and then reject the canonical # block on the basis that it's the same as an already-rejected block (which would be a consensus failure.) # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) # \ # b64a (18) # b64a is a bloated block (non-canonical varint) # b64 is a good block (same as b64 but w/ canonical varint) # self.log.info( "Accept a valid block even if a bloated version of the block has previously been sent") self.move_tip(60) regular_block = self.next_block("64a", spend=out[18]) # make it a "broken_block," with non-canonical serialization b64a = CBrokenBlock(regular_block) b64a.initialize(regular_block) self.blocks["64a"] = b64a self.tip = b64a tx = CTransaction() # use canonical serialization to calculate size script_length = LEGACY_MAX_BLOCK_SIZE - \ len(b64a.normal_serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0))) b64a = self.update_block("64a", [tx]) assert_equal(len(b64a.serialize()), LEGACY_MAX_BLOCK_SIZE + 8) self.send_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize():') # bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently # resend the header message, it won't send us the getdata message again. Just # disconnect and reconnect and then call sync_blocks. # TODO: improve this test to be less dependent on P2P DOS behaviour. node.disconnect_p2ps() self.reconnect_p2p() self.move_tip(60) b64 = CBlock(b64a) b64.vtx = copy.deepcopy(b64a.vtx) assert_equal(b64.hash, b64a.hash) assert_equal(len(b64.serialize()), LEGACY_MAX_BLOCK_SIZE) self.blocks[64] = b64 b64 = self.update_block(64, []) self.send_blocks([b64], True) self.save_spendable_output() # Spend an output created in the block itself # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # self.log.info( "Accept a block with a transaction spending an output created in the same block") self.move_tip(64) b65 = self.next_block(65) tx1 = self.create_and_sign_transaction(out[19], out[19].vout[0].nValue) tx2 = self.create_and_sign_transaction(tx1, 0) b65 = self.update_block(65, [tx1, tx2]) self.send_blocks([b65], True) self.save_spendable_output() # Attempt to double-spend a transaction created in a block # # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # \-> b67 (20) # # self.log.info( "Reject a block with a transaction double spending a transaction created in the same block") self.move_tip(65) b67 = self.next_block(67) tx1 = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue) tx2 = self.create_and_sign_transaction(tx1, 1) tx3 = self.create_and_sign_transaction(tx1, 2) b67 = self.update_block(67, [tx1, tx2, tx3]) self.send_blocks([b67], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # More tests of block subsidy # # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) # \-> b68 (20) # # b68 - coinbase with an extra 10 satoshis, # creates a tx that has 9 satoshis from out[20] go to fees # this fails because the coinbase is trying to claim 1 satoshi too much in fees # # b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee # this succeeds # self.log.info( "Reject a block trying to claim too much subsidy in the coinbase transaction") self.move_tip(65) b68 = self.next_block(68, additional_coinbase_value=10) tx = self.create_and_sign_transaction( out[20], out[20].vout[0].nValue - 9) b68 = self.update_block(68, [tx]) self.send_blocks([b68], success=False, reject_reason='bad-cb-amount', reconnect=True) self.log.info( "Accept a block claiming the correct subsidy in the coinbase transaction") self.move_tip(65) b69 = self.next_block(69, additional_coinbase_value=10) tx = self.create_and_sign_transaction( out[20], out[20].vout[0].nValue - 10) self.update_block(69, [tx]) self.send_blocks([b69], True) self.save_spendable_output() # Test spending the outpoint of a non-existent transaction # # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) # \-> b70 (21) # self.log.info( "Reject a block containing a transaction spending from a non-existent input") self.move_tip(69) b70 = self.next_block(70, spend=out[21]) bogus_tx = CTransaction() bogus_tx.sha256 = uint256_from_str( b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c") tx = CTransaction() tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) tx.vout.append(CTxOut(1, b"")) pad_tx(tx) b70 = self.update_block(70, [tx]) self.send_blocks([b70], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) # # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) # \-> b71 (21) # # b72 is a good block. # b71 is a copy of 72, but re-adds one of its transactions. However, # it has the same hash as b72. self.log.info( "Reject a block containing a duplicate transaction but with the same Merkle root (Merkle tree malleability") self.move_tip(69) b72 = self.next_block(72) tx1 = self.create_and_sign_transaction(out[21], 2) tx2 = self.create_and_sign_transaction(tx1, 1) b72 = self.update_block(72, [tx1, tx2]) # now tip is 72 b71 = copy.deepcopy(b72) # add duplicate last transaction b71.vtx.append(b72.vtx[-1]) # b71 builds off b69 self.block_heights[b71.sha256] = self.block_heights[b69.sha256] + 1 self.blocks[71] = b71 assert_equal(len(b71.vtx), 4) assert_equal(len(b72.vtx), 3) assert_equal(b72.sha256, b71.sha256) self.move_tip(71) self.send_blocks([b71], success=False, reject_reason='bad-txns-duplicate', reconnect=True) self.move_tip(72) self.send_blocks([b72], True) self.save_spendable_output() self.log.info("Skipped sigops tests") # tests were moved to feature_block_sigops.py b75 = self.next_block(75) self.save_spendable_output() b76 = self.next_block(76) self.save_spendable_output() self.send_blocks([b75, b76], True) # Test transaction resurrection # # -> b77 (24) -> b78 (25) -> b79 (26) # \-> b80 (25) -> b81 (26) -> b82 (27) # # b78 creates a tx, which is spent in b79. After b82, both should be in mempool # # The tx'es must be unsigned and pass the node's mempool policy. It is unsigned for the # rather obscure reason that the Python signature code does not distinguish between # Low-S and High-S values (whereas the bitcoin code has custom code which does so); # as a result of which, the odds are 50% that the python code will use the right # value and the transaction will be accepted into the mempool. Until we modify the # test framework to support low-S signing, we are out of luck. # # To get around this issue, we construct transactions which are not signed and which # spend to OP_TRUE. If the standard-ness rules change, this test would need to be # updated. (Perhaps to spend to a P2SH OP_TRUE script) self.log.info("Test transaction resurrection during a re-org") self.move_tip(76) b77 = self.next_block(77) tx77 = self.create_and_sign_transaction(out[24], 10 * COIN) b77 = self.update_block(77, [tx77]) self.send_blocks([b77], True) self.save_spendable_output() b78 = self.next_block(78) tx78 = self.create_tx(tx77, 0, 9 * COIN) b78 = self.update_block(78, [tx78]) self.send_blocks([b78], True) b79 = self.next_block(79) tx79 = self.create_tx(tx78, 0, 8 * COIN) b79 = self.update_block(79, [tx79]) self.send_blocks([b79], True) # mempool should be empty assert_equal(len(self.nodes[0].getrawmempool()), 0) self.move_tip(77) b80 = self.next_block(80, spend=out[25]) self.send_blocks([b80], False, request_block=False) self.save_spendable_output() b81 = self.next_block(81, spend=out[26]) # other chain is same length self.send_blocks([b81], False, request_block=False) self.save_spendable_output() b82 = self.next_block(82, spend=out[27]) # now this chain is longer, triggers re-org self.send_blocks([b82], True) self.save_spendable_output() # now check that tx78 and tx79 have been put back into the peer's # mempool mempool = self.nodes[0].getrawmempool() assert_equal(len(mempool), 2) assert tx78.hash in mempool assert tx79.hash in mempool # Test invalid opcodes in dead execution paths. # # -> b81 (26) -> b82 (27) -> b83 (28) # self.log.info( "Accept a block with invalid opcodes in dead execution paths") b83 = self.next_block(83) op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF] script = CScript(op_codes) tx1 = self.create_and_sign_transaction( out[28], out[28].vout[0].nValue, script) tx2 = self.create_and_sign_transaction(tx1, 0, CScript([OP_TRUE])) tx2.vin[0].scriptSig = CScript([OP_FALSE]) tx2.rehash() b83 = self.update_block(83, [tx1, tx2]) self.send_blocks([b83], True) self.save_spendable_output() # Reorg on/off blocks that have OP_RETURN in them (and try to spend them) # # -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31) # \-> b85 (29) -> b86 (30) \-> b89a (32) # self.log.info("Test re-orging blocks with OP_RETURN in them") b84 = self.next_block(84) tx1 = self.create_tx(out[29], 0, 0, CScript([OP_RETURN])) vout_offset = len(tx1.vout) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.calc_sha256() self.sign_tx(tx1, out[29]) tx1.rehash() tx2 = self.create_tx(tx1, vout_offset, 0, CScript([OP_RETURN])) tx2.vout.append(CTxOut(0, CScript([OP_RETURN]))) tx3 = self.create_tx(tx1, vout_offset + 1, 0, CScript([OP_RETURN])) tx3.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx4 = self.create_tx(tx1, vout_offset + 2, 0, CScript([OP_TRUE])) tx4.vout.append(CTxOut(0, CScript([OP_RETURN]))) tx5 = self.create_tx(tx1, vout_offset + 3, 0, CScript([OP_RETURN])) b84 = self.update_block(84, [tx1, tx2, tx3, tx4, tx5]) self.send_blocks([b84], True) self.save_spendable_output() self.move_tip(83) b85 = self.next_block(85, spend=out[29]) self.send_blocks([b85], False) # other chain is same length b86 = self.next_block(86, spend=out[30]) self.send_blocks([b86], True) self.move_tip(84) b87 = self.next_block(87, spend=out[30]) self.send_blocks([b87], False) # other chain is same length self.save_spendable_output() b88 = self.next_block(88, spend=out[31]) self.send_blocks([b88], True) self.save_spendable_output() # trying to spend the OP_RETURN output is rejected b89a = self.next_block("89a", spend=out[32]) tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE])) b89a = self.update_block("89a", [tx]) self.send_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) self.log.info( "Test a re-org of one week's worth of blocks (1088 blocks)") self.move_tip(88) LARGE_REORG_SIZE = 1088 blocks = [] spend = out[32] for i in range(89, LARGE_REORG_SIZE + 89): b = self.next_block(i, spend) tx = CTransaction() script_length = LEGACY_MAX_BLOCK_SIZE - len(b.serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0))) b = self.update_block(i, [tx]) assert_equal(len(b.serialize()), LEGACY_MAX_BLOCK_SIZE) blocks.append(b) self.save_spendable_output() spend = self.get_spendable_output() self.send_blocks(blocks, True, timeout=960) chain1_tip = i # now create alt chain of same length self.move_tip(88) blocks2 = [] for i in range(89, LARGE_REORG_SIZE + 89): blocks2.append(self.next_block("alt" + str(i))) self.send_blocks(blocks2, False, request_block=False) # extend alt chain to trigger re-org block = self.next_block("alt" + str(chain1_tip + 1)) self.send_blocks([block], True, timeout=960) # ... and re-org back to the first chain self.move_tip(chain1_tip) block = self.next_block(chain1_tip + 1) self.send_blocks([block], False, request_block=False) block = self.next_block(chain1_tip + 2) self.send_blocks([block], True, timeout=960)
def test_basic(self): # All messages are received in the same socket which means # that this test fails if the publishing order changes. # Note that the publishing order is not defined in the documentation and # is subject to change. import zmq # Invalid zmq arguments don't take down the node, see #17185. self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"]) address = 'tcp://127.0.0.1:28332' socket = self.ctx.socket(zmq.SUB) socket.set(zmq.RCVTIMEO, 60000) # Subscribe to all available topics. hashblock = ZMQSubscriber(socket, b"hashblock") hashtx = ZMQSubscriber(socket, b"hashtx") rawblock = ZMQSubscriber(socket, b"rawblock") rawtx = ZMQSubscriber(socket, b"rawtx") if self.is_wallet_compiled(): self.hashwallettx = ZMQSubscriber(socket, b"hashwallettx") self.rawwallettx = ZMQSubscriber(socket, b"rawwallettx") self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx, getattr(self, 'hashwallettx', None), getattr(self, 'rawwallettx', None)] if sub is not None]) connect_nodes(self.nodes[0], 1) socket.connect(address) # Relax so that the subscriber is ready before publishing zmq messages sleep(0.2) if self.is_wallet_compiled(): self.sync_all() # Flush initial wallettx events before we begin while True: try: topic, body, seq = self.hashwallettx.socket.recv_multipart() except zmq.ZMQError: break subscriber = {b'hashwallettx-block': self.hashwallettx, b'rawwallettx-block': self.rawwallettx}[topic] assert_equal(struct.unpack('<I', seq)[-1], subscriber.sequence) subscriber.sequence += 1 num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) if self.is_wallet_compiled(): genhashes = self.nodes[0].generate(num_blocks) else: genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = hashtx.receive() # Should receive the coinbase raw transaction. hex = rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) if self.is_wallet_compiled(): # Should receive wallet tx wallettxid = self.hashwallettx.receive(b"hashwallettx-block") wallethex = self.rawwallettx.receive(b"rawwallettx-block") wallettx = CTransaction() wallettx.deserialize(BytesIO(wallethex)) wallettx.calc_sha256() assert_equal(wallettx.hash, wallettxid.hex()) # Should receive the generated block hash. hash = hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) # Should receive the generated raw block. block = rawblock.receive() assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = rawtx.receive() assert_equal(payment_txid, hash256_reversed(hex).hex()) if self.is_wallet_compiled(): wallettxid = self.hashwallettx.receive(b"hashwallettx-mempool") wallethex = self.rawwallettx.receive(b"rawwallettx-mempool") assert_equal(hash256_reversed(wallethex), wallettxid) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ {"type": "pubhashblock", "address": address, "hwm": 1000}, {"type": "pubhashtx", "address": address, "hwm": 1000}, ] + ([{"type": "pubhashwallettx", "address": address, "hwm": 1000}] if self.is_wallet_compiled() else []) + [ {"type": "pubrawblock", "address": address, "hwm": 1000}, {"type": "pubrawtx", "address": address, "hwm": 1000}, ] + ([{"type": "pubrawwallettx", "address": address, "hwm": 1000}] if self.is_wallet_compiled() else []) + [ ]) assert_equal(self.nodes[1].getzmqnotifications(), [])
def get_tx(self): tx = CTransaction() tx.calc_sha256() return tx
def get_tx(self): tx = CTransaction() tx.vin.append(self.valid_txin) tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE]))) tx.calc_sha256() return tx
def get_tx(self): tx = CTransaction() tx.vin.append(self.valid_txin) tx.calc_sha256() return tx
def test_basic(self): # All messages are received in the same socket which means # that this test fails if the publishing order changes. # Note that the publishing order is not defined in the documentation and # is subject to change. import zmq # Invalid zmq arguments don't take down the node, see #17185. self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"]) address = 'tcp://127.0.0.1:28332' socket = self.ctx.socket(zmq.SUB) socket.set(zmq.RCVTIMEO, 60000) # Subscribe to all available topics. hashblock = ZMQSubscriber(socket, b"hashblock") hashtx = ZMQSubscriber(socket, b"hashtx") rawblock = ZMQSubscriber(socket, b"rawblock") rawtx = ZMQSubscriber(socket, b"rawtx") self.restart_node(0, [ "-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx] ]) connect_nodes(self.nodes[0], 1) socket.connect(address) # Relax so that the subscriber is ready before publishing zmq messages sleep(0.2) num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = hashtx.receive() # Should receive the coinbase raw transaction. hex = rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated block hash. hash = hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) # Should receive the generated raw block. block = rawblock.receive() assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = rawtx.receive() assert_equal(payment_txid, hash256_reversed(hex).hex()) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ { "type": "pubhashblock", "address": address, "hwm": 1000 }, { "type": "pubhashtx", "address": address, "hwm": 1000 }, { "type": "pubrawblock", "address": address, "hwm": 1000 }, { "type": "pubrawtx", "address": address, "hwm": 1000 }, ]) assert_equal(self.nodes[1].getzmqnotifications(), [])
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 = CECKey() coinbase_key.set_secretbytes(b"horsebattery") coinbase_pubkey = coinbase_key.get_pubkey() # 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(120) 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)
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 mine_block(self, node, vtx=None, mn_payee=None, mn_amount=None, use_mnmerkleroot_from_tip=False, expected_error=None): if vtx is None: vtx = [] bt = node.getblocktemplate({'rules': ['segwit']}) height = bt['height'] tip_hash = bt['previousblockhash'] tip_block = node.getblock(tip_hash, 2)["tx"][0] coinbasevalue = 50 * COIN halvings = int(height / 150) # regtest coinbasevalue >>= halvings miner_script = self.nodes[0].getaddressinfo( self.nodes[0].getnewaddress())['scriptPubKey'] if mn_payee is None: if isinstance(bt['masternode'], list): mn_payee = bt['masternode'][0]['script'] else: mn_payee = bt['masternode']['script'] # we can't take the masternode payee amount from the template here as we might have additional fees in vtx new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value if mn_amount is None: mn_amount = get_masternode_payment( height, coinbasevalue, bt['masternode_collateral_height']) + new_fees / 2 miner_amount = int(coinbasevalue * 0.25) miner_amount += new_fees / 2 coinbase = CTransaction() coinbase.vout.append( CTxOut(int(miner_amount), hex_str_to_bytes(miner_script))) coinbase.vout.append(CTxOut(int(mn_amount), hex_str_to_bytes(mn_payee))) coinbase.vin = create_coinbase(height).vin # Recreate mn root as using one in BT would result in invalid merkle roots for masternode lists coinbase.nVersion = bt['version_coinbase'] if len(bt['default_witness_commitment_extra']) != 0: if use_mnmerkleroot_from_tip: cbtx = FromHex(CCbTx(version=2), bt['default_witness_commitment_extra']) if 'cbTx' in tip_block: cbtx.merkleRootMNList = int( tip_block['cbTx']['merkleRootMNList'], 16) else: cbtx.merkleRootMNList = 0 coinbase.extraData = cbtx.serialize() else: coinbase.extraData = hex_str_to_bytes( bt['default_witness_commitment_extra']) coinbase.calc_sha256(with_witness=True) block = create_block(int(tip_hash, 16), coinbase) block.nVersion = 4 block.vtx += vtx block.hashMerkleRoot = block.calc_merkle_root() add_witness_commitment(block) block.solve() result = node.submitblock(ToHex(block)) if expected_error is not None and result != expected_error: raise AssertionError( 'mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result)) elif expected_error is None and result is not None: raise AssertionError('submitblock returned %s' % (result))
def run_test(self): self.generate(self.nodes[0], 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_equal(tmpl['sizelimit'], 1000000) assert 'weightlimit' not in tmpl assert_equal(tmpl['sigoplimit'], 20000) assert_equal(tmpl['transactions'][0]['hash'], txid) assert_equal(tmpl['transactions'][0]['sigops'], 2) assert '!segwit' not in tmpl['rules'] self.generate(self.nodes[0], 1) # block 162 balance_presetup = self.nodes[0].getbalance() self.pubkey = [] p2sh_ids = [ ] # p2sh_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE embedded in p2sh wit_ids = [ ] # wit_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts 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, bytes.fromhex(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 _ in range(2): p2sh_ids[i].append([]) wit_ids[i].append([]) for _ 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.generate(self.nodes[0], 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.generate(self.nodes[0], 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][P2WPKH][0], True) # block 424 self.skip_mine(self.nodes[2], wit_ids[NODE_2][P2WSH][0], True) # block 425 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][P2WPKH][0], True) # block 426 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][P2WSH][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-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False) self.fail_accept( self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False) self.generate(self.nodes[2], 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.generate( self.nodes[2], 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], "non-mandatory-script-verify-flag (Witness program hash mismatch)", wit_ids[NODE_0][P2WPKH][0], sign=False) self.fail_accept( self.nodes[0], "non-mandatory-script-verify-flag (Witness program was passed an empty witness)", wit_ids[NODE_0][P2WSH][0], sign=False) self.fail_accept( self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WPKH][0], sign=False) self.fail_accept( self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WSH][0], sign=False) # unsigned with redeem script self.fail_accept( self.nodes[0], "non-mandatory-script-verify-flag (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0])) self.fail_accept( self.nodes[0], "non-mandatory-script-verify-flag (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=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 = tx_from_hex(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() # Coinbase contains the witness commitment nonce, check that RPC shows us coinbase_txid = self.nodes[2].getblock(blockhash)['tx'][0] coinbase_tx = self.nodes[2].gettransaction(txid=coinbase_txid, verbose=True) witnesses = coinbase_tx["decoded"]["vin"][0]["txinwitness"] assert_equal(len(witnesses), 1) assert_is_hex_string(witnesses[0]) assert_equal(witnesses[0], '00' * 32) 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)', wit_ids[NODE_2][P2WPKH][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', p2sh_ids[NODE_2][P2WPKH][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)', p2sh_ids[NODE_2][P2WSH][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][P2WPKH][0], True) # block 432 self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WSH][0], True) # block 433 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WPKH][0], True) # block 434 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WSH][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) raw_tx = self.nodes[0].getrawtransaction(txid, True) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert_greater_than_or_equal( tmpl['sizelimit'], 3999577 ) # actual maximum size is lower due to minimum mandatory non-witness data assert_equal(tmpl['weightlimit'], 4000000) assert_equal(tmpl['sigoplimit'], 80000) assert_equal(tmpl['transactions'][0]['txid'], txid) expected_sigops = 9 if 'txinwitness' in raw_tx["vin"][0] else 8 assert_equal(tmpl['transactions'][0]['sigops'], expected_sigops) assert '!segwit' in tmpl['rules'] self.generate(self.nodes[0], 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 = tx_from_hex(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 = tx_from_hex(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"], tx1.get_vsize()) assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], tx1.get_weight()) # 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( tx.serialize().hex())['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = tx_from_hex(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"], tx.get_vsize()) assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], tx.get_weight()) # 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=tx.serialize().hex(), 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"], tx.get_vsize()) assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], tx.get_weight()) # Mine a block to clear the gbt cache again. self.generate(self.nodes[0], 1) self.log.info("Verify behaviour of importaddress 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 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, bytes.fromhex(pubkeys[3]), bytes.fromhex(pubkeys[4]), OP_2, OP_CHECKMULTISIG ]) solvable_after_importaddress.append(script_to_p2sh_script(script)) 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_key = bytes.fromhex( "02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D" ) unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key) unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh) p2shop0 = script_to_p2sh_script(op0) p2wshop1 = script_to_p2wsh_script(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 = bytes.fromhex(v['hex']) importlist.append(bare.hex()) importlist.append(script_to_p2wsh_script(bare).hex()) else: pubkey = bytes.fromhex(v['pubkey']) p2pk = CScript([pubkey, OP_CHECKSIG]) p2pkh = key_to_p2pkh_script(pubkey) importlist.append(p2pk.hex()) importlist.append(p2pkh.hex()) importlist.append(key_to_p2wpkh_script(pubkey).hex()) importlist.append(script_to_p2wsh_script(p2pk).hex()) importlist.append(script_to_p2wsh_script(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( "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])] 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']['address'], 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.restart_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_basic(self): # Invalid zmq arguments don't take down the node, see #17185. self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"]) address = 'tcp://127.0.0.1:28332' sockets = [] subs = [] services = [b"hashblock", b"hashtx", b"rawblock", b"rawtx"] for service in services: sockets.append(self.ctx.socket(zmq.SUB)) sockets[-1].set(zmq.RCVTIMEO, 60000) subs.append(ZMQSubscriber(sockets[-1], service)) # Subscribe to all available topics. hashblock = subs[0] hashtx = subs[1] rawblock = subs[2] rawtx = subs[3] self.restart_node(0, [ "-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx] ]) self.connect_nodes(0, 1) for socket in sockets: socket.connect(address) # Relax so that the subscriber is ready before publishing zmq messages sleep(0.2) num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = hashtx.receive() # Should receive the coinbase raw transaction. hex = rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated raw block. block = rawblock.receive() assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) # Should receive the generated block hash. hash = hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = rawtx.receive() assert_equal(payment_txid, hash256_reversed(hex).hex()) # Mining the block with this tx should result in second notification # after coinbase tx notification self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) hashtx.receive() txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ { "type": "pubhashblock", "address": address, "hwm": 1000 }, { "type": "pubhashtx", "address": address, "hwm": 1000 }, { "type": "pubrawblock", "address": address, "hwm": 1000 }, { "type": "pubrawtx", "address": address, "hwm": 1000 }, ]) assert_equal(self.nodes[1].getzmqnotifications(), [])
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.p2ps[0].send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.nodes[0].generatetoaddress( 100, 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.p2ps[0].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=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)) tx_orphan_2_invalid.calc_sha256() self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2ps[0].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.p2ps[0].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) self.wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool())) self.log.info('Test orphan pool overflow') orphan_tx_pool = [CTransaction() for _ in range(101)] for i in range(len(orphan_tx_pool)): orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333))) orphan_tx_pool[i].vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']): node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False) rejected_parent = CTransaction() rejected_parent.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0))) rejected_parent.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) rejected_parent.rehash() with node.assert_debug_log([ 'not keeping orphan with rejected parents {}'.format( rejected_parent.hash) ]): node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)
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.set_base_version(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)
def test_basic(self): # Invalid zmq arguments don't take down the node, see #17185. self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"]) address = 'tcp://127.0.0.1:28100' subs = self.setup_zmq_test( [(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]], connect_nodes=True) hashblock = subs[0] hashtx = subs[1] rawblock = subs[2] rawtx = subs[3] num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = hashtx.receive() # Should receive the coinbase raw transaction. hex = rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated raw block. block = rawblock.receive() assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) # Should receive the generated block hash. hash = hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = rawtx.receive() assert_equal(payment_txid, hash256_reversed(hex).hex()) # Mining the block with this tx should result in second notification # after coinbase tx notification self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) hashtx.receive() txid = hashtx.receive() assert_equal(payment_txid, txid.hex()) self.log.info("Test the getzmqnotifications RPC") assert_equal(self.nodes[0].getzmqnotifications(), [ { "type": "pubhashblock", "address": address, "hwm": 1000 }, { "type": "pubhashtx", "address": address, "hwm": 1000 }, { "type": "pubrawblock", "address": address, "hwm": 1000 }, { "type": "pubrawtx", "address": address, "hwm": 1000 }, ]) assert_equal(self.nodes[1].getzmqnotifications(), [])
def _zmq_test(self): num_blocks = 5 self.log.info( "Generate {0} blocks (and {0} coinbase txes)".format(num_blocks)) genhashes = self.nodes[0].generate(num_blocks) self.sync_all() for x in range(num_blocks): # Should receive the coinbase txid. txid = self.hashtx.receive() # Should receive the coinbase raw transaction. hex = self.rawtx.receive() tx = CTransaction() tx.deserialize(BytesIO(hex)) tx.calc_sha256() assert_equal(tx.hash, txid.hex()) # Should receive the generated block hash. hash = self.hashblock.receive().hex() assert_equal(genhashes[x], hash) # The block should only have the coinbase txid. assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) # Should receive the generated raw block. block = self.rawblock.receive() assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1.0) self.sync_all() # Should receive the broadcasted txid. txid = self.hashtx.receive() assert_equal(payment_txid, txid.hex()) # Should receive the broadcasted raw transaction. hex = self.rawtx.receive() assert_equal(payment_txid, hash256_reversed(hex).hex()) # Do a double-spend and verify that we got the double-spend tx notifications (hashds & rawds) self.log.info("Creating double-spend transactions") fee = 1000 / 1e8 amt, vout = None, None tx = CTransaction() tx.deserialize(BytesIO(hex)) # Find the vout that we are able to sign from the previous payment tx for i, txout in enumerate(tx.vout): if txout.nValue == int(1.0 * 1e8): vout = i amt = txout.nValue assert amt is not None amt /= 1e8 assert amt > fee * 2 self.log.info(f"Spending {amt} from {payment_txid}:{vout}, fee: {fee}") ds_txs = [None, None] addr = self.nodes[0].getnewaddress() ds_txs[0] = create_raw_transaction(self.nodes[0], payment_txid, addr, amt - fee, vout) self.log.info("Signed tx 0") ds_txs[1] = create_raw_transaction(self.nodes[0], payment_txid, addr, amt - fee * 2, vout) self.log.info("Signed tx 1 (conflicting tx)") # Broadcast the two tx's via the other node ds_txid = self.nodes[1].sendrawtransaction(ds_txs[0]) # Gobble up the two zmq notifs for hashtx and verify them again txid = self.hashtx.receive() # Should receive the broadcasted raw transaction. hex = self.rawtx.receive() assert_equal(ds_txid, hash256_reversed(hex).hex()) assert_equal(ds_txid, txid.hex()) # this is normal, it gets rejected as an attempted double-spend assert_raises_rpc_error(-26, "txn-mempool-conflict (code 18)", self.nodes[1].sendrawtransaction, ds_txs[1]) self.sync_all() # Should receive the in-mempool txid as a double-spend notification self.log.info("Receiving hashds") ds_txid_zmq: bytes = self.hashds.receive() assert_equal(ds_txid, ds_txid_zmq.hex()) # Should also receive the raw double-spent tx data self.log.info("Receiving rawds") ds_tx_zmq: bytes = self.rawds.receive() assert_equal(ds_txs[0], ds_tx_zmq.hex())
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) # 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=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 ... ') 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 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 privkey = b"aa3680d5d48a8283413f7a108367c7299ca73f553735860a87b08f39395618b7" key = CECKey() key.set_secretbytes(privkey) key.set_compressed(True) pubkey = CPubKey(key.get_pubkey()) pubkeyhash = hash160(pubkey) SCRIPT_PUB_KEY = CScript([ CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG) ]) self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height, pubkey), block_time) block.solve(self.signblockprivkey) # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) 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=False) # 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 tx_withhold = CTransaction() tx_withhold.vin.append( CTxIn(outpoint=COutPoint(block1.vtx[0].malfixsha256, 0))) tx_withhold.vout.append( CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY)) tx_withhold.calc_sha256() (sighash, err) = SignatureHash(CScript([pubkey, OP_CHECKSIG]), tx_withhold, 0, SIGHASH_ALL) signature = key.sign(sighash) + b'\x01' # 0x1 is SIGHASH_ALL tx_withhold.vin[0].scriptSig = CScript([signature]) # 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.malfixsha256, 0))) tx_orphan_1.vout = [ CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY) ] * 3 tx_orphan_1.calc_sha256() (sighash, err) = SignatureHash(SCRIPT_PUB_KEY, tx_orphan_1, 0, SIGHASH_ALL) signature = key.sign(sighash) + b'\x01' # 0x1 is SIGHASH_ALL tx_orphan_1.vin[0].scriptSig = CScript([signature, pubkey]) # 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.malfixsha256, 0))) tx_orphan_2_no_fee.vout.append( CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY)) (sighash, err) = SignatureHash(SCRIPT_PUB_KEY, tx_orphan_2_no_fee, 0, SIGHASH_ALL) signature = key.sign(sighash) + b'\x01' # 0x1 is SIGHASH_ALL tx_orphan_2_no_fee.vin[0].scriptSig = CScript([signature, pubkey]) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.malfixsha256, 1))) tx_orphan_2_valid.vout.append( CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY)) tx_orphan_2_valid.calc_sha256() (sighash, err) = SignatureHash(SCRIPT_PUB_KEY, tx_orphan_2_valid, 0, SIGHASH_ALL) signature = key.sign(sighash) + b'\x01' # 0x1 is SIGHASH_ALL tx_orphan_2_valid.vin[0].scriptSig = CScript([signature, pubkey]) # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.malfixsha256, 2))) tx_orphan_2_invalid.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY)) (sighash, err) = SignatureHash(SCRIPT_PUB_KEY, tx_orphan_2_invalid, 0, SIGHASH_ALL) signature = key.sign(sighash) + b'\x01' # 0x1 is SIGHASH_ALL tx_orphan_2_invalid.vin[0].scriptSig = CScript([signature, pubkey]) 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.hashMalFix 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=False) # 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)