8000 test: tx orphan handling by glozow · Pull Request #28199 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
8000

test: tx orphan handling #28199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 22, 2023
Merged

Conversation

glozow
Copy link
Member
@glozow glozow commented Aug 2, 2023

I was doing some mutation testing (through reckless refactoring) locally and found some specific behaviors in orphan handling that weren't picked up by tests. Adding some of these test cases now can maybe help with reviewing refactors like #28031.

  • Parent requests aren't sent immediately. A delay is added and the requests are filtered by AlreadyHaveTx before they are sent, which means you can't use fake orphans to probe precise arrival timing of a tx.
  • Parent requests include all that are not AlreadyHaveTx. This means old confirmed parents may be requested.
  • The node does not give up on orphans if the peer responds to a parent request with notfound. This means that if a parent is an old confirmed transaction (in which notfound is expected), the orphan should still be resolved.
  • Rejected parents can cause an orphan to be dropped, but it depends on the reason and only based on txid.
  • Rejected parents can cause an orphan to be rejected too, by both wtxid and txid.
  • Requests for orphan parents should be de-duplicated with "regular" txrequest. If a missing parent has the same hash as an in-flight request, it shouldn't be requested.
  • Multiple orphans with overlapping parents should not cause duplicated parent requests.

@glozow glozow added the Tests label Aug 2, 2023
@DrahtBot
Copy link
Contributor
DrahtBot commented Aug 2, 2023

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK dergoegge, achow101, fjahr, instagibbs
Concept ACK jamesob, brunoerg, jonatack, Empact, ajtowns

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #20892 (tests: Run both descriptor and legacy tests within a single test invocation by achow101)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@DrahtBot DrahtBot changed the title functional test: tx orphan handling test: tx orphan handling Aug 2, 2023
@glozow glozow force-pushed the 2023-08-test-orphan-handling branch from bbd5459 to 29ec234 Compare August 2, 2023 11:42
@dergoegge
Copy link
Member

Concept ACK

Good to have these tests prior to refactoring

@jamesob
Copy link
Contributor
jamesob commented Aug 2, 2023

Concept ACK, looks like some great additional coverage.

@brunoerg
Copy link
Contributor
brunoerg commented Aug 2, 2023

Concept ACK

@DrahtBot DrahtBot removed the CI failed label Aug 2, 2023
@jonatack
Copy link
Member
jonatack commented Aug 2, 2023

Concept ACK

Copy link
Member
@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moar coverage good

# Node receives transaction. It attempts to obfuscate the exact timing at which this
# transaction entered its mempool. Send unsolicited because otherwise we need to wait for
# request delays.
peer_normal.send_and_ping(msg_tx(tx_parent_arrives["tx"]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
peer_normal.send_and_ping(msg_tx(tx_parent_arrives["tx"]))
peer_normal.send_and_ping(msg_tx(tx_parent_arrives["tx"]))
assert tx_parent_arrives["txid"] in node.getrawmempool()

my brain for some reason was thinking it was in the orphan pool as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add an rpc to allow querying the orphanage?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add an rpc to allow querying the orphanage?

Could do that, as there's a bit of black box-ness here. Though I also think that the ideal situation would be to write unit tests if we're interested in the exact contents of the orphanage.

@Empact
Copy link
Contributor
Empact commented Aug 3, 2023

Concept ACK

@instagibbs
Copy link
Member

ACK abe8536

#28199 (comment) would be nice to be slightly cleaned up for future readers

@glozow
Copy link
Member Author
glozow commented Aug 8, 2023

thanks for the concept acks @dergoegge @jamesob @brunoerg @Empact @jonatack, would appreciate a review of test too 🙏

Copy link
Member
@dergoegge dergoegge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK abe8536

Copy link
Contributor
@ajtowns ajtowns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK

I think we should probably prefer to test behaviours that are intentional/desirable, rather than behaviours that just happen to be how it got implemented. It's not immediately clear to me which class the things being tested here fall into.

@bitcoin bitcoin deleted a comment from Jones098 Aug 11, 2023
Copy link
Member Author
@glozow glozow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably prefer to test behaviours that are intentional/desirable, rather than behaviours that just happen to be how it got implemented.

Yeah the purpose of writing these tests was largely documentation and not changing behavior unintentionally.

I think most of the things here are intentional, though can think of at least one thing that seems to be "that's just how it got implemented." I was trying to figure out whether we would want to use a notfound for a parent tx to mean we should give up on processing the orphan that depends on it. Currently we don't do anything since we don't track the fact that the request is related to an orphan. But since we just request based on !AlreadyHaveTx we might reject due to a long-confirmed parent. I suppose it would be different if validation told net_processing which inputs were missing and request those. Though that doesn't seem worth implementing if we can just do ancestor package relay.

@glozow glozow force-pushed the 2023-08-test-orphan-handling branch from abe8536 to 7a82b98 Compare August 11, 2023 15:14
@glozow glozow force-pushed the 2023-08-test-orphan-handling branch 2 times, most recently from e6a9d75 to d011a23 Compare August 14, 2023 13:45
glozow added 2 commits August 14, 2023 15:53
Have each TestNode keep track of the last timestamp it called
setmocktime with, and add a bumpmocktime() function to bump by a
number of seconds. Makes it easy to fast forward n seconds without
keeping track of what the last timestamp was.
@glozow glozow force-pushed the 2023-08-test-orphan-handling branch from d011a23 to 9eac5a0 Compare August 14, 2023 14:55
Copy link
Member
@dergoegge dergoegge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reACK 9eac5a0

@DrahtBot DrahtBot requested a review from instagibbs August 14, 2023 15:27
@fanquake fanquake requested a review from ajtowns August 14, 2023 16:13
Copy link
Member
@achow101 achow101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 9eac5a0

self.nodes[0].bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
peer.sync_with_ping()
assert_equal(len(peer.last_message["getdata"].inv), 2)
peer.wait_for_parent_requests([int(txid_conf_old, 16), int(missing_tx["txid"], 16)])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting the already confirmed transaction is surprising to me. Is this expected to change in future work (if so a comment mentioning that would be useful) or is it intended behavior?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting the already confirmed transaction is surprising to me.

Yep, was strange to me too, but AlreadyHaveTx is peerman's best metric for determining what the missing parents are (validation doesn't say which one(s) are missing). Also afaiu it's considered uncommon to (1) have an orphan (2) have a transaction that spends a very old coin, so I'm guessing this is quite rare.

Is this expected to change in future work

I'm not aware of any plans to change the legacy way of requesting orphan parents. But with package relay we'd ask for the list of unconfirmed ancestors, so we wouldn't have this kind of false positive.

assert grandchild["txid"] != grandchild["tx"].getwtxid()

# Relay the parent. It should be rejected because it pays 0 fees.
self.relay_transaction(peer1, parent_low_fee_nonsegwit["tx"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could check that parent_low_fee_nonsegwit is not in the mempool as done in the other test cases.

Copy link
Contributor
@fjahr fjahr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 9eac5a0

...modulo removing the dead code. Otherwise mostly nit-ish comments, I can also open a follow-up myself if someone feels like merging this as is.

self.num_nodes = 1
self.extra_args = [[]]

def create_parent_and_child(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unused.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad!

I can also open a follow-up myself if someone feels like merging this as is.

If it helps, they were written to be used in #28031. I pulled these tests out to merge before the refactors and I guess forgot to remove the helpers that aren't used (yet).

self._getdata_received = []

@property
def tx_received(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unused.

peer.wait_for_parent_requests([int(missing_grandparent["txid"], 16)])

# The node should put the orphan into the orphanage and request missing_parent, skipping
# missing_parent_orphan because it already has it in the orphanage.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could add another assertion here that missing_parent_orphan is indeed only requested once.


confirmed_utxos = [self.wallet_nonsegwit.get_utxo() for _ in range(4)]
assert all([utxo["confirmations"] > 0 for utxo in confirmed_utxos])
self.log.info("Test handling of multiple orphans with missing parents that are already being requested")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would argue that the (or the first) log should always be the first line of the test. Otherwise it may be confusing if an error happens in the code above it may look like the previous test failed.

self.nodes[0].bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
# There are 3 missing parents. missing_parent_A and missing_parent_AB should be requested.
# But inflight_parent_AB should not, because there is already an in-flight request for it.
peer_orphans.wait_for_parent_requests([int(missing_parent_A["txid"], 16), int(missing_parent_AB["txid"], 16)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I was a bit irritated that that inflight_parent_AB not being requested isn't checked here but I see it follows below. For expressiveness you could consider duplicating the check here or adding a small comment.


self.log.info("Test that an orphan with rejected parents, along with any descendants, cannot be retried with an alternate witness")
parent_low_fee_nonsegwit = self.wallet_nonsegwit.create_self_transfer(fee_rate=0)
assert_equal(parent_low_fee_nonsegwit["txid"], parent_low_fee_nonsegwit["tx"].getwtxid())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This sanity check is repeated multiple times. I think it would be enough to do it once when setting up the MiniWallet since that seems to be the only way there could be an issue, if somehow MiniWallet is broken.

@instagibbs
Copy link
Member

reACK 9eac5a0

@DrahtBot DrahtBot removed the request for review from instagibbs August 22, 2023 16:34
@achow101 achow101 merged commit 5aa67eb into bitcoin:master Aug 22, 2023
@glozow glozow deleted the 2023-08-test-orphan-handling branch August 22, 2023 21:41
Frank-GER pushed a commit to syscoin/syscoin that referenced this pull request Sep 8, 2023
9eac5a0 [functional test] transaction orphan handling (glozow)
61e77bb [test framework] make it easier to fast-forward setmocktime (glozow)

Pull request description:

  I was doing some mutation testing (through reckless refactoring) locally and found some specific behaviors in orphan handling that weren't picked up by tests. Adding some of these test cases now can maybe help with reviewing refactors like bitcoin#28031.

  - Parent requests aren't sent immediately. A delay is added and the requests are filtered by AlreadyHaveTx before they are sent, which means you can't use fake orphans to probe precise arrival timing of a tx.
  - Parent requests include all that are not AlreadyHaveTx. This means old confirmed parents may be requested.
  - The node does not give up on orphans if the peer responds to a parent request with notfound. This means that if a parent is an old confirmed transaction (in which notfound is expected), the orphan should still be resolved.
  - Rejected parents can cause an orphan to be dropped, but it depends on the reason and only based on txid.
  - Rejected parents can cause an orphan to be rejected too, by both wtxid and txid.
  - Requests for orphan parents should be de-duplicated with "regular" txrequest. If a missing parent has the same hash as an in-flight request, it shouldn't be requested.
  - Multiple orphans with overlapping parents should not cause duplicated parent requests.

ACKs for top commit:
  instagibbs:
    reACK bitcoin@9eac5a0
  dergoegge:
    reACK 9eac5a0
  achow101:
    ACK 9eac5a0
  fjahr:
    Code review ACK 9eac5a0

Tree-SHA512: 85488dc6a3f62cf0c38e7dfe7839c01215b44b172d1755b18164d41d01038f3a749451241e4eba8b857fd344a445740b21d6382c45977234b21460e3f53b1b2a
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jul 19, 2024
Summary:
```
I was doing some mutation testing (through reckless refactoring) locally and found some specific behaviors in orphan handling that weren't picked up by tests. Adding some of these test cases now can maybe help with reviewing refactors like #28031.
 - Parent requests aren't sent immediately. A delay is added and the requests are filtered by AlreadyHaveTx before they are sent, which means you can't use fake orphans to probe precise arrival timing of a tx.
 - Parent requests include all that are not AlreadyHaveTx. This means old confirmed parents may be requested.
 - The node does not give up on orphans if the peer responds to a parent request with notfound. This means that if a parent is an old confirmed transaction (in which notfound is expected), the orphan should still be resolved.
 - Rejected parents can cause an orphan to be dropped, but it depends on the reason and only based on txid.
 - Rejected parents can cause an orphan to be rejected too, by both wtxid and txid.
 - Requests for orphan parents should be de-duplicated with "regular" txrequest. If a missing parent has the same hash as an in-flight request, it shouldn't be requested.
 - Multiple orphans with overlapping parents should not cause duplicated parent requests.
```

Backport of [[bitcoin/bitcoin#28199 | core#28199]].

The test_orphan_inherit_rejection case makes no sense without segwit so it has been removed, and well as some changes related to the absence of segwit and/or mempool ancestors accounting.

Depends on D16505.

Test Plan:
  ./test/functional/test_runner.py p2p_orphan_handling

Reviewers: #bitcoin_abc, PiRK

Reviewed By: #bitcoin_abc, PiRK

Subscribers: PiRK

Differential Revision: https://reviews.bitcoinabc.org/D16503
roqqit pushed a commit to doged-io/doged that referenced this pull request Aug 1, 2024
Summary:
```
I was doing some mutation testing (through reckless refactoring) locally and found some specific behaviors in orphan handling that weren't picked up by tests. Adding some of these test cases now can maybe help with reviewing refactors like #28031.
 - Parent requests aren't sent immediately. A delay is added and the requests are filtered by AlreadyHaveTx before they are sent, which means you can't use fake orphans to probe precise arrival timing of a tx.
 - Parent requests include all that are not AlreadyHaveTx. This means old confirmed parents may be requested.
 - The node does not give up on orphans if the peer responds to a parent request with notfound. This means that if a parent is an old confirmed transaction (in which notfound is expected), the orphan should still be resolved.
 - Rejected parents can cause an orphan to be dropped, but it depends on the reason and only based on txid.
 - Rejected parents can cause an orphan to be rejected too, by both wtxid and txid.
 - Requests for orphan parents should be de-duplicated with "regular" txrequest. If a missing parent has the same hash as an in-flight request, it shouldn't be requested.
 - Multiple orphans with overlapping parents should not cause duplicated parent requests.
```

Backport of [[bitcoin/bitcoin#28199 | core#28199]].

The test_orphan_inherit_rejection case makes no sense without segwit so it has been removed, and well as some changes related to the absence of segwit and/or mempool ancestors accounting.

Depends on D16505.

Test Plan:
  ./test/functional/test_runner.py p2p_orphan_handling

Reviewers: #bitcoin_abc, PiRK

Reviewed By: #bitcoin_abc, PiRK

Subscribers: PiRK

Differential Revision: https://reviews.bitcoinabc.org/D16503
@bitcoin bitcoin locked and limited conversation to collaborators Aug 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0