Skip to content

Commit

Permalink
wip: more testing
Browse files Browse the repository at this point in the history
- Node should not request txdata if exact ancpkginfo has already been
  rejected.
- Node should not request txdata if one of the txns had a consensus
  failure.
- Node should still request txdata if one of the txns was too low fee.
- Node should still request ancpkginfo from other peers if a peer says a
  tx has no unconfirmed ancestors.
- In network, try chain of nodes where some have already received the
  orphan or a low-fee tx.
- Packages with overlapping ancestors (tx has multiple children).
  • Loading branch information
glozow committed Jan 31, 2023
1 parent dae34db commit 1b0f313
Showing 1 changed file with 67 additions and 2 deletions.
69 changes: 67 additions & 2 deletions test/functional/p2p_package_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
TXID_RELAY_DELAY,
UNCONDITIONAL_RELAY_DELAY,
)
from test_framework.script import (
CScript,
OP_FALSE,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
Expand Down Expand Up @@ -180,6 +184,17 @@ def create_package(self):
package_wtxids = [tx.getwtxid() for tx in package_txns]
return package_hex, package_txns, package_wtxids

def malleate_package(self, package_hex, package_txns, package_wtxids):
"""Malleate first transaction's witness to invalidate it. Does not change txid."""
tx_to_malleate = package_txns[0]
previous_txid = tx_to_malleate.rehash()
tx_to_malleate.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_FALSE])]
assert_equal(tx_to_malleate.rehash(), previous_txid)
assert tx_to_malleate.getwtxid() != package_wtxids[0]
package_hex[0] = tx_to_malleate.serialize().hex()
package_wtxids[0] = tx_to_malleate.getwtxid()
return package_hex, package_txns, package_wtxids

def fastforward(self, seconds):
"""Convenience helper function to fast-forward, so we don't need to keep track of the
starting time when we call setmocktime."""
Expand Down Expand Up @@ -340,7 +355,7 @@ def test_orphan_handling_prefer_ancpkginfo(self):
peer_nonpackage.wait_for_getdata_txids([int(tx.rehash(), 16) for tx in package_txns[:-1]])

@cleanup
def test_orphan_notfounds(self):
def test_orphan_announcer_memory(self):
self.log.info("Test that the node remembers who announced orphan transactions")
node = self.nodes[0]
package_hex, package_txns, package_wtxids = self.create_package()
Expand All @@ -355,6 +370,8 @@ def test_orphan_notfounds(self):
peer_package_relay2 = node.add_p2p_connection(PackageRelayer())
# Sends an inv for the orphan while the node is requesting ancpkginfo
peer_package_relay3 = node.add_p2p_connection(PackageRelayer())
# Sends an inv for the orphan while the node is requesting txdata using the ancpkginfo
peer_package_relay4 = node.add_p2p_connection(PackageRelayer())

peer_package_relay1.send_and_ping(msg_inv([orphan_inv]))
self.fastforward(NONPREF_PEER_TX_DELAY + 1)
Expand Down Expand Up @@ -387,6 +404,16 @@ def test_orphan_notfounds(self):
self.fastforward(1)
peer_package_relay3.sync_with_ping()
peer_package_relay3.wait_for_getancpkginfo(int(orphan_wtxid, 16))
# Peer 3 provides malleated tx
ancpkginfo_message = msg_ancpkginfo([int(wtxid, 16) for wtxid in package_wtxids])
peer_package_relay3.send_and_ping(ancpkginfo_message)
self.fastforward(1)
peer_package_relay3.wait_for_getpkgtxns([int(wtxid, 16) for wtxid in package_wtxids[:-1]])
peer_package_relay4.send_and_ping(msg_inv([orphan_inv]))
# Expires the ancpkginfo.
self.fastforward(60)
peer_package_relay3.sync_with_ping()
peer_package_relay4.wait_for_getancpkginfo(int(orphan_wtxid, 16))

@cleanup
def test_ancpkginfo_received(self):
Expand Down Expand Up @@ -443,6 +470,43 @@ def test_pkgtxns(self):
peer_requester_unannounced.send_and_ping(getpkgtxns_request)
peer_requester_unannounced.wait_for_pkgtxns(package_wtxids)

def relay_until_ancpkginfo(self, node, peer, orphan_tx, orphan_wtxid):
"""Automatically have the peer (which should be connected to the node) do the following:
- Relay the orphan.
- Wait for the node to request ancpkginfo.
"""
orphan_hash = int(orphan_wtxid, 16)
orphan_inv = CInv(t=MSG_WTX, h=orphan_hash)
peer.send_and_ping(msg_inv([orphan_inv]))
self.fastforward(NONPREF_PEER_TX_DELAY + 1)
peer.wait_for_getdata([orphan_hash])
peer.send_and_ping(msg_tx(orphan_tx))
self.fastforward(NONPREF_PEER_TX_DELAY + 1)
peer.wait_for_getancpkginfo(orphan_hash)

def relay_until_getpkgtxns(self, node, peer, orphan_tx, package_wtxids):
"""Automatically have the peer (which should be connected to the node) do the following:
- Relay the orphan.
- Wait for the node to request ancpkginfo.
- Send ancpkginfo.
- Wait for the node to send getpkgtxns.
"""
self.relay_until_ancpkginfo(node, peer, orphan_tx, package_wtxids[-1])
ancpkginfo_message = msg_ancpkginfo([int(wtxid, 16) for wtxid in package_wtxids])
peer.send_and_ping(ancpkginfo_message)
self.fastforward(NONPREF_PEER_TX_DELAY + 1)
peer.wait_for_getpkgtxns([int(wtxid, 16) for wtxid in package_wtxids[:-1]])

@cleanup
def test_failure_caches(self):
node = self.nodes[0]
self.log.info("Test how the node caches failed transactions and packages")
package_hex, package_txns, package_wtxids = self.create_package()

# TODO
peer1_no_txdata = node.add_p2p_connection(PackageRelayer())
self.relay_until_ancpkginfo(node, peer1_no_txdata, package_txns[-1], package_wtxids[-1])

def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
self.generate(self.wallet, 160)
Expand All @@ -452,9 +516,10 @@ def run_test(self):
self.test_orphan_get_ancpkginfo()
self.test_orphan_handling_prefer_outbound()
self.test_orphan_handling_prefer_ancpkginfo()
self.test_orphan_notfounds()
self.test_orphan_announcer_memory()
self.test_ancpkginfo_received()
self.test_pkgtxns()
self.test_failure_caches()


if __name__ == '__main__':
Expand Down

0 comments on commit 1b0f313

Please sign in to comment.