def test_compactblock_reconstruction_multiple_peers( self, node, stalling_peer, delivery_peer): assert (len(self.utxos)) def announce_cmpct_block(node_data, peer): utxo = self.utxos.pop(0) block_data = self.build_block_with_transactions(node_data, utxo, 5) cmpct_block_data = HeaderAndShortIDs() cmpct_block_data.initialize_from_block(block_data) msg_data = MsgCmpctBlock(cmpct_block_data.to_p2p()) peer.send_and_ping(msg_data) with mininode_lock: assert "getblocktxn" in peer.last_message return block_data, cmpct_block_data block, cmpct_block = announce_cmpct_block(node, stalling_peer) for tx in block.vtx[1:]: delivery_peer.send_message(MsgTx(tx)) delivery_peer.sync_with_ping() mempool = node.getrawmempool() for tx in block.vtx[1:]: assert (tx.hash in mempool) delivery_peer.send_and_ping(MsgCmpctBlock(cmpct_block.to_p2p())) assert_equal(int(node.getbestblockhash(), 16), block.sha256) self.utxos.append( [block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) # Now test that delivering an invalid compact block won't break relay block, cmpct_block = announce_cmpct_block(node, stalling_peer) for tx in block.vtx[1:]: delivery_peer.send_message(MsgTx(tx)) delivery_peer.sync_with_ping() cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [CTxInWitness()] cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ] cmpct_block.use_witness = True delivery_peer.send_and_ping(MsgCmpctBlock(cmpct_block.to_p2p())) assert (int(node.getbestblockhash(), 16) != block.sha256) msg = MsgBlockTxn() msg.block_transactions.blockhash = block.sha256 msg.block_transactions.transactions = block.vtx[1:] stalling_peer.send_and_ping(msg) assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def test_incorrect_blocktxn_response(self, node, test_node, version): if len(self.utxos) == 0: self.make_utxos() utxo = self.utxos.pop(0) block = self.build_block_with_transactions(node, utxo, 10) self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) # Relay the first 5 transactions from the block in advance for tx in block.vtx[1:6]: test_node.send_message(MsgTx(tx)) test_node.sync_with_ping() # Make sure all transactions were accepted. mempool = node.getrawmempool() for tx in block.vtx[1:6]: assert(tx.hash in mempool) # Send compact block comp_block = HeaderAndShortIDs() comp_block.initialize_from_block(block, prefill_list=[0], use_witness=(version == 2)) test_node.send_and_ping(MsgCmpctBlock(comp_block.to_p2p())) with mininode_lock: assert("getblocktxn" in test_node.last_message) absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute() assert_equal(absolute_indexes, [6, 7, 8, 9, 10]) # Now give an incorrect response. # Note that it's possible for mulecoind to be smart enough to know we're # lying, since it could check to see if the shortid matches what we're # sending, and eg disconnect us for misbehavior. If that behavior # change were made, we could just modify this test by having a # different peer provide the block further down, so that we're still # verifying that the block isn't marked bad permanently. This is good # enough for now. msg = MsgBlockTxn() if version==2: msg = MsgWitnessBlocktxn() msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:]) test_node.send_and_ping(msg) # Tip should not have updated assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock) # We should receive a getdata request wait_until(lambda: "getdata" in test_node.last_message, timeout=10, lock=mininode_lock, err_msg="test_node.last_message getdata") assert_equal(len(test_node.last_message["getdata"].inv), 1) assert(test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2|MSG_WITNESS_FLAG) assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256) # Deliver the block if version==2: test_node.send_and_ping(MsgWitnessBlock(block)) else: test_node.send_and_ping(MsgBlock(block)) assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def test_getblocktxn_requests(self, node, test_node, version): with_witness = (version == 2) def test_getblocktxn_response(compact_block, peer, expected_result): msg = MsgCmpctBlock(compact_block.to_p2p()) peer.send_and_ping(msg) with mininode_lock: assert ("getblocktxn" in peer.last_message) absolute_indexes = peer.last_message[ "getblocktxn"].block_txn_request.to_absolute() assert_equal(absolute_indexes, expected_result) def test_tip_after_message(node_data, peer, msg, tip): peer.send_and_ping(msg) assert_equal(int(node_data.getbestblockhash(), 16), tip) # First try announcing compactblocks that won't reconstruct, and verify # that we receive getblocktxn messages back. utxo = self.utxos.pop(0) block = self.build_block_with_transactions(node, utxo, 5) self.utxos.append( [block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) comp_block = HeaderAndShortIDs() comp_block.initialize_from_block(block, use_witness=with_witness) test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5]) msg_bt = MsgBlockTxn() if with_witness: msg_bt = MsgWitnessBlocktxn() # serialize with witnesses msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[1:]) test_tip_after_message(node, test_node, msg_bt, block.sha256) utxo = self.utxos.pop(0) block = self.build_block_with_transactions(node, utxo, 5) self.utxos.append( [block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) # Now try interspersing the prefilled transactions comp_block.initialize_from_block(block, prefill_list=[0, 1, 5], use_witness=with_witness) test_getblocktxn_response(comp_block, test_node, [2, 3, 4]) msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[2:5]) test_tip_after_message(node, test_node, msg_bt, block.sha256) # Now try giving one transaction ahead of time. utxo = self.utxos.pop(0) block = self.build_block_with_transactions(node, utxo, 5) self.utxos.append( [block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) test_node.send_and_ping(MsgTx(block.vtx[1])) assert (block.vtx[1].hash in node.getrawmempool()) # Prefill 4 out of the 6 transactions, and verify that only the one # that was not in the mempool is requested. comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4], use_witness=with_witness) test_getblocktxn_response(comp_block, test_node, [5]) msg_bt.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]]) test_tip_after_message(node, test_node, msg_bt, block.sha256) # Now provide all transactions to the node before the block is # announced and verify reconstruction happens immediately. utxo = self.utxos.pop(0) block = self.build_block_with_transactions(node, utxo, 10) self.utxos.append( [block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) for tx in block.vtx[1:]: test_node.send_message(MsgTx(tx)) test_node.sync_with_ping() # Make sure all transactions were accepted. mempool = node.getrawmempool() for tx in block.vtx[1:]: assert (tx.hash in mempool) # Clear out last request. with mininode_lock: test_node.last_message.pop("getblocktxn", None) # Send compact block comp_block.initialize_from_block(block, prefill_list=[0], use_witness=with_witness) test_tip_after_message(node, test_node, MsgCmpctBlock(comp_block.to_p2p()), block.sha256) with mininode_lock: # Shouldn't have gotten a request for any transaction assert ("getblocktxn" not in test_node.last_message)
def run_test(self): node0 = NodeConnCB() connections = [ NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) ] node0.add_connection(connections[0]) NetworkThread().start() # Start up network handling in another thread # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2) self.coinbase_blocks = self.nodes[0].generate(DERSIG_HEIGHT - 2) self.nodeaddress = self.nodes[0].getnewaddress() self.log.info( "Test that a transaction with non-DER signature can still appear in a block" ) spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) un_der_ify(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time) block.nVersion = 2 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 3") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(DERSIG_HEIGHT), block_time) block.nVersion = 2 block.rehash() block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock, err_msg="last_message") with mininode_lock: assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)') assert_equal(node0.last_message["reject"].data, block.sha256) del node0.last_message["reject"] self.log.info( "Test that transactions with non-DER signatures cannot appear in a block" ) block.nVersion = 3 spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) un_der_ify(spendtx) spendtx.rehash() # First we show that this tx is valid except for DERSIG by getting it # accepted to the mempool (which we can achieve with # -promiscuousmempoolflags). node0.send_and_ping(MsgTx(spendtx)) assert spendtx.hash in self.nodes[0].getrawmempool() # Now we verify that a block with this transaction is invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock, err_msg="last_message") with mininode_lock: # We can receive different reject messages depending on whether # Krond is running with multiple script check threads. If script # check threads are not in use, then transaction script validation # happens sequentially, and Krond produces more specific reject # reasons. assert node0.last_message["reject"].code in [ REJECT_INVALID, REJECT_NONSTANDARD ] assert_equal(node0.last_message["reject"].data, block.sha256) if node0.last_message["reject"].code == REJECT_INVALID: # Generic rejection when a block is invalid assert_equal(node0.last_message["reject"].reason, b'block-validation-failed') else: assert b'Non-canonical DER signature' in node0.last_message[ "reject"].reason self.log.info( "Test that a version 3 block with a DERSIG-compliant transaction is accepted" ) block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): node0 = NodeConnCB() connections = [ NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) ] node0.add_connection(connections[0]) NetworkThread().start() # Start up network handling in another thread # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() self.log.info("Mining %d blocks", CLTV_HEIGHT - 2) self.coinbase_blocks = self.nodes[0].generate(CLTV_HEIGHT - 2) self.nodeaddress = self.nodes[0].getnewaddress() self.log.info( "Test that an invalid-according-to-CLTV transaction can still appear in a block" ) spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) cltv_invalidate(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time) block.nVersion = 3 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 4") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time) block.nVersion = 3 block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock, err_msg="last_message") with mininode_lock: assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000003)') assert_equal(node0.last_message["reject"].data, block.sha256) del node0.last_message["reject"] self.log.info( "Test that invalid-according-to-cltv transactions cannot appear in a block" ) block.nVersion = 4 spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) cltv_invalidate(spendtx) spendtx.rehash() # First we show that this tx is valid except for CLTV by getting it # accepted to the mempool (which we can achieve with # -promiscuousmempoolflags). node0.send_and_ping(MsgTx(spendtx)) assert spendtx.hash in self.nodes[0].getrawmempool() # Now we verify that a block with this transaction is invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock, err_msg="last_message") with mininode_lock: assert node0.last_message["reject"].code in [ REJECT_INVALID, REJECT_NONSTANDARD ] assert_equal(node0.last_message["reject"].data, block.sha256) if node0.last_message["reject"].code == REJECT_INVALID: # Generic rejection when a block is invalid assert_equal(node0.last_message["reject"].reason, b'block-validation-failed') else: assert b'Negative locktime' in node0.last_message[ "reject"].reason self.log.info( "Test that a version 4 block with a valid-according-to-CLTV transaction is accepted" ) spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1) spendtx.rehash() block.vtx.pop(1) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() node0.send_and_ping(MsgBlock(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)