-
Notifications
You must be signed in to change notification settings - Fork 655
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
fix: add replay protection on upgraded channels #5651
Conversation
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.
Looks great, lgtm! I just left a minor suggestion on godoc.
Thanks @colin-axner!
@@ -623,17 +623,17 @@ func (k Keeper) HasInflightPackets(ctx sdk.Context, portID, channelID string) bo | |||
return iterator.Valid() | |||
} | |||
|
|||
// SetPruningSequenceEnd sets the channel's pruning sequence end to the store. | |||
func (k Keeper) SetPruningSequenceEnd(ctx sdk.Context, portID, channelID string, sequence uint64) { | |||
// SetRecvStartSequence sets the channel's recv start sequence to the store. |
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 we add an extra note, with something like:
NOTE: RecvStartSequence is only set for channels which have undergone an upgrade. This sequences acts as a checkpoint for pruning historical channel data
could also add a note saying its used as the upper bound when pruning
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.
or maybe something specific like "This sequence indicates the first packet sequence which should be processed after an upgrade"
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 thought about this, but none of the other set functions for nextSequenceRecv
nextSequenceAck
indicate their purpose. I wasn't sure where we should be documenting their behaviour (presumably at least in the spec). I can add a comment in you'd like?
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 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 a juicy test 🚀 🥇
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.
LGTM really nice job with this one, left a few minor wording suggestions but fix looks solid, nice job 🥇
// rejected. | ||
recvStartSequence, _ := k.GetRecvStartSequence(ctx, packet.GetDestPort(), packet.GetDestChannel()) | ||
if packet.GetSequence() < recvStartSequence { | ||
return errorsmod.Wrap(types.ErrPacketReceived, "packet already processed in previous channel upgrade") |
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.
Does previous version
sound a bit better here?
return errorsmod.Wrap(types.ErrPacketReceived, "packet already processed in previous channel upgrade") | |
return errorsmod.Wrap(types.ErrPacketReceived, "packet already processed in a previous channel version") |
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 wanted to stay away from "version" here since you can upgrade a channel without changing its version
modules/core/integration_test.go
Outdated
ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" | ||
) | ||
|
||
// If packet receipts are pruned, it is possible to double spend via a |
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.
// If packet receipts are pruned, it is possible to double spend via a | |
// If packet receipts are pruned, it should not be possible to double spend via a |
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 understood colin's comment to mean that double spending works if the proof is submitted, that's why we added the check against the packet recv seq start in addition to the proof verification? but im not super opinionated, i think it makes sense this way as well.
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.
yea exactly, I can update the comment to be more specific
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 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.
Nice work, @colin-axner!
// rejected. | ||
recvStartSequence, _ := k.GetRecvStartSequence(ctx, packet.GetDestPort(), packet.GetDestChannel()) | ||
if packet.GetSequence() < recvStartSequence { | ||
return errorsmod.Wrap(types.ErrPacketReceived, "packet already processed in previous channel upgrade") |
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.
Would it be possible to do return types.ErrNoOpMsg
instead of returning an error? Just the same as we do in case the packet receipt for the sequence already exists, so that we prevent consuming fees. We could log the error message instead to give this information.
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.
No, in my opinion this is an error. This would mean someone is attempting to receive packets on a channel that has been upgraded to different fields. We added ErrNoOpMsg
so that relayers in a race wouldn't cause each other to fail other relays, but in this situation, I'd say a relayer is either malfunctioning or a user is acting maliciously, in which case I'd much prefer to return an error than to silently fail
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's a possibility one relayer is attempting to flush while another relayer completes the upgrade, but I think this would be very rare and in such cases, I'd still prefer to reject the entire transaction (the relayer is probably flushing stale packets in the tx)
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.
super nice work @colin-axner on the find and the fix!
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.
Excellent work @colin-axner !!
I made |
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.
ignore brain-fart now deleted review, lgtm!
* test: add integration test for double spend attack * refactor: draft alternative approach to fixing double spend * refactor: cleanup tests, deduplicate key storage, add documentation * godoc * test: add packet already recevied unit test case * satisfy the linter * imp: add additional comment to integration test * imp: add a little more info to the test comment * review suggestions + make setRecvStartSeqeuence private (cherry picked from commit 8b6932b)
* test: add integration test for double spend attack * refactor: draft alternative approach to fixing double spend * refactor: cleanup tests, deduplicate key storage, add documentation * godoc * test: add packet already recevied unit test case * satisfy the linter * imp: add additional comment to integration test * imp: add a little more info to the test comment * review suggestions + make setRecvStartSeqeuence private (cherry picked from commit 8b6932b) Co-authored-by: colin axnér <[email protected]> Co-authored-by: Damian Nolan <[email protected]>
Description
After performing a channel upgrade, we have reached a new checkpoint state where we should not process any old packets. Replay protection is currently given by packet receipts on unordered channels and next sequence recv on ordered channels. Because upgrading may result in invalid packet receipt state (either by pruning old packet receipts or upgrading from an ordered to unordered channel) we need to add an additional defensive check which ensures packets are only being received after the next checkpoint sequence, otherwise replay attacks (such as double spends) are possible
closes: #XXXX
Commit Message / Changelog Entry
see the guidelines for commit messages. (view raw markdown for examples)
Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.
docs/
) or specification (x/<module>/spec/
).godoc
comments.Files changed
in the Github PR explorer.Codecov Report
in the comment section below once CI passes.