212 lines
8.2 KiB
Python
Executable File
212 lines
8.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2018 The Zcash developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
|
|
|
from test_framework.authproxy import JSONRPCException
|
|
from test_framework.mininode import NodeConn, NetworkThread, CInv, \
|
|
msg_mempool, msg_getdata, msg_tx, mininode_lock, SAPLING_PROTO_VERSION
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
from test_framework.util import assert_equal, connect_nodes_bi, fail, \
|
|
p2p_port, start_nodes, sync_blocks, sync_mempools
|
|
from tx_expiry_helper import TestNode, create_transaction
|
|
|
|
|
|
class TxExpiringSoonTest(BitcoinTestFramework):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.num_nodes = 3
|
|
self.setup_clean_chain = True
|
|
|
|
def setup_network(self):
|
|
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
|
|
connect_nodes_bi(self.nodes, 0, 1)
|
|
# We don't connect node 2
|
|
|
|
def send_transaction(self, testnode, block, address, expiry_height):
|
|
tx = create_transaction(self.nodes[0],
|
|
block,
|
|
address,
|
|
10.0,
|
|
expiry_height)
|
|
testnode.send_message(msg_tx(tx))
|
|
|
|
# Sync up with node after p2p messages delivered
|
|
testnode.sync_with_ping()
|
|
|
|
# Sync nodes 0 and 1
|
|
sync_blocks(self.nodes[:2])
|
|
sync_mempools(self.nodes[:2])
|
|
|
|
return tx
|
|
|
|
def verify_inv(self, testnode, tx):
|
|
# Make sure we are synced before sending the mempool message
|
|
testnode.sync_with_ping()
|
|
|
|
# Send p2p message "mempool" to receive contents from zcashd node in "inv" message
|
|
with mininode_lock:
|
|
testnode.last_inv = None
|
|
testnode.send_message(msg_mempool())
|
|
|
|
# Sync up with node after p2p messages delivered
|
|
testnode.sync_with_ping()
|
|
|
|
with mininode_lock:
|
|
msg = testnode.last_inv
|
|
assert_equal(len(msg.inv), 1)
|
|
assert_equal(tx.sha256, msg.inv[0].hash)
|
|
|
|
def send_data_message(self, testnode, tx):
|
|
# Send p2p message "getdata" to verify tx gets sent in "tx" message
|
|
getdatamsg = msg_getdata()
|
|
getdatamsg.inv = [CInv(1, tx.sha256)]
|
|
with mininode_lock:
|
|
testnode.last_notfound = None
|
|
testnode.last_tx = None
|
|
testnode.send_message(getdatamsg)
|
|
|
|
def verify_last_tx(self, testnode, tx):
|
|
# Sync up with node after p2p messages delivered
|
|
testnode.sync_with_ping()
|
|
|
|
# Verify data received in "tx" message is for tx
|
|
with mininode_lock:
|
|
incoming_tx = testnode.last_tx.tx
|
|
incoming_tx.rehash()
|
|
assert_equal(tx.sha256, incoming_tx.sha256)
|
|
|
|
def run_test(self):
|
|
testnode0 = TestNode()
|
|
connections = []
|
|
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0],
|
|
testnode0, "regtest", SAPLING_PROTO_VERSION))
|
|
testnode0.add_connection(connections[0])
|
|
|
|
# Start up network handling in another thread
|
|
NetworkThread().start()
|
|
testnode0.wait_for_verack()
|
|
|
|
# Verify mininodes are connected to zcashd nodes
|
|
peerinfo = self.nodes[0].getpeerinfo()
|
|
versions = [x["version"] for x in peerinfo]
|
|
assert_equal(1, versions.count(SAPLING_PROTO_VERSION))
|
|
assert_equal(0, peerinfo[0]["banscore"])
|
|
|
|
# Mine some blocks so we can spend
|
|
coinbase_blocks = self.nodes[0].generate(200)
|
|
node_address = self.nodes[0].getnewaddress()
|
|
|
|
# Sync nodes 0 and 1
|
|
sync_blocks(self.nodes[:2])
|
|
sync_mempools(self.nodes[:2])
|
|
|
|
# Verify block count
|
|
assert_equal(self.nodes[0].getblockcount(), 200)
|
|
assert_equal(self.nodes[1].getblockcount(), 200)
|
|
assert_equal(self.nodes[2].getblockcount(), 0)
|
|
|
|
# Mininodes send expiring soon transaction in "tx" message to zcashd node
|
|
self.send_transaction(testnode0, coinbase_blocks[0], node_address, 203)
|
|
|
|
# Assert that the tx is not in the mempool (expiring soon)
|
|
assert_equal([], self.nodes[0].getrawmempool())
|
|
assert_equal([], self.nodes[1].getrawmempool())
|
|
assert_equal([], self.nodes[2].getrawmempool())
|
|
|
|
# Mininodes send transaction in "tx" message to zcashd node
|
|
tx2 = self.send_transaction(testnode0, coinbase_blocks[1], node_address, 204)
|
|
|
|
# tx2 is not expiring soon
|
|
assert_equal([tx2.hash], self.nodes[0].getrawmempool())
|
|
assert_equal([tx2.hash], self.nodes[1].getrawmempool())
|
|
# node 2 is isolated
|
|
assert_equal([], self.nodes[2].getrawmempool())
|
|
|
|
# Verify txid for tx2
|
|
self.verify_inv(testnode0, tx2)
|
|
self.send_data_message(testnode0, tx2)
|
|
self.verify_last_tx(testnode0, tx2)
|
|
|
|
# Sync and mine an empty block with node 2, leaving tx in the mempool of node0 and node1
|
|
for blkhash in coinbase_blocks:
|
|
blk = self.nodes[0].getblock(blkhash, 0)
|
|
self.nodes[2].submitblock(blk)
|
|
self.nodes[2].generate(1)
|
|
|
|
# Verify block count
|
|
assert_equal(self.nodes[0].getblockcount(), 200)
|
|
assert_equal(self.nodes[1].getblockcount(), 200)
|
|
assert_equal(self.nodes[2].getblockcount(), 201)
|
|
|
|
# Reconnect node 2 to the network
|
|
connect_nodes_bi(self.nodes, 0, 2)
|
|
|
|
# Set up test node for node 2
|
|
testnode2 = TestNode()
|
|
connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2],
|
|
testnode2, "regtest", SAPLING_PROTO_VERSION))
|
|
testnode2.add_connection(connections[-1])
|
|
|
|
# Verify block count
|
|
sync_blocks(self.nodes[:3])
|
|
assert_equal(self.nodes[0].getblockcount(), 201)
|
|
assert_equal(self.nodes[1].getblockcount(), 201)
|
|
assert_equal(self.nodes[2].getblockcount(), 201)
|
|
|
|
# Verify contents of mempool
|
|
assert_equal([tx2.hash], self.nodes[0].getrawmempool())
|
|
assert_equal([tx2.hash], self.nodes[1].getrawmempool())
|
|
assert_equal([], self.nodes[2].getrawmempool())
|
|
|
|
# Confirm tx2 cannot be submitted to a mempool because it is expiring soon.
|
|
try:
|
|
rawtx2 = tx2.serialize().hex()
|
|
self.nodes[2].sendrawtransaction(rawtx2)
|
|
fail("Sending transaction should have failed")
|
|
except JSONRPCException as e:
|
|
assert_equal(
|
|
"tx-expiring-soon: expiryheight is 204 but should be at least 205 to avoid transaction expiring soon",
|
|
e.error['message']
|
|
)
|
|
|
|
self.send_data_message(testnode0, tx2)
|
|
|
|
# Sync up with node after p2p messages delivered
|
|
testnode0.sync_with_ping()
|
|
|
|
# Verify node 0 does not reply to "getdata" by sending "tx" message, as tx2 is expiring soon
|
|
with mininode_lock:
|
|
assert_equal(testnode0.last_tx, None)
|
|
|
|
# Verify mininode received a "notfound" message containing the txid of tx2
|
|
with mininode_lock:
|
|
msg = testnode0.last_notfound
|
|
assert_equal(len(msg.inv), 1)
|
|
assert_equal(tx2.sha256, msg.inv[0].hash)
|
|
|
|
# Create a transaction to verify that processing of "getdata" messages is functioning
|
|
tx3 = self.send_transaction(testnode0, coinbase_blocks[2], node_address, 999)
|
|
|
|
self.send_data_message(testnode0, tx3)
|
|
self.verify_last_tx(testnode0, tx3)
|
|
# Verify txid for tx3 is returned in "inv", but tx2 which is expiring soon is not returned
|
|
self.verify_inv(testnode0, tx3)
|
|
self.verify_inv(testnode2, tx3)
|
|
|
|
# Verify contents of mempool
|
|
assert_equal({tx2.hash, tx3.hash}, set(self.nodes[0].getrawmempool()))
|
|
assert_equal({tx2.hash, tx3.hash}, set(self.nodes[1].getrawmempool()))
|
|
assert_equal({tx3.hash}, set(self.nodes[2].getrawmempool()))
|
|
|
|
# Verify banscore for nodes are still zero
|
|
assert_equal(0, sum(peer["banscore"] for peer in self.nodes[0].getpeerinfo()))
|
|
assert_equal(0, sum(peer["banscore"] for peer in self.nodes[2].getpeerinfo()))
|
|
|
|
[c.disconnect_node() for c in connections]
|
|
|
|
|
|
if __name__ == '__main__':
|
|
TxExpiringSoonTest().main()
|