diff --git a/test/functional/p2p_opportunistic_1p1c.py b/test/functional/p2p_opportunistic_1p1c.py index 56ab394baa..ad42b7308b 100755 --- a/test/functional/p2p_opportunistic_1p1c.py +++ b/test/functional/p2p_opportunistic_1p1c.py @@ -91,6 +91,7 @@ class PackageRelayTest(BitcoinTestFramework): def test_basic_child_then_parent(self): node = self.nodes[0] self.log.info("Check that opportunistic 1p1c logic works when child is received before parent") + node.setmocktime(int(time.time())) low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet) high_fee_child = self.wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=20*FEERATE_1SAT_VB) @@ -100,11 +101,13 @@ class PackageRelayTest(BitcoinTestFramework): # 1. Child is received first (perhaps the low feerate parent didn't meet feefilter or the requests were sent to different nodes). It is missing an input. high_child_wtxid_int = high_fee_child["tx"].wtxid_int peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=high_child_wtxid_int)])) + node.bumpmocktime(NONPREF_PEER_TX_DELAY) peer_sender.wait_for_getdata([high_child_wtxid_int]) peer_sender.send_and_ping(msg_tx(high_fee_child["tx"])) # 2. Node requests the missing parent by txid. parent_txid_int = int(low_fee_parent["txid"], 16) + node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY) peer_sender.wait_for_getdata([parent_txid_int]) # 3. Sender relays the parent. Parent+Child are evaluated as a package and accepted. @@ -120,6 +123,7 @@ class PackageRelayTest(BitcoinTestFramework): @cleanup def test_basic_parent_then_child(self, wallet): node = self.nodes[0] + node.setmocktime(int(time.time())) low_fee_parent = self.create_tx_below_mempoolminfee(wallet) high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=20*FEERATE_1SAT_VB) @@ -146,6 +150,7 @@ class PackageRelayTest(BitcoinTestFramework): # 3. Node requests the missing parent by txid. # It should do so even if it has previously rejected that parent for being too low feerate. parent_txid_int = int(low_fee_parent["txid"], 16) + node.bumpmocktime(TXID_RELAY_DELAY) peer_sender.wait_for_getdata([parent_txid_int]) # 4. Sender re-relays the parent. Parent+Child are evaluated as a package and accepted. @@ -159,6 +164,7 @@ class PackageRelayTest(BitcoinTestFramework): @cleanup def test_low_and_high_child(self, wallet): node = self.nodes[0] + node.setmocktime(int(time.time())) low_fee_parent = self.create_tx_below_mempoolminfee(wallet) # This feerate is above mempoolminfee, but not enough to also bump the low feerate parent. feerate_just_above = node.getmempoolinfo()["mempoolminfee"] @@ -189,6 +195,7 @@ class PackageRelayTest(BitcoinTestFramework): # 3. Node requests the orphan's missing parent. parent_txid_int = int(low_fee_parent["txid"], 16) + node.bumpmocktime(TXID_RELAY_DELAY) peer_sender.wait_for_getdata([parent_txid_int]) # 4. The low parent + low child are submitted as a package. They are not accepted due to low package feerate. @@ -216,6 +223,7 @@ class PackageRelayTest(BitcoinTestFramework): # 6. Node requests the orphan's parent, even though it has already been rejected, both by # itself and with a child. This is necessary, otherwise high_fee_child can be censored. parent_txid_int = int(low_fee_parent["txid"], 16) + node.bumpmocktime(TXID_RELAY_DELAY) peer_sender.wait_for_getdata([parent_txid_int]) # 7. The low feerate parent + high feerate child are submitted as a package. @@ -231,6 +239,7 @@ class PackageRelayTest(BitcoinTestFramework): def test_orphan_consensus_failure(self): self.log.info("Check opportunistic 1p1c logic requires parent and child to be from the same peer") node = self.nodes[0] + node.setmocktime(int(time.time())) low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet) coin = low_fee_parent["new_utxo"] address = node.get_deterministic_priv_key().address @@ -246,11 +255,13 @@ class PackageRelayTest(BitcoinTestFramework): # 1. Child is received first. It is missing an input. child_wtxid_int = tx_orphan_bad_wit.wtxid_int bad_orphan_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=child_wtxid_int)])) + node.bumpmocktime(NONPREF_PEER_TX_DELAY) bad_orphan_sender.wait_for_getdata([child_wtxid_int]) bad_orphan_sender.send_and_ping(msg_tx(tx_orphan_bad_wit)) # 2. Node requests the missing parent by txid. parent_txid_int = int(low_fee_parent["txid"], 16) + node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY) bad_orphan_sender.wait_for_getdata([parent_txid_int]) # 3. A different peer relays the parent. Package is not evaluated because the transactions @@ -273,6 +284,8 @@ class PackageRelayTest(BitcoinTestFramework): def test_parent_consensus_failure(self): self.log.info("Check opportunistic 1p1c logic with consensus-invalid parent causes disconnect of the correct peer") node = self.nodes[0] + node.setmocktime(int(time.time())) + low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet) high_fee_child = self.wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*FEERATE_1SAT_VB) @@ -287,11 +300,13 @@ class PackageRelayTest(BitcoinTestFramework): # 1. Child is received first. It is missing an input. child_wtxid_int = high_fee_child["tx"].wtxid_int package_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=child_wtxid_int)])) + node.bumpmocktime(NONPREF_PEER_TX_DELAY) package_sender.wait_for_getdata([child_wtxid_int]) package_sender.send_and_ping(msg_tx(high_fee_child["tx"])) # 2. Node requests the missing parent by txid. parent_txid_int = tx_parent_bad_wit.txid_int + node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY) package_sender.wait_for_getdata([parent_txid_int]) # 3. A different node relays the parent. The parent is first evaluated by itself and @@ -309,6 +324,7 @@ class PackageRelayTest(BitcoinTestFramework): # It can send the "real" parent transaction, and the package is accepted. parent_wtxid_int = low_fee_parent["tx"].wtxid_int package_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=parent_wtxid_int)])) + node.bumpmocktime(NONPREF_PEER_TX_DELAY) package_sender.wait_for_getdata([parent_wtxid_int]) package_sender.send_and_ping(msg_tx(low_fee_parent["tx"])) @@ -355,6 +371,7 @@ class PackageRelayTest(BitcoinTestFramework): def test_other_parent_in_mempool(self): self.log.info("Check opportunistic 1p1c works when part of a 2p1c (child already has another parent in mempool)") node = self.nodes[0] + node.setmocktime(int(time.time())) # Grandparent will enter mempool by itself grandparent_high = self.wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*10, confirmed_only=True) @@ -383,6 +400,7 @@ class PackageRelayTest(BitcoinTestFramework): # 4. Node requests parent_low. parent_low_txid_int = int(parent_low["txid"], 16) + node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY) peer_sender.wait_for_getdata([parent_low_txid_int]) peer_sender.send_and_ping(msg_tx(parent_low["tx"])) @@ -410,9 +428,10 @@ class PackageRelayTest(BitcoinTestFramework): peer_normal = node.add_p2p_connection(P2PInterface()) peer_doser = node.add_p2p_connection(P2PInterface()) + num_individual_dosers = 10 self.log.info("Create very large orphans to be sent by DoSy peers (may take a while)") - large_orphans = [create_large_orphan() for _ in range(100)] + large_orphans = [create_large_orphan() for _ in range(50)] # Check to make sure these are orphans, within max standard size (to be accepted into the orphanage) for large_orphan in large_orphans: assert_greater_than_or_equal(100000, large_orphan.get_vsize()) @@ -422,11 +441,10 @@ class PackageRelayTest(BitcoinTestFramework): assert not testres[0]["allowed"] assert_equal(testres[0]["reject-reason"], "missing-inputs") - num_individual_dosers = 30 self.log.info(f"Connect {num_individual_dosers} peers and send a very large orphan from each one") # This test assumes that unrequested transactions are processed (skipping inv and # getdata steps because they require going through request delays) - # Connect 20 peers and have each of them send a large orphan. + # Connect 10 peers and have each of them send a large orphan. for large_orphan in large_orphans[:num_individual_dosers]: peer_doser_individual = node.add_p2p_connection(P2PInterface()) peer_doser_individual.send_and_ping(msg_tx(large_orphan)) @@ -462,7 +480,7 @@ class PackageRelayTest(BitcoinTestFramework): peer_normal.wait_for_getdata([parent_txid_int]) self.log.info("Send another round of very large orphans from a DoSy peer") - for large_orphan in large_orphans[30:]: + for large_orphan in large_orphans[num_individual_dosers:]: peer_doser.send_and_ping(msg_tx(large_orphan)) # Something was evicted; the orphanage does not contain all large orphans + the 1p1c child @@ -480,12 +498,12 @@ class PackageRelayTest(BitcoinTestFramework): peer_normal = node.add_p2p_connection(P2PInterface()) - # 2 sets of peers: the first set all send the same batch_size orphans. The second set each - # sends batch_size distinct orphans. + # The first set of peers all send the same batch_size orphans. Then a single peer sends + # batch_single_doser distinct orphans. batch_size = 51 num_peers_shared = 60 - num_peers_unique = 40 - + batch_single_doser = 100 + assert_greater_than(num_peers_shared * batch_size + batch_single_doser, 3000) # 60 peers * 51 orphans = 3060 announcements shared_orphans = [self.create_small_orphan() for _ in range(batch_size)] self.log.info(f"Send the same {batch_size} orphans from {num_peers_shared} DoSy peers (may take a while)") @@ -525,22 +543,15 @@ class PackageRelayTest(BitcoinTestFramework): node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY) peer_normal.wait_for_getdata([parent_txid_int]) - # Each of the num_peers_unique peers creates a distinct set of orphans - many_orphans = [self.create_small_orphan() for _ in range(batch_size * num_peers_unique)] + self.log.info(f"Send {batch_single_doser} new orphans from one DoSy peer") + peer_doser_batch = node.add_p2p_connection(P2PInterface()) + this_batch_orphans = [self.create_small_orphan() for _ in range(batch_single_doser)] + for tx in this_batch_orphans: + # Don't wait for responses, because it dramatically increases the runtime of this test. + peer_doser_batch.send_without_ping(msg_tx(tx)) - self.log.info(f"Send sets of {batch_size} orphans from {num_peers_unique} DoSy peers (may take a while)") - for peernum in range(num_peers_unique): - peer_doser_batch = node.add_p2p_connection(P2PInterface()) - this_batch_orphans = many_orphans[batch_size*peernum : batch_size*(peernum+1)] - for tx in this_batch_orphans: - # Don't wait for responses, because it dramatically increases the runtime of this test. - peer_doser_batch.send_without_ping(msg_tx(tx)) - - # Ensure at least one of the peer's orphans shows up in getorphantxs. Since each peer is - # reserved a portion of orphanage space, this must happen as long as the orphans are not - # rejected for some other reason. - peer_doser_batch.sync_with_ping() - self.wait_until(lambda: any([tx.txid_hex in node.getorphantxs() for tx in this_batch_orphans])) + peer_doser_batch.sync_with_ping() + self.wait_until(lambda: any([tx.txid_hex in node.getorphantxs() for tx in this_batch_orphans])) self.log.info("Check that orphan from normal peer still exists in orphanage") assert high_fee_child["txid"] in node.getorphantxs()