Skip to content
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

Merged
merged 12 commits into from
Jan 22, 2024

Conversation

colin-axner
Copy link
Contributor

@colin-axner colin-axner commented Jan 18, 2024

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

type: commit message

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.

  • Targeted PR against the correct branch (see CONTRIBUTING.md).
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the module structure standards and Go style guide.
  • Wrote unit and integration tests.
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/).
  • Added relevant godoc comments.
  • Provide a commit message to be used for the changelog entry in the PR description for review.
  • Re-reviewed Files changed in the Github PR explorer.
  • Review Codecov Report in the comment section below once CI passes.

@colin-axner colin-axner added audit Feedback from implementation audit priority PRs that need prompt reviews channel-upgradability Channel upgradability feature backport-to-v8.1.x labels Jan 18, 2024
@colin-axner colin-axner self-assigned this Jan 18, 2024
@colin-axner colin-axner added this to the 04-channel upgrades RC milestone Jan 18, 2024
@colin-axner colin-axner changed the title fix: double spend solution on channel upgrades using counterparty next sequence send fix: add replay protection on upgraded channels Jan 22, 2024
@colin-axner colin-axner marked this pull request as ready for review January 22, 2024 12:17
Copy link
Contributor

@damiannolan damiannolan left a 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.
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 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

Copy link
Contributor

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"

Copy link
Contributor Author

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

what a juicy test 🚀 🥇

Copy link
Contributor

@chatton chatton left a 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")
Copy link
Contributor

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?

Suggested change
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")

Copy link
Contributor Author

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

ibcmock "github.com/cosmos/ibc-go/v8/testing/mock"
)

// If packet receipts are pruned, it is possible to double spend via a
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// 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

Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@crodriguezvega crodriguezvega left a 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")
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

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)

Copy link
Contributor

@charleenfei charleenfei left a 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!

Copy link
Member

@AdityaSripal AdityaSripal left a 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 !!

@colin-axner
Copy link
Contributor Author

I made setRecvStartSequence private to avoid any accidental pitfalls

Copy link
Contributor

@DimitrisJim DimitrisJim left a 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!

@colin-axner colin-axner merged commit 8b6932b into main Jan 22, 2024
63 checks passed
@colin-axner colin-axner deleted the colin/double-spend-option2 branch January 22, 2024 16:08
mergify bot pushed a commit that referenced this pull request Jan 22, 2024
* 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)
crodriguezvega pushed a commit that referenced this pull request Jan 23, 2024
* 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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
audit Feedback from implementation audit channel-upgradability Channel upgradability feature priority PRs that need prompt reviews
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

7 participants