Skip to content

solana: Fix logic error in Inbox release process#336

Closed
johnsaigle wants to merge 5 commits intowormhole-foundation:mainfrom
johnsaigle:solana/release-logic
Closed

solana: Fix logic error in Inbox release process#336
johnsaigle wants to merge 5 commits intowormhole-foundation:mainfrom
johnsaigle:solana/release-logic

Conversation

@johnsaigle
Copy link
Copy Markdown
Collaborator

@johnsaigle johnsaigle commented Mar 22, 2024

There are a few suspicious aspects of the inbox release logic:

  1. release_inbound_unlock takes a flag that determines whether a false result from try_release should result in an error being returned. The flag indicates that it should revert on delay. However, try_release can also return false when a transaction has not been approved (regardless of whether or not it is delayed).
    If the flag is set, the transaction will not return an error when a user tries to release a transaction that is not approved even though whether a transaction is approved has nothing to do with its expected behaviour when it is delayed.

Context:

try_release() returns Ok(false) when a transfer is not approved (L37)
https://github.com/wormhole-foundation/example-native-token-transfers/blob/93350abef9c3b520a01dd150a1b09a603c1e1160/solana/programs/example-native-token-transfers/src/queue/inbox.rs#L33-L47

release_inbound_mint (and release_inbound_unlock) return Ok(()) when transfer not approved and the revert_on_delay flag is false (L75)
https://github.com/wormhole-foundation/example-native-token-transfers/blob/93350abef9c3b520a01dd150a1b09a603c1e1160/solana/programs/example-native-token-transfers/src/instructions/release_inbound.rs#L63-L79

  1. The custom error TransferNotApproved is defined but unused in the codebase

Taken together, I believe try_release() should revert with the TransferNotApproved error. This would disambiguate the various 'false' results and provide more intuitive behaviour when a user tries to release a transaction that is not approved.

Current behaviour:

  • if revert_on_delay == false and ReleaseStatus::NotApproved, try_release() returns Ok(())
  • if revert_on_delay == true and ReleaseStatus::NotApproved, try_release() reverts with CantReleaseYet

New behaviour:

  • if revert_on_delay == false and ReleaseStatus::NotApproved, try_release() reverts with TransferNotApproved
  • if revert_on_delay == true and ReleaseStatus::NotApproved, try_release() reverts with TransferNotApproved

Note that funds are safe in both cases. assert statements in both release_inbound functions check that the ReleaseStatus is Redeemed before minting funds.

@johnsaigle johnsaigle requested a review from kcsongor March 22, 2024 19:38
/// Release an inbound transfer and mint the tokens to the recipient.
/// When `revert_on_error` is true, the transaction will revert if the
/// release timestamp has not been reached. When `revert_on_error` is false, the
/// When `revert_on_delay` is true, the transaction will revert if the
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The actual argument is called revert_on_delay. But perhaps this flag should revert on any error and not just the case where the transaction is not ready to be released?

@kcsongor
Copy link
Copy Markdown
Contributor

I think we should keep the existing behaviour, but potentially think of a name for the flag that better describes it? Something like revert_when_not_ready...

The thinking here is that it's convenient for the client to send all the instructions in a single transaction (the transceiver redeem one, then the inbound release one). Whether the transaction can be redeemed immediately cannot be determined on the client side atomically, so the client would either have to perform two transactions, or do some non-trivial computation (involving a lookup) to figure out whether the transceiver redeem ix hits the threshold.

The intention of this flag is definitely to "attempt to redeem if we can". I originally called the flag "revert on error" (as you point out in the comment), but that's not a very good name because the function errors out if the message is already release regardless of the flag.

@johnsaigle
Copy link
Copy Markdown
Collaborator Author

OK, makes sense. I'm happy with renaming the flag.
The last thing I'm wondering about though is this case:

if revert_on_delay == true and ReleaseStatus::NotApproved, try_release() reverts with CantReleaseYet

Maybe this could return TransferNotApproved? It might be helpful for a user to know why their transfer can't be released given that there are two different reasons why. It gives them a way to solve the problem

@nik-suri nik-suri added the solana Change to Solana programs label Mar 25, 2024
@kcsongor
Copy link
Copy Markdown
Contributor

Yeah that makes sense to me. Maybe the cleanest way to do that is to propagate the boolean flag to try_release and have it revert with the appropriate message.

@johnsaigle
Copy link
Copy Markdown
Collaborator Author

Sounds good to me. I'll update this PR

@johnsaigle johnsaigle force-pushed the solana/release-logic branch 5 times, most recently from 065aa32 to b8ce756 Compare April 2, 2024 13:26
@johnsaigle johnsaigle force-pushed the solana/release-logic branch from b8ce756 to c8ae28f Compare May 9, 2024 14:11
johnsaigle added 5 commits May 9, 2024 10:43
There are a few suspicious aspects of the inbox release logic:
1. `release_inbound_unlock` takes a flag that determines whether a
   `false` result from `try_release` should cause an error. The flag
   indicates that it should revert on delay. However, `try_release` can
   also return `false` when a transaction has not been approved. If the
   flag is set, the transaction will not return an error when a user
   tries to release a transaction that is not approved even though
   whether a transaction is approved has nothing to do with its expected
   behaviour when it is delayed
2. The custom error TransferNotApproved is defined but unused

Taken together, I believe `try_release()` should revert with the
TransferNotApproved error. This would disambiguate the various 'false'
results and provide more intuitive behaviour when a user tries to
release a transaction that is not approved.
@johnsaigle johnsaigle force-pushed the solana/release-logic branch from c8ae28f to ed933ac Compare May 9, 2024 14:43
"kind": "struct",
"fields": [
{
"name": "revertOnDelay",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not sure it's worth introducing a breaking change at this point

@nvsriram
Copy link
Copy Markdown
Contributor

Changes merged through #593

@nvsriram nvsriram closed this Jun 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 refactor solana Change to Solana programs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants