Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Add tests for wtxid tx relay in segwit test
Browse files Browse the repository at this point in the history
Also cleans up some doublicate lines in the rest of the test.

co-authored-by: Anthony Towns <aj@erisian.com.au>
2 people authored and jnewbery committed Jul 28, 2020
1 parent 5aae26a commit 59f38ad
Showing 1 changed file with 91 additions and 6 deletions.
97 changes: 91 additions & 6 deletions test/functional/p2p_segwit.py
Original file line number Diff line number Diff line change
@@ -22,7 +22,9 @@
CTxOut,
CTxWitness,
MAX_BLOCK_BASE_SIZE,
MSG_TX,
MSG_WITNESS_FLAG,
MSG_WTX,
NODE_NETWORK,
NODE_WITNESS,
msg_no_witness_block,
@@ -32,6 +34,7 @@
msg_tx,
msg_block,
msg_no_witness_tx,
msg_wtxidrelay,
ser_uint256,
ser_vector,
sha256,
@@ -79,6 +82,7 @@
softfork_active,
hex_str_to_bytes,
assert_raises_rpc_error,
wait_until,
)

# The versionbit bit used to signal activation of SegWit
@@ -141,23 +145,40 @@ def test_witness_block(node, p2p, block, accepted, with_witness=True, reason=Non


class TestP2PConn(P2PInterface):
def __init__(self):
def __init__(self, wtxidrelay=False):
super().__init__()
self.getdataset = set()
self.last_wtxidrelay = []
self.lastgetdata = []
self.wtxidrelay = wtxidrelay

# Avoid sending out msg_getdata in the mininode thread as a reply to invs.
# They are not needed and would only lead to races because we send msg_getdata out in the test thread
def on_inv(self, message):
pass

def on_version(self, message):
if self.wtxidrelay:
self.send_message(msg_wtxidrelay())
super().on_version(message)

def on_getdata(self, message):
self.lastgetdata = message.inv
for inv in message.inv:
self.getdataset.add(inv.hash)

def announce_tx_and_wait_for_getdata(self, tx, timeout=60, success=True):
def on_wtxidrelay(self, message):
self.last_wtxidrelay.append(message)

def announce_tx_and_wait_for_getdata(self, tx, timeout=60, success=True, use_wtxid=False):
with mininode_lock:
self.last_message.pop("getdata", None)
self.send_message(msg_inv(inv=[CInv(1, tx.sha256)]))
if use_wtxid:
wtxid = tx.calc_sha256(True)
self.send_message(msg_inv(inv=[CInv(MSG_WTX, wtxid)]))
else:
self.send_message(msg_inv(inv=[CInv(MSG_TX, tx.sha256)]))

if success:
self.wait_for_getdata(timeout)
else:
@@ -275,6 +296,7 @@ def run_test(self):
self.test_upgrade_after_activation()
self.test_witness_sigops()
self.test_superfluous_witness()
self.test_wtxid_relay()

# Individual tests

@@ -1268,7 +1290,6 @@ def test_tx_relay_after_segwit_activation(self):
test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=True, accepted=False)

# Verify that removing the witness succeeds.
self.test_node.announce_tx_and_wait_for_getdata(tx)
test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True)

# Now try to add extra witness data to a valid witness tx.
@@ -1295,8 +1316,6 @@ def test_tx_relay_after_segwit_activation(self):
# Node will not be blinded to the transaction
self.std_node.announce_tx_and_wait_for_getdata(tx3)
test_transaction_acceptance(self.nodes[1], self.std_node, tx3, True, False, 'tx-size')
self.std_node.announce_tx_and_wait_for_getdata(tx3)
test_transaction_acceptance(self.nodes[1], self.std_node, tx3, True, False)

# Remove witness stuffing, instead add extra witness push on stack
tx3.vout[0] = CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))
@@ -2015,6 +2034,11 @@ def test_witness_sigops(self):

# TODO: test p2sh sigop counting

# Cleanup and prep for next test
self.utxo.pop(0)
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))

@subtest # type: ignore
def test_superfluous_witness(self):
# Serialization of tx that puts witness flag to 3 always
def serialize_with_bogus_witness(tx):
@@ -2058,6 +2082,67 @@ def serialize(self):
with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
self.nodes[0].p2p.send_and_ping(msg_bogus_tx(tx))

@subtest # type: ignore
def test_wtxid_relay(self):
# Use brand new nodes to avoid contamination from earlier tests
self.wtx_node = self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=True), services=NODE_NETWORK | NODE_WITNESS)
self.tx_node = self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=False), services=NODE_NETWORK | NODE_WITNESS)

# Check wtxidrelay feature negotiation message through connecting a new peer
def received_wtxidrelay():
return (len(self.wtx_node.last_wtxidrelay) > 0)
wait_until(received_wtxidrelay, timeout=60, lock=mininode_lock)

# Create a Segwit output from the latest UTXO
# and announce it to the network
witness_program = CScript([OP_TRUE])
witness_hash = sha256(witness_program)
script_pubkey = CScript([OP_0, witness_hash])

tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey))
tx.rehash()

# Create a Segwit transaction
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_pubkey))
tx2.wit.vtxinwit.append(CTxInWitness())
tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
tx2.rehash()

# Announce Segwit transaction with wtxid
# and wait for getdata
self.wtx_node.announce_tx_and_wait_for_getdata(tx2, use_wtxid=True)
with mininode_lock:
lgd = self.wtx_node.lastgetdata[:]
assert_equal(lgd, [CInv(MSG_WTX, tx2.calc_sha256(True))])

# Announce Segwit transaction from non wtxidrelay peer
# and wait for getdata
self.tx_node.announce_tx_and_wait_for_getdata(tx2, use_wtxid=False)
with mininode_lock:
lgd = self.tx_node.lastgetdata[:]
assert_equal(lgd, [CInv(MSG_TX | MSG_WITNESS_FLAG, tx2.sha256)])

# Send tx2 through; it's an orphan so won't be accepted
with mininode_lock:
self.tx_node.last_message.pop("getdata", None)
test_transaction_acceptance(self.nodes[0], self.tx_node, tx2, with_witness=True, accepted=False)

# Expect a request for parent (tx) due to use of non-WTX peer
self.tx_node.wait_for_getdata(60)
with mininode_lock:
lgd = self.tx_node.lastgetdata[:]
assert_equal(lgd, [CInv(MSG_TX | MSG_WITNESS_FLAG, tx.sha256)])

# Send tx through
test_transaction_acceptance(self.nodes[0], self.tx_node, tx, with_witness=False, accepted=True)

# Check tx2 is there now
assert_equal(tx2.hash in self.nodes[0].getrawmempool(), True)


if __name__ == '__main__':
SegWitTest().main()

0 comments on commit 59f38ad

Please sign in to comment.