Description
I've been reviewing the CheckReplay() logic in fsm/transaction.go. I noticed that Canopy uses a combination of timestamp + CreatedHeight (within BlockAcceptanceRange) and checks the indexer via store.GetTxByHash(hashBz) to prevent replay attacks.
The Concern
I have a question regarding the network's resilience during node pruning. If a node prunes its transaction indexer to save storage, and a transaction is still within the BlockAcceptanceRange (4320 blocks) but its record has been removed from the local RIndexerI:
- Will
store.GetTxByHash(hashBz) return nil?
- If it returns
nil, does this allow a previously successful transaction to be accepted again (replayed) by the State Machine within that block window?
Suggested Discussion
Should we consider a more permanent state-based tracking (like nonces) or ensure that BlockAcceptanceRange is strictly tied to the minimum prune height to prevent this edge case?
I'm a "World Builder" within the ecosystem and would love to help clarify or fix this if it's a valid concern.
Description
I've been reviewing the
CheckReplay()logic infsm/transaction.go. I noticed that Canopy uses a combination oftimestamp+CreatedHeight(withinBlockAcceptanceRange) and checks the indexer viastore.GetTxByHash(hashBz)to prevent replay attacks.The Concern
I have a question regarding the network's resilience during node pruning. If a node prunes its transaction indexer to save storage, and a transaction is still within the
BlockAcceptanceRange(4320 blocks) but its record has been removed from the localRIndexerI:store.GetTxByHash(hashBz)returnnil?nil, does this allow a previously successful transaction to be accepted again (replayed) by the State Machine within that block window?Suggested Discussion
Should we consider a more permanent state-based tracking (like nonces) or ensure that
BlockAcceptanceRangeis strictly tied to the minimum prune height to prevent this edge case?I'm a "World Builder" within the ecosystem and would love to help clarify or fix this if it's a valid concern.