-
Notifications
You must be signed in to change notification settings - Fork 37.7k
[NO MERGE] BIP331 Ancestor Package Relay #27742
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
base: master
Are you sure you want to change the base?
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process. ConflictsReviewers, this pull request conflicts with the following ones:
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. |
8327584
to
ae55561
Compare
9cfb775
to
cdb5b16
Compare
didn't real whole OP until now. In my not expert opinion, the orphan handling seems reasonable, and I think it makes to open it ASAP to take it out of draft |
Offline feedback suggested I clarify what I mean by "approach feedback welcome" before "I open separate PRs." This is a large project, and the first few p2p commits essentially define the interface. I'd like to get rough consensus on approach before we start looking at code details and merging PRs, because I believe it will help us "get useful stuff in" faster and avoid premature optimizations. Here are some approach questions I think we should answer before diving in:
|
Is the idea here that protection is a bandiwdth optimization to avoid fetching an orphan twice in a row in the presence of malicious peers or random churn? Reasonable if so, just want this to be explicitly stated if so. |
Yes exactly. Worse is we want to avoid a situation where we requested the rest of the package while the orphan was in our orphanage, but then it fell out while we were waiting for the rest of the transactions. Then, we should re-download the orphan, but what do we do with the rest of them? We basically have to redownload the whole thing again, otherwise we might end up in an endless cycle of redownloading if we put them into an orphanage but don't necessarily protect them from eviction. Here is a more detailed doc for the orphanage protection and "token bucket" scheme: https://gist.github.com/glozow/7c0ff639f996e660146314edffa6f06c. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Along with the gist, focused on orphan protection and bucketing for concept understanding. Dumping comments related to those, and left a note on the gist.
|
||
// Skip if already requested in the (recent-ish) past. | ||
if (packageinfo_requested.contains(GetPackageInfoRequestId(nodeid, wtxid, PKG_RELAY_ANCPKG))) return; | ||
|
||
// Skip if this peer is already using a lot of space in the orphanage. | ||
if (m_orphanage.BytesFromPeer(nodeid) > MAX_ORPHANAGE_USAGE) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BytesFromPeer
counts all announced orphans, even if duplicates, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, think we should use the new orphan tx size as well here to avoid going past MAX_ORPHANAGE_USAGE?
@@ -303,6 +376,9 @@ class TxPackageTracker::Impl { | |||
auto pending_iter = pending_package_info.find(packageid); | |||
Assume(pending_iter != pending_package_info.end()); | |||
if (pending_iter != pending_package_info.end()) { | |||
for (const auto& [wtxid, missing] : pending_iter->second.m_txdata_status) { | |||
if (!missing) MaybeUnprotectOrphan(nodeid, wtxid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the conditional guarding against?
* that the number of non-protected orphan entries does not exceed 100. Afterward, Size() may | ||
* return a number greater than 100. It is the caller's responsibility to ensure that not too | ||
* many orphans are protected. | ||
*/ | ||
void LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename arg max_unprotected_orphans
?
auto it_last = m_orphan_list.back(); | ||
m_orphan_list[old_pos] = it_last; | ||
it_last->second.list_pos = old_pos; | ||
if (it->second.list_pos >= 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this function ever be called with a protected orphan? Tests don't cover these lines for protected peers(added an assert, never hit), and seems suspect that it would delete things below this block if it's protected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this function ever be called with a protected orphan?
Yes. It's called when orphans expire as well.
Tests don't cover these lines for protected peers
Sounds like I neglected to write a test for this. Expiry is pretty long, but theoretically we can hit this if we have multiple peers stalling us on the resolution of this orphan. We might happen to be trying package relay with one of the peers when the expiration is reached.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Didn't put 2 and 2 together. Comment-worthy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Along with the gist, focused on orphan protection and bucketing for concept understanding. Dumping comments related to those, and left a note on the gist.
@@ -72,6 +72,13 @@ class TxOrphanage { | |||
LOCK(m_mutex); | |||
return m_total_orphan_bytes; | |||
} | |||
size_t BytesFromPeer(NodeId peer) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this introduces an interdependency between the number of outgoing transaction bytes we can receive from a peer and the maximum flow of LN commitment transaction+CPFP in weight a direct transaction-relay peer can announce to us and therefore we can validate. An option_anchors_zero_fee_htlc_tx
LN commitment transaction weight is 900 WU with no pending HTLCs. As the orphan memory usage is implemented, it’s unclear if there is any processing guarantees for a peer announcing transaction to us, such processing guarantees are hard to enforce as we would be dependent on the full-node host performance.
Note current MAX_ORPHANAGE_USAGE
is defined as MAX_ORPHANAGE_USAGE{10*MAX_ORPHANAGE_PROTECTION_OUTBOUND}
though it sounds applied on processing inbound announcement (L276 in src/node/txpackagetracker.cpp
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Orphan usage would effect the CPFP transactions, IIUC, not the commitment transactions. The CPFP is deemed high enough, sent to the peer, the peer holds the child tx in orphan set and requests the full package, which isn't size-bound(beyond current mempool limits).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be sure we’re in sync on BytesFromPeer
and MAX_ORPHANAGE_USAGE
, it’s correct this a per-peer limit on the space in vbytes occupied by received orphans ?
If yes, I think this still introduce an interdependency between the number of outgoing transactions bytes and the maximum flow of LN commitment transactions and this limit should be documented (a la doc/policy/
), or even better announced as part of sendpackages
info.
@@ -58,6 +59,36 @@ class TxPackageTracker { | |||
/** Return how many entries exist in the orphange */ | |||
size_t OrphanageSize(); | |||
|
|||
/** Received an announcement from this peer for a tx we already know is an orphan; should be | |||
* called for every peer that announces the tx, even if they are not a package relay peer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a argument that can be made if memory and throughputs limits (e.g MAX_PEER_ANNOUNCEMENTS
) are applied in function of the type of transactions received (e.g v3 transactions) and that can be used to nudge multi-party and contracting protocols transactions broadcasters to use fee-bumping efficient mechanism such as one CPFP covering multiple parents due to the package limits. This would be a discount incentive by analogy with SegWit spending discounted by consensus validation rules.
On the other hand, such incentive might have “bad incentives” as such package topology might be unsafe (i.e if all the parents confirmation are time-sensitive) and this can become a further complication of our transaction-relay stack.
There is still a last open question if such lack of dissociation between type of traffic (e.g packages from simple transactions) could be abused when package validation is deployed, and CPU performance processing asymmetries (i.e AcceptSingleTransaction
vs AcceptMultipleTransactions
vs AcceptPackageWrappingSingle
vs AcceptAncestorPackage
) between our code paths could be exploited by a transaction-relay jamming adversary.
LOCK(cs_main); | ||
// We won't re-validate the exact same transaction or package again. | ||
if (m_recent_rejects_reconsiderable.contains(GetPackageHash(package.m_unvalidated_txns))) { | ||
// Should we do anything else here? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So m_recent_rejects_reconsiderable
logs transaction on its wtxid (which is good to avoid witness inflation changing the result) however isn’t that two restrictive if you have PKG1 submitted with A+B, A is too low with B and then you have A+B’ where A is good enough ? Or is that TX_LOW_FEE
when the transaction isn’t good enough to meet “mempool min fee not meet” / “min relay fee not met” ?
If it’s correct, I would still favor to exempt package transaction from min relay fee checks as it’s some awful interdependency for pre-signed lightning transaction :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's the idea of "reconsiderable." If you get A again with a different package, you reconsider it. If you get A+B again, you don't. In this set of changes, TX_LOW_FEE
is used to decide to put something in the reconsiderable cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay “reconsiderable” makes sense to me, still have to review it’s correctly implemented.
If it’s correct, I would still favor to exempt package transaction from min relay fee checks as it’s some awful interdependency for pre-signed lightning transaction :)
For this concern, in fact it’s already addressed by the latest state of bip 331 code and nversion=3 documentation, confusion is mine, I thought mempool min fee checks on parent was still present from previous package / acceptance relay reviews.
if (single_res_it != subpackage_result.m_tx_results.end()) { | ||
const auto single_res = single_res_it->second; | ||
if (single_res.m_state.GetResult() != TxValidationResult::TX_MEMPOOL_POLICY && | ||
single_res.m_state.GetResult() != TxValidationResult::TX_LOW_FEE && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this check is correct in what it aims to achieve, i.e not apply the two mempool min fee checks on ancestor package transaction, if it received as such from AcceptAncestorPackage
/ ProcessNewPackage
, however this is unclear if this exemption apply for all BIP331 package, indifferently of their transactions versions, or only for nversion=3 ones.
For context, see lightningdevkit/rust-lightning#2415 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments on part 2 (Negotiate Package Relay, Request Ancestor Package Info For Orphans) - will move on to part 3 soon.
} | ||
uint256 GetPackageHash(const std::vector<CTransactionRef>& transactions) | ||
{ | ||
std::vector<uint256> wtxids_copy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wtxids
, this one is not a copy.
* Memory used: 1.3 MB | ||
* FIXME: this filter can probably be smaller, but how much smaller? | ||
*/ | ||
CRollingBloomFilter m_recent_rejects_reconsiderable GUARDED_BY(::cs_main){120'000, 0.000'001}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3494f96:
Do we need to adjust the logic in ProcessOrphanTx()
too, by inserting there into m_recent_rejects_reconsiderable
instead of m_recent_rejects
if the reason was low-fee? It currently adds the wtxid of a reconsidered orphan to m_recent_rejects
if it fails for policy/fee-related reasons. But if that resolved orphan is itself one of the parent of another orphan we wouldn't consider that (possibly high-fee) child because a parent was rejected.
@@ -5077,6 +5131,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, | |||
// If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as | |||
// completed in TxRequestTracker. | |||
m_txrequest.ReceivedResponse(pfrom.GetId(), inv.hash); | |||
} else if (inv.IsMsgAncPkgInfo() && m_enable_package_relay) { | |||
if (!m_txpackagetracker->PkgInfoAllowed(pfrom.GetId(), inv.hash, node::PKG_RELAY_ANCPKG)) { | |||
LogPrint(BCLog::NET, "\nUnsolicited ancpkginfo sent, disconnecting peer=%d\n", pfrom.GetId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's an unsolicited NOTFOUND
of type MSG_ANCPKGINFO
, would be good to distinguish between the inv type and the actual ANCPKGINFO
message.
src/node/txpackagetracker.cpp
Outdated
if (!packageinfo_requested.contains(GetPackageInfoRequestId(nodeid, wtxid, version))) { | ||
return false; | ||
} | ||
orphan_request_tracker.ReceivedResponse(nodeid, wtxid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PkgInfoAllowed()
calling ReceivedResponse()
as a side effect is surprising to me. Wouldn't it be better if the caller would also have to explicitly call ForgetPkgInfo()
if they want to forget about the package? This is also not mentioned in its doc.
Assume(info_per_peer.find(nodeid) != info_per_peer.end()); | ||
// Add the orphan's wtxid as-is. | ||
LogPrint(BCLog::TXPACKAGES, "\nResolving orphan %s, requesting by ancpkginfo from peer=%d\n", gtxid.GetHash().ToString(), nodeid); | ||
results.emplace_back(gtxid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the splitting into commits is not ideal here: In ae26647, we add logic to return the wtxid, expecting it to be requested by MSG_ANCPKGINFO
. But the net_processing code to actually do this is only added in dd6fb13, so that at ae26647, we'd send out a MSG_TX
getdata, thus re-requesting a TX we already know. These two things should go together into one commit.
@@ -3440,6 +3457,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, | |||
|
|||
if (greatest_common_version >= WTXID_RELAY_VERSION) { | |||
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::WTXIDRELAY)); | |||
if (m_enable_package_relay) { | |||
m_txpackagetracker->ReceivedVersion(peer->m_id); | |||
if (!m_ignore_incoming_txs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also disable sending "sendpackages" for outgoing block-relay-only connections and probably also feelers, would suggest to just use RejectIncomingTxs()
.
Also, I think this should be moved up and combined with the m_enable_package_relay
check two lines above, because I see no reason to call m_txpackagetracker->ReceivedVersion()
in these scenarios.
🐙 This pull request conflicts with the target branch and needs rebase. |
I suspect this implementation, with an absurdly high ancestor chain limit being set, would violate the 100 txn limit for |
Are you sure? We still require an |
Added check that outbound |
Dropping note from offline discussion: probably worthwhile to just to advise users that setting ancestor count higher than 25 may end in weird behavior with chains longer than what package relay can support. I don't know of anyone who sets this value higher manually for production usage, and the value can be updated later easily as an implementation detail. |
* | ||
* Upon receiving an announcement for a transaction, if it exists in this filter, do not | ||
* download the txdata. Upon receiving a package info, if it contains a transaction in this | ||
* filter, do not download the tx data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can say do not download "the whole package data", if this is referencing to the check L4415 in src/net_processing.cpp
as pkg info itself is discarded.
There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
Not intended for merge. Not planning on rebasing right now since this should be built on top of cluster mempool and other WIP projects. |
⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
e518a8b [functional test] opportunistic 1p1c package submission (glozow) 87c5c52 [p2p] opportunistically accept 1-parent-1-child packages (glozow) 6c51e1d [p2p] add separate rejections cache for reconsiderable txns (glozow) 410ebd6 [fuzz] break out parent functions and add GetChildrenFrom* coverage (glozow) d095316 [unit test] TxOrphanage::GetChildrenFrom* (glozow) 2f51cd6 [txorphanage] add method to get all orphans spending a tx (glozow) 092c978 [txpackages] add canonical way to get hash of package (glozow) c3c1e15 [doc] restore comment about why we check if ptx HasWitness before caching rejected txid (glozow) 6f4da19 guard against MempoolAcceptResult::m_replaced_transactions (glozow) Pull request description: This enables 1p1c packages to propagate in the "happy case" (i.e. not reliable if there are adversaries) and contains a lot of package relay-related code. See #27463 for overall package relay tracking. Rationale: This is "non-robust 1-parent-1-child package relay" which is immediately useful. - Relaying 1-parent-1-child CPFP when mempool min feerate is high would be a subset of all package relay use cases, but a pretty significant improvement over what we have today, where such transactions don't propagate at all. [1] - Today, a miner can run this with a normal/small maxmempool to get revenue from 1p1c CPFP'd transactions without losing out on the ones with parents below mempool minimum feerate. - The majority of this code is useful for building more featureful/robust package relay e.g. see the code in #27742. The first 2 commits are followups from #29619: - #29619 (comment) - #29619 (comment) Q: What makes this short of a more full package relay feature? (1) it only supports packages in which 1 of the parents needs to be CPFP'd by the child. That includes 1-parent-1-child packages and situations in which the other parents already pay for themselves (and are thus in mempool already when the package is submitted). More general package relay is a future improvement that requires more engineering in mempool and validation - see #27463. (2) We rely on having kept the child in orphanage, and don't make any attempt to protect it while we wait to receive the parent. If we are experiencing a lot of orphanage churn (e.g. an adversary is purposefully sending us a lot of transactions with missing inputs), we will fail to submit packages. This limitation has been around for 12+ years, see #27742 which adds a token bucket scheme for protecting package-related orphans at a limited rate per peer. (3) Our orphan-handling logic is somewhat opportunistic; we don't make much effort to resolve an orphan beyond asking the child's sender for the parents. This means we may miss packages if the first sender fails to give us the parent (intentionally or unintentionally). To make this more robust, we need receiver-side logic to retry orphan resolution with multiple peers. This is also an existing problem which has a proposed solution in #28031. [1]: see this writeup and its links https://github.com/bitcoin/bips/blob/02ec218c7857ef60914e9a3d383b68caf987f70b/bip-0331.mediawiki#propagate-high-feerate-transactions ACKs for top commit: sr-gi: tACK e518a8b instagibbs: reACK e518a8b theStack: Code-review ACK e518a8b 📦 dergoegge: light Code review ACK e518a8b achow101: ACK e518a8b Tree-SHA512: 632579fbe7160cb763bbec6d82ca0dab484d5dbbc7aea90c187c0b9833b8d7c1e5d13b8587379edd3a3b4a02a5a1809020369e9cd09a4ebaf729921f65c15943
⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
1 similar comment
⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
…ution 86d7135 [p2p] only attempt 1p1c when both txns provided by the same peer (glozow) f7658d9 [cleanup] remove p2p_inv from AddTxAnnouncement (glozow) 063c132 [functional test] getorphantxs reflects multiple announcers (glozow) 0da693f [functional test] orphan handling with multiple announcers (glozow) b6ea4a9 [p2p] try multiple peers for orphan resolution (glozow) 1d2e1d7 [refactor] move creation of unique_parents to helper function (glozow) c6893b0 [txdownload] remove unique_parents that we already have (glozow) 163aaf2 [fuzz] orphanage multiple announcer functions (glozow) 22b023b [unit test] multiple orphan announcers (glozow) 96c1a82 [unit test] TxOrphanage EraseForBlock (glozow) 04448ce [txorphanage] add GetTx so that orphan vin can be read (glozow) e810842 [txorphanage] support multiple announcers (glozow) 62a9ff1 [refactor] change type of unique_parents to Txid (glozow) 6951ddc [txrequest] GetCandidatePeers (glozow) Pull request description: Part of #27463. (Transaction) **orphan resolution** is a process that kicks off when we are missing UTXOs to validate an unconfirmed transaction. We currently request missing parents by txid; BIP 331 also defines a way to [explicitly request ancestors](https://github.com/bitcoin/bips/blob/master/bip-0331.mediawiki#handle-orphans-better). Currently, when we find that a transaction is an orphan, we only try to resolve it with the peer who provided the `tx`. If this doesn't work out (e.g. they send a `notfound` or don't respond), we do not try again. We actually can't, because we've already forgotten who else could resolve this orphan (i.e. all the other peers who announced the transaction). What is wrong with this? It makes transaction download less reliable, particularly for 1p1c packages which must go through orphan resolution in order to be downloaded. Can we fix this with BIP 331 / is this "duct tape" before the real solution? BIP 331 (receiver-initiated ancestor package relay) is also based on the idea that there is an orphan that needs resolution, but it's just a new way of communicating information. It's not inherently more honest; you can request ancestor package information and get a `notfound`. So ancestor package relay still requires some kind of procedure for retrying when an orphan resolution attempt fails. See the #27742 implementation which builds on this orphan resolution tracker to keep track of what packages to download (it just isn't rebased on this exact branch). The difference when using BIP 331 is that we request `ancpkginfo` and then `pkgtxns` instead of the parent txids. Zooming out, we'd like orphan handling to be: - Bandwidth-efficient: don't have too many requests out at once. As already implemented today, transaction requests for orphan parents and regular download both go through the `TxRequestTracker` so that we don't have duplicate requests out. - Not vulnerable to censorship: don't give up too easily, use all candidate peers. See e.g. https://bitcoincore.org/en/2024/07/03/disclose_already_asked_for/ - Load-balance between peers: don't overload peers; use all peers available. This is also useful for when we introduce per-peer orphan protection, since each peer will have limited slots. The approach taken in this PR is to think of each peer who announces an orphan as a potential "orphan resolution candidate." These candidates include: - the peer who sent us the orphan tx - any peers who announced the orphan prior to us downloading it - any peers who subsequently announce the orphan after we have started trying to resolve it For each orphan resolution candidate, we treat them as having "announced" all of the missing parents to us at the time of receipt of this orphan transaction (or at the time they announced the tx if they do so after we've already started tracking it as an orphan). We add the missing parents as entries to `m_txrequest`, incorporating the logic of typical txrequest processing, which means we prefer outbounds, try not to have duplicate requests in flight, don't overload peers, etc. ACKs for top commit: marcofleon: Code review ACK 86d7135 instagibbs: reACK 86d7135 dergoegge: Code review ACK 86d7135 mzumsande: ACK 86d7135 Tree-SHA512: 618d523b86e60c3ea039e88326d50db4e55e8e18309c6a20e8f2b10ed9e076f1de0315c335fd3b8abdabcc8b53cbceb66fb59147d05470ea25b83a2b4bd9c877
⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
1 similar comment
⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
WORK IN PROGRESS. Please do not run it for any use other than testing.
This PR is not meant for merge. This branch exists to help reviewers see what package relay would look like all together. I will open separate PRs for these components and expect to add more tests, docs, and polish along the way. This PR contains all of the functionality built in a linear manner. However, since there are pieces in various areas of the codebase and they can make progress in parallel, commits don't necessarily need to be merged in this order.
See #27463 for what PR(s) are open for review right now.
Note to Reviewers
The major purpose of this PR is to solicit "approach" review.
This is a large project, and the first few p2p commits essentially define the interface. I'd like to get rough consensus on approach before we start looking at code details and merging PRs, because I believe it will help us "get useful stuff in" faster and avoid premature optimizations.
Here are some questions I hope are answered before we try to merge anything (originally #27742 (comment)):
Comments about naming, typos, code details, etc. are also appreciated but please note I may wait until their respective PRs are open to incorporate your comments. Thank you for your patience.
Design Docs
BIP: bitcoin/bips#1382
Orphanage changes: https://gist.github.com/glozow/7c0ff639f996e660146314edffa6f06c
Project Structure
3 Major Milestones
This project is split into 3 milestones, each of which gives us something useful.
Modularize and improve orphan-handling (also some refactoring).
TxDownloadManager
class, responsible for downloading transactions that have been announced to us.TxRequestTracker
Orphan Request Tracker which helps track orphans we need to resolve. Preferentially request orphan resolution from outbounds, preferred relay, etc., over inbounds.When package relay peers are available, use
ancpkginfo
instead of missing parents when handling orphans.sendpackages
negotiation logic.getdata(MSG_ANCPKGINFO)
to package relay peers for orphan resolution. Useancpkginfo
to request missing ancestors through normal individual transaction relay.Download and validate ancestor packages using
getpkgtxns
andpkgtxns
.getpkgtxns
andpkgtxns
. Send apkgtxns
using the list of missing transactions fromancpkginfo
.Todo improvements
These could be added to the milestones or deferred until basic functionality is deployed.
PackageToValidate
inGetTxToReconsider
.