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

chore(core, sequencer): move stateless checks to domain type parsing #1770

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

ethanoroshiba
Copy link
Contributor

@ethanoroshiba ethanoroshiba commented Oct 30, 2024

Summary

Moved stateless checks for Ics20Withdrawal and BridgeUnlock to occur when the types are parsed from protobuf.

Background

Made more sense to check these fields sooner in the process rather than later, and be able to consolidate some of the checks that are shared between BridgeUnlock and Ics20Withdrawal.

Changes

  • Moved stateless checks for Ics20Withdrawal and BridgeUnlock to profobuf try_from_raw and try_from_raw_ref impls.
  • Changed Ics20Withdrawal to be an enum whose variants represent a user withdrawal and a withdrawal from rollup.

Testing

Passing all tests.

Changelogs

No updates required.

Related Issues

closes #1430

@github-actions github-actions bot added the sequencer pertaining to the astria-sequencer crate label Oct 30, 2024
if timeout_time == 0 {
return Err(Ics20WithdrawalError::invalid_timeout_time());
}
if amount == 0 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we even need to check this? Protobuf represents 0 values as empty, and when decoding this it should result in None I think

Copy link
Member

Choose a reason for hiding this comment

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

I don't even think we should reject this. If somebody wants to send (and pay for) a transfer, why would we not permit it?

Copy link
Contributor Author

@ethanoroshiba ethanoroshiba Nov 14, 2024

Choose a reason for hiding this comment

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

Removed in 84b00ef. I believe this has been a topic of discussion before, but that as of right now we don't allow empty transfers? I could be wrong, though.

Comment on lines +1061 to +1062
let parsed_withdrawal: Ics20WithdrawalFromRollup =
serde_json::from_str(&memo).map_err(Ics20WithdrawalError::parse_memo)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should we edit our domain type Ics20Withdrawal to then include the parsed withdrawal as well? Then, we don't have to parse again during check_and_execute

Copy link
Member

Choose a reason for hiding this comment

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

Yes, absolutely. Easiest is probably to just make Ics20Withdrawal an enum over 2 variants (one where bridge_address is set and contains the parsed payload, and one where it's not set and just contains the passed through String memo).

You should also lean on a serde(try_from = "") to a) first read into some UnverifiedIcs20WithdrawalFromRollup, and then b) via a TryFrom<UnverifiedIcs20WithdrawalFromRollup> Ics20WithdrawalFromRollup come up with the final type.

Copy link
Contributor Author

@ethanoroshiba ethanoroshiba Nov 14, 2024

Choose a reason for hiding this comment

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

Split into enum in 84b00ef. I wanted to ask further before continuing with your second suggestion: in order to do this we would need to implement a new UnverifiedIcs20WithdrawalFromRollup type and then the TryFrom trait as well for it. While this may be fine, it would be a little bit backwards from the way we currently do things.

Ics20WithdrawalFromRollup is the raw protobuf type, which usually represents the "unverified" version as opposed to the verified one. We could rename it to reflect that it is unverified, and then create a new domain type for the verified version, but I'm not sure if the name change would be proto breaking. In addition to this, it seems like it would be many more lines for questionable benefit to just performing these verifications in Ics20Withdrawal::try_from_raw. Do you still think it would be worth it to do this?

@ethanoroshiba ethanoroshiba marked this pull request as ready for review October 30, 2024 18:18
@ethanoroshiba ethanoroshiba requested a review from a team as a code owner October 30, 2024 18:18
if timeout_time == 0 {
return Err(Ics20WithdrawalError::invalid_timeout_time());
}
if amount == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

I don't even think we should reject this. If somebody wants to send (and pay for) a transfer, why would we not permit it?

@@ -1107,8 +1136,20 @@ impl Protobuf for Ics20Withdrawal {
.transpose()
.map_err(Ics20WithdrawalError::invalid_bridge_address)?;

if bridge_address.is_some() {
Copy link
Member

Choose a reason for hiding this comment

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

I am pretty unhappy that we now have a JSON formatted payload in here. I wonder if we can make a breaking change to these transactions and add another variant to the protobuf that makes this a protobuf.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Considering this would be network breaking, I'm not sure what we can really do about it now, barring a hard fork :/

We could make a new action which uses a protobuf-formatted memo instead and follow the same upgrade path as the new ValidatorUpdateV2 action, but I'm not sure how worth it that would be.

Comment on lines +1061 to +1062
let parsed_withdrawal: Ics20WithdrawalFromRollup =
serde_json::from_str(&memo).map_err(Ics20WithdrawalError::parse_memo)?;
Copy link
Member

Choose a reason for hiding this comment

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

Yes, absolutely. Easiest is probably to just make Ics20Withdrawal an enum over 2 variants (one where bridge_address is set and contains the parsed payload, and one where it's not set and just contains the passed through String memo).

You should also lean on a serde(try_from = "") to a) first read into some UnverifiedIcs20WithdrawalFromRollup, and then b) via a TryFrom<UnverifiedIcs20WithdrawalFromRollup> Ics20WithdrawalFromRollup come up with the final type.

@@ -37,6 +37,7 @@ penumbra-proto = { workspace = true }
prost = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde_json = { workspace = true }
Copy link
Member

Choose a reason for hiding this comment

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

Will this change force us to always activate serde for all of astria-core? After all, the memo serde impls are feature gated I think.

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 tried to address this in 84b00ef. Let me know if this is what you had in mind!

@github-actions github-actions bot added conductor pertaining to the astria-conductor crate proto pertaining to the Astria Protobuf spec sequencer-relayer pertaining to the astria-sequencer-relayer crate composer pertaining to composer labels Nov 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
composer pertaining to composer conductor pertaining to the astria-conductor crate proto pertaining to the Astria Protobuf spec sequencer pertaining to the astria-sequencer crate sequencer-relayer pertaining to the astria-sequencer-relayer crate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

refactor stateless checks to be done on types with withdrawals
2 participants