-
Notifications
You must be signed in to change notification settings - Fork 76
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,22 +21,25 @@ use crate::{ | |
IncorrectRollupIdLength, | ||
RollupId, | ||
}, | ||
protocol::fees::v1::{ | ||
BridgeLockFeeComponents, | ||
BridgeSudoChangeFeeComponents, | ||
BridgeUnlockFeeComponents, | ||
FeeAssetChangeFeeComponents, | ||
FeeChangeFeeComponents, | ||
FeeComponentError, | ||
IbcRelayFeeComponents, | ||
IbcRelayerChangeFeeComponents, | ||
IbcSudoChangeFeeComponents, | ||
Ics20WithdrawalFeeComponents, | ||
InitBridgeAccountFeeComponents, | ||
RollupDataSubmissionFeeComponents, | ||
SudoAddressChangeFeeComponents, | ||
TransferFeeComponents, | ||
ValidatorUpdateFeeComponents, | ||
protocol::{ | ||
fees::v1::{ | ||
BridgeLockFeeComponents, | ||
BridgeSudoChangeFeeComponents, | ||
BridgeUnlockFeeComponents, | ||
FeeAssetChangeFeeComponents, | ||
FeeChangeFeeComponents, | ||
FeeComponentError, | ||
IbcRelayFeeComponents, | ||
IbcRelayerChangeFeeComponents, | ||
IbcSudoChangeFeeComponents, | ||
Ics20WithdrawalFeeComponents, | ||
InitBridgeAccountFeeComponents, | ||
RollupDataSubmissionFeeComponents, | ||
SudoAddressChangeFeeComponents, | ||
TransferFeeComponents, | ||
ValidatorUpdateFeeComponents, | ||
}, | ||
memos::v1::Ics20WithdrawalFromRollup, | ||
}, | ||
Protobuf, | ||
}; | ||
|
@@ -1032,12 +1035,19 @@ impl Protobuf for Ics20Withdrawal { | |
bridge_address, | ||
use_compat_address, | ||
} = proto; | ||
let amount = amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?; | ||
let amount = u128::from(amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?); | ||
let return_address = Address::try_from_raw( | ||
&return_address.ok_or(Ics20WithdrawalError::field_not_set("return_address"))?, | ||
) | ||
.map_err(Ics20WithdrawalError::return_address)?; | ||
|
||
if timeout_time == 0 { | ||
return Err(Ics20WithdrawalError::invalid_timeout_time()); | ||
} | ||
if amount == 0 { | ||
return Err(Ics20WithdrawalError::invalid_amount()); | ||
} | ||
|
||
let timeout_height = timeout_height | ||
.ok_or(Ics20WithdrawalError::field_not_set("timeout_height"))? | ||
.into(); | ||
|
@@ -1047,8 +1057,20 @@ impl Protobuf for Ics20Withdrawal { | |
.transpose() | ||
.map_err(Ics20WithdrawalError::invalid_bridge_address)?; | ||
|
||
if bridge_address.is_some() { | ||
let parsed_withdrawal: Ics20WithdrawalFromRollup = | ||
serde_json::from_str(&memo).map_err(Ics20WithdrawalError::parse_memo)?; | ||
Comment on lines
+1061
to
+1062
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we edit our domain type |
||
validate_rollup_return_address(&parsed_withdrawal.rollup_return_address) | ||
.map_err(Ics20WithdrawalError::rollup_withdrawal)?; | ||
validate_withdrawal_event_id_and_block_number( | ||
&parsed_withdrawal.rollup_withdrawal_event_id, | ||
parsed_withdrawal.rollup_block_number, | ||
) | ||
.map_err(Ics20WithdrawalError::rollup_withdrawal)?; | ||
} | ||
|
||
Ok(Self { | ||
amount: amount.into(), | ||
amount, | ||
denom: denom.parse().map_err(Ics20WithdrawalError::invalid_denom)?, | ||
destination_chain_address, | ||
return_address, | ||
|
@@ -1089,14 +1111,21 @@ impl Protobuf for Ics20Withdrawal { | |
bridge_address, | ||
use_compat_address, | ||
} = proto; | ||
let amount = amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?; | ||
let amount = u128::from(amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?); | ||
let return_address = Address::try_from_raw( | ||
return_address | ||
.as_ref() | ||
.ok_or(Ics20WithdrawalError::field_not_set("return_address"))?, | ||
) | ||
.map_err(Ics20WithdrawalError::return_address)?; | ||
|
||
if *timeout_time == 0 { | ||
return Err(Ics20WithdrawalError::invalid_timeout_time()); | ||
} | ||
if amount == 0 { | ||
return Err(Ics20WithdrawalError::invalid_amount()); | ||
} | ||
|
||
let timeout_height = timeout_height | ||
.clone() | ||
.ok_or(Ics20WithdrawalError::field_not_set("timeout_height"))? | ||
|
@@ -1107,8 +1136,20 @@ impl Protobuf for Ics20Withdrawal { | |
.transpose() | ||
.map_err(Ics20WithdrawalError::invalid_bridge_address)?; | ||
|
||
if bridge_address.is_some() { | ||
let parsed_withdrawal: Ics20WithdrawalFromRollup = | ||
serde_json::from_str(memo).map_err(Ics20WithdrawalError::parse_memo)?; | ||
validate_rollup_return_address(&parsed_withdrawal.rollup_return_address) | ||
.map_err(Ics20WithdrawalError::rollup_withdrawal)?; | ||
validate_withdrawal_event_id_and_block_number( | ||
&parsed_withdrawal.rollup_withdrawal_event_id, | ||
parsed_withdrawal.rollup_block_number, | ||
) | ||
.map_err(Ics20WithdrawalError::rollup_withdrawal)?; | ||
} | ||
|
||
Ok(Self { | ||
amount: amount.into(), | ||
amount, | ||
denom: denom.parse().map_err(Ics20WithdrawalError::invalid_denom)?, | ||
destination_chain_address: destination_chain_address.clone(), | ||
return_address, | ||
|
@@ -1189,11 +1230,32 @@ impl Ics20WithdrawalError { | |
Self(Ics20WithdrawalErrorKind::InvalidBridgeAddress(err)) | ||
} | ||
|
||
#[must_use] | ||
fn invalid_denom(source: asset::ParseDenomError) -> Self { | ||
Self(Ics20WithdrawalErrorKind::InvalidDenom { | ||
source, | ||
}) | ||
} | ||
|
||
#[must_use] | ||
fn invalid_timeout_time() -> Self { | ||
Self(Ics20WithdrawalErrorKind::InvalidTimeoutTime) | ||
} | ||
|
||
#[must_use] | ||
fn invalid_amount() -> Self { | ||
Self(Ics20WithdrawalErrorKind::InvalidAmount) | ||
} | ||
|
||
#[must_use] | ||
fn parse_memo(err: serde_json::Error) -> Self { | ||
Self(Ics20WithdrawalErrorKind::ParseMemo(err)) | ||
} | ||
|
||
#[must_use] | ||
fn rollup_withdrawal(err: RollupWithdrawalError) -> Self { | ||
Self(Ics20WithdrawalErrorKind::RollupWithdrawal(err)) | ||
} | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
|
@@ -1210,6 +1272,14 @@ enum Ics20WithdrawalErrorKind { | |
InvalidBridgeAddress(#[source] AddressError), | ||
#[error("`denom` field was invalid")] | ||
InvalidDenom { source: asset::ParseDenomError }, | ||
#[error("`timeout_time` must be non-zero")] | ||
InvalidTimeoutTime, | ||
#[error("`amount` must be greater than zero")] | ||
InvalidAmount, | ||
#[error("failed to parse memo for ICS bound bridge withdrawal")] | ||
ParseMemo(#[source] serde_json::Error), | ||
#[error("rollup withdrawal information was invalid")] | ||
RollupWithdrawal(#[source] RollupWithdrawalError), | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
|
@@ -1705,18 +1775,30 @@ impl Protobuf for BridgeUnlock { | |
let to = to | ||
.ok_or_else(|| BridgeUnlockError::field_not_set("to")) | ||
.and_then(|to| Address::try_from_raw(&to).map_err(BridgeUnlockError::address))?; | ||
let amount = amount.ok_or_else(|| BridgeUnlockError::field_not_set("amount"))?; | ||
let amount = u128::from(amount.ok_or_else(|| BridgeUnlockError::field_not_set("amount"))?); | ||
if amount == 0 { | ||
return Err(BridgeUnlockError::invalid_amount()); | ||
} | ||
if memo.len() > 64 { | ||
return Err(BridgeUnlockError::invalid_memo()); | ||
} | ||
let fee_asset = fee_asset.parse().map_err(BridgeUnlockError::fee_asset)?; | ||
|
||
validate_withdrawal_event_id_and_block_number( | ||
&rollup_withdrawal_event_id, | ||
rollup_block_number, | ||
) | ||
.map_err(BridgeUnlockError::rollup_withdrawal)?; | ||
|
||
let bridge_address = bridge_address | ||
.ok_or_else(|| BridgeUnlockError::field_not_set("bridge_address")) | ||
.and_then(|to| Address::try_from_raw(&to).map_err(BridgeUnlockError::bridge_address))?; | ||
Ok(Self { | ||
to, | ||
amount: amount.into(), | ||
amount, | ||
fee_asset, | ||
memo, | ||
bridge_address, | ||
memo, | ||
rollup_block_number, | ||
rollup_withdrawal_event_id, | ||
}) | ||
|
@@ -1766,6 +1848,21 @@ impl BridgeUnlockError { | |
source, | ||
}) | ||
} | ||
|
||
#[must_use] | ||
fn invalid_amount() -> Self { | ||
Self(BridgeUnlockErrorKind::InvalidAmount) | ||
} | ||
|
||
#[must_use] | ||
fn invalid_memo() -> Self { | ||
Self(BridgeUnlockErrorKind::InvalidMemo) | ||
} | ||
|
||
#[must_use] | ||
fn rollup_withdrawal(err: RollupWithdrawalError) -> Self { | ||
Self(BridgeUnlockErrorKind::RollupWithdrawal(err)) | ||
} | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
|
@@ -1778,6 +1875,12 @@ enum BridgeUnlockErrorKind { | |
FeeAsset { source: asset::ParseDenomError }, | ||
#[error("the `bridge_address` field was invalid")] | ||
BridgeAddress { source: AddressError }, | ||
#[error("`amount` must be greater than zero")] | ||
InvalidAmount, | ||
#[error("`memo` muse be no longer than 64 bytes")] | ||
InvalidMemo, | ||
#[error("rollup withdrawal information was invalid")] | ||
RollupWithdrawal(#[source] RollupWithdrawalError), | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
|
@@ -2076,3 +2179,41 @@ impl Protobuf for FeeChange { | |
}) | ||
} | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
enum RollupWithdrawalError { | ||
#[error("rollup return address must be non-empty")] | ||
EmptyRollupReturnAddress, | ||
#[error("rollup return address must be no more than 256 bytes")] | ||
InvalidRollupReturnAddress, | ||
#[error("rollup withdrawal event id must be non-empty")] | ||
EmptyRollupEventId, | ||
#[error("rollup withdrawal event id must be no more than 256 bytes")] | ||
InvalidRollupEventId, | ||
#[error("rollup block number must be non-zero")] | ||
InvalidRollupBlockNumber, | ||
} | ||
|
||
fn validate_rollup_return_address(address: &str) -> Result<(), RollupWithdrawalError> { | ||
if address.is_empty() { | ||
return Err(RollupWithdrawalError::EmptyRollupReturnAddress); | ||
} else if address.len() > 256 { | ||
return Err(RollupWithdrawalError::InvalidRollupReturnAddress); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn validate_withdrawal_event_id_and_block_number( | ||
id: &str, | ||
block: u64, | ||
) -> Result<(), RollupWithdrawalError> { | ||
if id.is_empty() { | ||
return Err(RollupWithdrawalError::EmptyRollupEventId); | ||
} else if id.len() > 256 { | ||
return Err(RollupWithdrawalError::InvalidRollupEventId); | ||
} | ||
if block == 0 { | ||
return Err(RollupWithdrawalError::InvalidRollupBlockNumber); | ||
} | ||
Ok(()) | ||
} |
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.
Do we even need to check this? Protobuf represents 0 values as empty, and when decoding this it should result in
None
I think