P2P data propagation
This page contains notes about how block and transaction data is tracked and propagated by
zcashd
. Most of this behaviour is inherited from Bitcoin Core, but some differences have
developed.
Some of this content is duplicated from in-code comments, but assembling this summary in one place is generally useful for understanding the overall dynamic :)
recentRejects
When a transaction is rejected by AcceptToMemoryPool
, the transaction is added to the
recentRejects
Bloom filter, so that we don't process it again. The Bloom filter resets
whenever the chain tip changes, as previously invalid transactions might become valid.
To prevent DoS attacks against wallets submitting transactions, recentRejects
needs to
store a commitment to the entire transaction. This ensures that if a transaction is
malleated by a network peer to invalidate its authorizing data, the node will ignore
future advertisements of that specific transaction, but still request alternative versions
of the same txid (which might have valid authorizing data).
- For pre-v5 transactions, the txid commits to the entire transaction, and the wtxid is the txid with a globally-fixed (all-ones) suffix.
- For v5+ transactions, the wtxid commits to the entire transaction.
mapOrphanTransactions
Upstream uses this map to store transactions that are rejected by AcceptToMemoryPool
because the node doesn't have their transparent inputs. zcashd
inherits this behaviour
but limits it to purely-transparent transactions (that is, if a transaction contains any
shielded components, the node rejects it as invalid and adds it to recentRejects
).
mapOrphanTransactions
indexes transactions by txid. This means that if an orphan
transaction is received (spending transparent UTXOs the node does not know about), and it
also happens to be invalid for other reasons (subsequent AcceptToMemoryPool
rules that
haven't yet been checked), then the node will not request any v5+ transactions with the
same txid but different authorizing data. This does not create a DoS problem for wallets,
because an adversary that manipulated an orphan transaction to be invalid under the above
constraints would also need to prevent the orphan's parent from entering the mempool, and
eventually a parent is reached that is not an orphan. Once the orphan's direct parent is
accepted, the orphan is re-evaluated, and if it had been manipulated to be invalid, its
wtxid is added to recentRejects
while its txid is removed from mapOrphanTransactions
,
enabling the wallet to rebroadcast the unmodified transaction.