diff --git a/Cargo.lock b/Cargo.lock index c2e4aad8d7..def7009d8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -842,7 +842,6 @@ dependencies = [ "insta", "maplit", "matchit", - "paste", "penumbra-ibc", "penumbra-proto", "penumbra-tower-trace", @@ -964,6 +963,7 @@ dependencies = [ "hex", "indenter", "itertools 0.12.1", + "penumbra-ibc", "predicates", "prost", "rlp", diff --git a/crates/astria-core/src/protocol/fees/v1.rs b/crates/astria-core/src/protocol/fees/v1.rs index f77441a90c..fb5571e9b9 100644 --- a/crates/astria-core/src/protocol/fees/v1.rs +++ b/crates/astria-core/src/protocol/fees/v1.rs @@ -1,8 +1,33 @@ +use std::{ + fmt::{ + self, + Debug, + Formatter, + }, + marker::PhantomData, +}; + +use penumbra_ibc::IbcRelay; use prost::Name as _; use crate::{ generated::astria::protocol::fees::v1 as raw, primitive::v1::asset, + protocol::transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, + }, Protobuf, }; @@ -49,6 +74,7 @@ macro_rules! impl_protobuf_for_fee_components { multiplier: multiplier .ok_or_else(|| Self::Error::missing_field(Self::Raw::full_name(), "multiplier"))? .into(), + _phantom: PhantomData, }) } @@ -56,6 +82,7 @@ macro_rules! impl_protobuf_for_fee_components { let Self { base, multiplier, + _phantom, } = self; Self::Raw { base: Some(base.into()), @@ -67,104 +94,79 @@ macro_rules! impl_protobuf_for_fee_components { }; } impl_protobuf_for_fee_components!( - TransferFeeComponents => raw::TransferFeeComponents, - RollupDataSubmissionFeeComponents => raw::RollupDataSubmissionFeeComponents, - Ics20WithdrawalFeeComponents => raw::Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents => raw::InitBridgeAccountFeeComponents, - BridgeLockFeeComponents => raw::BridgeLockFeeComponents, - BridgeUnlockFeeComponents => raw::BridgeUnlockFeeComponents, - BridgeSudoChangeFeeComponents => raw::BridgeSudoChangeFeeComponents, - ValidatorUpdateFeeComponents => raw::ValidatorUpdateFeeComponents, - IbcRelayerChangeFeeComponents => raw::IbcRelayerChangeFeeComponents, - IbcRelayFeeComponents => raw::IbcRelayFeeComponents, - FeeAssetChangeFeeComponents => raw::FeeAssetChangeFeeComponents, - FeeChangeFeeComponents => raw::FeeChangeFeeComponents, - SudoAddressChangeFeeComponents => raw::SudoAddressChangeFeeComponents, - IbcSudoChangeFeeComponents => raw::IbcSudoChangeFeeComponents, + FeeComponents => raw::TransferFeeComponents, + FeeComponents => raw::RollupDataSubmissionFeeComponents, + FeeComponents => raw::Ics20WithdrawalFeeComponents, + FeeComponents => raw::InitBridgeAccountFeeComponents, + FeeComponents => raw::BridgeLockFeeComponents, + FeeComponents => raw::BridgeUnlockFeeComponents, + FeeComponents => raw::BridgeSudoChangeFeeComponents, + FeeComponents => raw::ValidatorUpdateFeeComponents, + FeeComponents => raw::IbcRelayerChangeFeeComponents, + FeeComponents => raw::IbcRelayFeeComponents, + FeeComponents => raw::FeeAssetChangeFeeComponents, + FeeComponents => raw::FeeChangeFeeComponents, + FeeComponents => raw::SudoAddressChangeFeeComponents, + FeeComponents => raw::IbcSudoChangeFeeComponents, ); -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct TransferFeeComponents { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct RollupDataSubmissionFeeComponents { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Ics20WithdrawalFeeComponents { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct InitBridgeAccountFeeComponents { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct BridgeLockFeeComponents { - pub base: u128, - pub multiplier: u128, +pub struct FeeComponents { + base: u128, + multiplier: u128, + _phantom: PhantomData, } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct BridgeUnlockFeeComponents { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct BridgeSudoChangeFeeComponents { - pub base: u128, - pub multiplier: u128, -} +impl FeeComponents { + #[must_use] + pub fn new(base: u128, multiplier: u128) -> Self { + Self { + base, + multiplier, + _phantom: PhantomData, + } + } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct IbcRelayFeeComponents { - pub base: u128, - pub multiplier: u128, -} + #[must_use] + pub fn base(&self) -> u128 { + self.base + } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct ValidatorUpdateFeeComponents { - pub base: u128, - pub multiplier: u128, + #[must_use] + pub fn multiplier(&self) -> u128 { + self.multiplier + } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct FeeAssetChangeFeeComponents { - pub base: u128, - pub multiplier: u128, +impl Debug for FeeComponents { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct(&format!("FeeComponents<{}>", T::Raw::NAME)) + .field("base", &self.base) + .field("multiplier", &self.multiplier) + .finish() + } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct FeeChangeFeeComponents { - pub base: u128, - pub multiplier: u128, +impl Debug for FeeComponents { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("FeeComponents") + .field("base", &self.base) + .field("multiplier", &self.multiplier) + .finish() + } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct IbcRelayerChangeFeeComponents { - pub base: u128, - pub multiplier: u128, +impl Clone for FeeComponents { + fn clone(&self) -> Self { + *self + } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct SudoAddressChangeFeeComponents { - pub base: u128, - pub multiplier: u128, -} +impl Copy for FeeComponents {} -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct IbcSudoChangeFeeComponents { - pub base: u128, - pub multiplier: u128, +impl PartialEq for FeeComponents { + fn eq(&self, other: &Self) -> bool { + self.base == other.base && self.multiplier == other.multiplier + } } #[derive(Debug, Clone)] diff --git a/crates/astria-core/src/protocol/genesis/v1.rs b/crates/astria-core/src/protocol/genesis/v1.rs index de177b1cc9..2dac7e1aae 100644 --- a/crates/astria-core/src/protocol/genesis/v1.rs +++ b/crates/astria-core/src/protocol/genesis/v1.rs @@ -1,6 +1,7 @@ use std::convert::Infallible; pub use penumbra_ibc::params::IBCParameters; +use penumbra_ibc::IbcRelay; use crate::{ generated::astria::protocol::genesis::v1 as raw, @@ -15,22 +16,26 @@ use crate::{ Bech32, Bech32m, }, - protocol::fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - FeeComponentError, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, + protocol::{ + fees::v1::{ + FeeComponentError, + FeeComponents, + }, + transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, + }, }, Protobuf, }; @@ -580,20 +585,20 @@ impl From for IBCParameters { #[derive(Clone, Debug)] pub struct GenesisFees { - pub rollup_data_submission: Option, - pub transfer: Option, - pub ics20_withdrawal: Option, - pub init_bridge_account: Option, - pub bridge_lock: Option, - pub bridge_unlock: Option, - pub bridge_sudo_change: Option, - pub ibc_relay: Option, - pub validator_update: Option, - pub fee_asset_change: Option, - pub fee_change: FeeChangeFeeComponents, - pub ibc_relayer_change: Option, - pub sudo_address_change: Option, - pub ibc_sudo_change: Option, + pub rollup_data_submission: Option>, + pub transfer: Option>, + pub ics20_withdrawal: Option>, + pub init_bridge_account: Option>, + pub bridge_lock: Option>, + pub bridge_unlock: Option>, + pub bridge_sudo_change: Option>, + pub ibc_relay: Option>, + pub validator_update: Option>, + pub fee_asset_change: Option>, + pub fee_change: FeeComponents, + pub ibc_relayer_change: Option>, + pub sudo_address_change: Option>, + pub ibc_sudo_change: Option>, } impl Protobuf for GenesisFees { @@ -623,65 +628,65 @@ impl Protobuf for GenesisFees { } = raw; let rollup_data_submission = rollup_data_submission .clone() - .map(RollupDataSubmissionFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("rollup_data_submission", e))?; let transfer = transfer .clone() - .map(TransferFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("transfer", e))?; let ics20_withdrawal = ics20_withdrawal .clone() - .map(Ics20WithdrawalFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("ics20_withdrawal", e))?; let init_bridge_account = init_bridge_account .clone() - .map(InitBridgeAccountFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("init_bridge_account", e))?; let bridge_lock = bridge_lock .clone() - .map(BridgeLockFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("bridge_lock", e))?; let bridge_unlock = bridge_unlock .clone() - .map(BridgeUnlockFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("bridge_unlock", e))?; let bridge_sudo_change = bridge_sudo_change .clone() - .map(BridgeSudoChangeFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("bridge_sudo_change", e))?; let ibc_relay = ibc_relay .clone() - .map(IbcRelayFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("ibc_relay", e))?; let validator_update = validator_update .clone() - .map(ValidatorUpdateFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("validator_update", e))?; let fee_asset_change = fee_asset_change .clone() - .map(FeeAssetChangeFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("fee_asset_change", e))?; - let fee_change = FeeChangeFeeComponents::try_from_raw( + let fee_change = FeeComponents::::try_from_raw( fee_change .clone() .ok_or_else(|| Self::Error::field_not_set("fee_change"))?, @@ -690,19 +695,19 @@ impl Protobuf for GenesisFees { let ibc_relayer_change = ibc_relayer_change .clone() - .map(IbcRelayerChangeFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("ibc_relayer_change", e))?; let sudo_address_change = sudo_address_change .clone() - .map(SudoAddressChangeFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("sudo_address_change", e))?; let ibc_sudo_change = ibc_sudo_change .clone() - .map(IbcSudoChangeFeeComponents::try_from_raw) + .map(FeeComponents::::try_from_raw) .transpose() .map_err(|e| FeesError::fee_components("ibc_sudo_change", e))?; @@ -742,27 +747,29 @@ impl Protobuf for GenesisFees { ibc_sudo_change, } = self; Self::Raw { - transfer: transfer.map(|act| TransferFeeComponents::to_raw(&act)), + transfer: transfer.map(|act| FeeComponents::::to_raw(&act)), rollup_data_submission: rollup_data_submission - .map(|act| RollupDataSubmissionFeeComponents::to_raw(&act)), + .map(|act| FeeComponents::::to_raw(&act)), ics20_withdrawal: ics20_withdrawal - .map(|act| Ics20WithdrawalFeeComponents::to_raw(&act)), + .map(|act| FeeComponents::::to_raw(&act)), init_bridge_account: init_bridge_account - .map(|act| InitBridgeAccountFeeComponents::to_raw(&act)), - bridge_lock: bridge_lock.map(|act| BridgeLockFeeComponents::to_raw(&act)), - bridge_unlock: bridge_unlock.map(|act| BridgeUnlockFeeComponents::to_raw(&act)), + .map(|act| FeeComponents::::to_raw(&act)), + bridge_lock: bridge_lock.map(|act| FeeComponents::::to_raw(&act)), + bridge_unlock: bridge_unlock.map(|act| FeeComponents::::to_raw(&act)), bridge_sudo_change: bridge_sudo_change - .map(|act| BridgeSudoChangeFeeComponents::to_raw(&act)), - ibc_relay: ibc_relay.map(|act| IbcRelayFeeComponents::to_raw(&act)), + .map(|act| FeeComponents::::to_raw(&act)), + ibc_relay: ibc_relay.map(|act| FeeComponents::::to_raw(&act)), validator_update: validator_update - .map(|act| ValidatorUpdateFeeComponents::to_raw(&act)), - fee_asset_change: fee_asset_change.map(|act| FeeAssetChangeFeeComponents::to_raw(&act)), + .map(|act| FeeComponents::::to_raw(&act)), + fee_asset_change: fee_asset_change + .map(|act| FeeComponents::::to_raw(&act)), fee_change: Some(fee_change.to_raw()), ibc_relayer_change: ibc_relayer_change - .map(|act| IbcRelayerChangeFeeComponents::to_raw(&act)), + .map(|act| FeeComponents::::to_raw(&act)), sudo_address_change: sudo_address_change - .map(|act| SudoAddressChangeFeeComponents::to_raw(&act)), - ibc_sudo_change: ibc_sudo_change.map(|act| IbcSudoChangeFeeComponents::to_raw(&act)), + .map(|act| FeeComponents::::to_raw(&act)), + ibc_sudo_change: ibc_sudo_change + .map(|act| FeeComponents::::to_raw(&act)), } } } @@ -837,7 +844,6 @@ mod tests { .unwrap() } - #[expect(clippy::too_many_lines, reason = "for testing purposes")] fn proto_genesis_state() -> raw::GenesisAppState { raw::GenesisAppState { accounts: vec![ @@ -870,104 +876,22 @@ mod tests { }), allowed_fee_assets: vec!["nria".into()], fees: Some(raw::GenesisFees { - transfer: Some( - TransferFeeComponents { - base: 12, - multiplier: 0, - } - .to_raw(), - ), + transfer: Some(FeeComponents::::new(12, 0).to_raw()), rollup_data_submission: Some( - RollupDataSubmissionFeeComponents { - base: 32, - multiplier: 1, - } - .to_raw(), - ), - init_bridge_account: Some( - InitBridgeAccountFeeComponents { - base: 48, - multiplier: 0, - } - .to_raw(), - ), - bridge_lock: Some( - BridgeLockFeeComponents { - base: 12, - multiplier: 1, - } - .to_raw(), - ), - bridge_unlock: Some( - BridgeUnlockFeeComponents { - base: 12, - multiplier: 0, - } - .to_raw(), - ), - bridge_sudo_change: Some( - BridgeSudoChangeFeeComponents { - base: 24, - multiplier: 0, - } - .to_raw(), - ), - ics20_withdrawal: Some( - Ics20WithdrawalFeeComponents { - base: 24, - multiplier: 0, - } - .to_raw(), - ), - ibc_relay: Some( - IbcRelayFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - validator_update: Some( - ValidatorUpdateFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - fee_asset_change: Some( - FeeAssetChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - fee_change: Some( - FeeChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - ibc_relayer_change: Some( - IbcRelayerChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - sudo_address_change: Some( - SudoAddressChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - ibc_sudo_change: Some( - IbcSudoChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), + FeeComponents::::new(32, 1).to_raw(), ), + init_bridge_account: Some(FeeComponents::::new(48, 0).to_raw()), + bridge_lock: Some(FeeComponents::::new(12, 1).to_raw()), + bridge_unlock: Some(FeeComponents::::new(12, 0).to_raw()), + bridge_sudo_change: Some(FeeComponents::::new(24, 0).to_raw()), + ics20_withdrawal: Some(FeeComponents::::new(24, 0).to_raw()), + ibc_relay: Some(FeeComponents::::new(0, 0).to_raw()), + validator_update: Some(FeeComponents::::new(0, 0).to_raw()), + fee_asset_change: Some(FeeComponents::::new(0, 0).to_raw()), + fee_change: Some(FeeComponents::::new(0, 0).to_raw()), + ibc_relayer_change: Some(FeeComponents::::new(0, 0).to_raw()), + sudo_address_change: Some(FeeComponents::::new(0, 0).to_raw()), + ibc_sudo_change: Some(FeeComponents::::new(0, 0).to_raw()), }), } } diff --git a/crates/astria-core/src/protocol/transaction/v1/action/group/tests.rs b/crates/astria-core/src/protocol/transaction/v1/action/group/tests.rs index 6eb546ab91..b06d16723e 100644 --- a/crates/astria-core/src/protocol/transaction/v1/action/group/tests.rs +++ b/crates/astria-core/src/protocol/transaction/v1/action/group/tests.rs @@ -19,6 +19,7 @@ use crate::{ BridgeUnlock, FeeAssetChange, FeeChange, + FeeComponents, IbcRelayerChange, IbcSudoChange, Ics20Withdrawal, @@ -26,7 +27,6 @@ use crate::{ RollupDataSubmission, SudoAddressChange, Transfer, - TransferFeeComponents, ValidatorUpdate, }, }; @@ -104,10 +104,7 @@ fn from_list_of_actions_bundleable_sudo() { let asset: Denom = "nria".parse().unwrap(); let actions = vec![ - Action::FeeChange(FeeChange::Transfer(TransferFeeComponents { - base: 100, - multiplier: 0, - })), + Action::FeeChange(FeeChange::Transfer(FeeComponents::::new(100, 0))), Action::FeeAssetChange(FeeAssetChange::Addition(asset)), Action::IbcRelayerChange(IbcRelayerChange::Addition(address)), ]; diff --git a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs index 80ba7d2326..5c2b93745b 100644 --- a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs @@ -22,21 +22,8 @@ use crate::{ RollupId, }, protocol::fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, FeeComponentError, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, + FeeComponents, }, Protobuf, }; @@ -1942,20 +1929,20 @@ enum FeeChangeErrorKind { #[derive(Debug, Clone)] pub enum FeeChange { - Transfer(TransferFeeComponents), - RollupDataSubmission(RollupDataSubmissionFeeComponents), - Ics20Withdrawal(Ics20WithdrawalFeeComponents), - InitBridgeAccount(InitBridgeAccountFeeComponents), - BridgeLock(BridgeLockFeeComponents), - BridgeUnlock(BridgeUnlockFeeComponents), - BridgeSudoChange(BridgeSudoChangeFeeComponents), - IbcRelay(IbcRelayFeeComponents), - ValidatorUpdate(ValidatorUpdateFeeComponents), - FeeAssetChange(FeeAssetChangeFeeComponents), - FeeChange(FeeChangeFeeComponents), - IbcRelayerChange(IbcRelayerChangeFeeComponents), - SudoAddressChange(SudoAddressChangeFeeComponents), - IbcSudoChange(IbcSudoChangeFeeComponents), + Transfer(FeeComponents), + RollupDataSubmission(FeeComponents), + Ics20Withdrawal(FeeComponents), + InitBridgeAccount(FeeComponents), + BridgeLock(FeeComponents), + BridgeUnlock(FeeComponents), + BridgeSudoChange(FeeComponents), + IbcRelay(FeeComponents), + ValidatorUpdate(FeeComponents), + FeeAssetChange(FeeComponents), + FeeChange(FeeComponents), + IbcRelayerChange(FeeComponents), + SudoAddressChange(FeeComponents), + IbcSudoChange(FeeComponents), } impl Protobuf for FeeChange { @@ -2021,54 +2008,148 @@ impl Protobuf for FeeChange { fn try_from_raw_ref(proto: &raw::FeeChange) -> Result { Ok(match &proto.fee_components { Some(raw::fee_change::FeeComponents::Transfer(fee_change)) => { - Self::Transfer(TransferFeeComponents::try_from_raw_ref(fee_change)?) + Self::Transfer(FeeComponents::::try_from_raw_ref(fee_change)?) } Some(raw::fee_change::FeeComponents::RollupDataSubmission(fee_change)) => { - Self::RollupDataSubmission(RollupDataSubmissionFeeComponents::try_from_raw_ref( + Self::RollupDataSubmission(FeeComponents::::try_from_raw_ref( fee_change, )?) } Some(raw::fee_change::FeeComponents::Ics20Withdrawal(fee_change)) => { - Self::Ics20Withdrawal(Ics20WithdrawalFeeComponents::try_from_raw_ref(fee_change)?) + Self::Ics20Withdrawal(FeeComponents::::try_from_raw_ref( + fee_change, + )?) } Some(raw::fee_change::FeeComponents::InitBridgeAccount(fee_change)) => { - Self::InitBridgeAccount(InitBridgeAccountFeeComponents::try_from_raw_ref( + Self::InitBridgeAccount(FeeComponents::::try_from_raw_ref( fee_change, )?) } Some(raw::fee_change::FeeComponents::BridgeLock(fee_change)) => { - Self::BridgeLock(BridgeLockFeeComponents::try_from_raw_ref(fee_change)?) + Self::BridgeLock(FeeComponents::::try_from_raw_ref(fee_change)?) } Some(raw::fee_change::FeeComponents::BridgeUnlock(fee_change)) => { - Self::BridgeUnlock(BridgeUnlockFeeComponents::try_from_raw_ref(fee_change)?) + Self::BridgeUnlock(FeeComponents::::try_from_raw_ref(fee_change)?) } Some(raw::fee_change::FeeComponents::BridgeSudoChange(fee_change)) => { - Self::BridgeSudoChange(BridgeSudoChangeFeeComponents::try_from_raw_ref(fee_change)?) + Self::BridgeSudoChange(FeeComponents::::try_from_raw_ref( + fee_change, + )?) } Some(raw::fee_change::FeeComponents::IbcRelay(fee_change)) => { - Self::IbcRelay(IbcRelayFeeComponents::try_from_raw_ref(fee_change)?) + Self::IbcRelay(FeeComponents::::try_from_raw_ref(fee_change)?) } Some(raw::fee_change::FeeComponents::ValidatorUpdate(fee_change)) => { - Self::ValidatorUpdate(ValidatorUpdateFeeComponents::try_from_raw_ref(fee_change)?) + Self::ValidatorUpdate(FeeComponents::::try_from_raw_ref( + fee_change, + )?) } Some(raw::fee_change::FeeComponents::FeeAssetChange(fee_change)) => { - Self::FeeAssetChange(FeeAssetChangeFeeComponents::try_from_raw_ref(fee_change)?) + Self::FeeAssetChange(FeeComponents::::try_from_raw_ref( + fee_change, + )?) } Some(raw::fee_change::FeeComponents::FeeChange(fee_change)) => { - Self::FeeChange(FeeChangeFeeComponents::try_from_raw_ref(fee_change)?) + Self::FeeChange(FeeComponents::::try_from_raw_ref(fee_change)?) } Some(raw::fee_change::FeeComponents::IbcRelayerChange(fee_change)) => { - Self::IbcRelayerChange(IbcRelayerChangeFeeComponents::try_from_raw_ref(fee_change)?) + Self::IbcRelayerChange(FeeComponents::::try_from_raw_ref( + fee_change, + )?) } Some(raw::fee_change::FeeComponents::SudoAddressChange(fee_change)) => { - Self::SudoAddressChange(SudoAddressChangeFeeComponents::try_from_raw_ref( + Self::SudoAddressChange(FeeComponents::::try_from_raw_ref( fee_change, )?) } - Some(raw::fee_change::FeeComponents::IbcSudoChange(fee_change)) => { - Self::IbcSudoChange(IbcSudoChangeFeeComponents::try_from_raw_ref(fee_change)?) - } + Some(raw::fee_change::FeeComponents::IbcSudoChange(fee_change)) => Self::IbcSudoChange( + FeeComponents::::try_from_raw_ref(fee_change)?, + ), None => return Err(FeeChangeError::field_unset("fee_components")), }) } } + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::Transfer(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::RollupDataSubmission(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::Ics20Withdrawal(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::InitBridgeAccount(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::BridgeLock(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::BridgeUnlock(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::BridgeSudoChange(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::IbcRelay(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::ValidatorUpdate(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::FeeAssetChange(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::FeeChange(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::IbcRelayerChange(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::SudoAddressChange(fee) + } +} + +impl From> for FeeChange { + fn from(fee: FeeComponents) -> Self { + FeeChange::IbcSudoChange(fee) + } +} diff --git a/crates/astria-sequencer-utils/Cargo.toml b/crates/astria-sequencer-utils/Cargo.toml index 9f0bed5d91..2c2974577f 100644 --- a/crates/astria-sequencer-utils/Cargo.toml +++ b/crates/astria-sequencer-utils/Cargo.toml @@ -21,6 +21,7 @@ ethers-core = "2.0.14" hex = { workspace = true } indenter = "0.3.3" itertools = { workspace = true } +penumbra-ibc = { workspace = true } prost = { workspace = true } rlp = "0.5.2" serde = { workspace = true } diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 6d7d277719..12a09ca709 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -12,26 +12,26 @@ use astria_core::{ }, primitive::v1::Address, protocol::{ - fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, - }, + fees::v1::FeeComponents, genesis::v1::{ Account, GenesisAppState, }, + transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, + }, }, Protobuf, }; @@ -39,6 +39,7 @@ use astria_eyre::eyre::{ Result, WrapErr as _, }; +use penumbra_ibc::IbcRelay; const ASTRIA_ADDRESS_PREFIX: &str = "astria"; @@ -90,7 +91,6 @@ fn address_prefixes() -> AddressPrefixes { } } -#[expect(clippy::too_many_lines, reason = "all lines reasonably necessary")] fn proto_genesis_state() -> astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { accounts: accounts().into_iter().map(Protobuf::into_raw).collect(), @@ -107,104 +107,22 @@ fn proto_genesis_state() -> astria_core::generated::astria::protocol::genesis::v }), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: Some(GenesisFees { - transfer: Some( - TransferFeeComponents { - base: 12, - multiplier: 0, - } - .to_raw(), - ), + transfer: Some(FeeComponents::::new(12, 0).to_raw()), rollup_data_submission: Some( - RollupDataSubmissionFeeComponents { - base: 32, - multiplier: 1, - } - .to_raw(), - ), - init_bridge_account: Some( - InitBridgeAccountFeeComponents { - base: 48, - multiplier: 0, - } - .to_raw(), - ), - bridge_lock: Some( - BridgeLockFeeComponents { - base: 12, - multiplier: 1, - } - .to_raw(), - ), - bridge_unlock: Some( - BridgeUnlockFeeComponents { - base: 12, - multiplier: 0, - } - .to_raw(), - ), - bridge_sudo_change: Some( - BridgeSudoChangeFeeComponents { - base: 24, - multiplier: 0, - } - .to_raw(), - ), - ics20_withdrawal: Some( - Ics20WithdrawalFeeComponents { - base: 24, - multiplier: 0, - } - .to_raw(), - ), - ibc_relay: Some( - IbcRelayFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - validator_update: Some( - ValidatorUpdateFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - fee_asset_change: Some( - FeeAssetChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - fee_change: Some( - FeeChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - ibc_relayer_change: Some( - IbcRelayerChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - sudo_address_change: Some( - SudoAddressChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), - ), - ibc_sudo_change: Some( - IbcSudoChangeFeeComponents { - base: 0, - multiplier: 0, - } - .to_raw(), + FeeComponents::::new(32, 1).to_raw(), ), + init_bridge_account: Some(FeeComponents::::new(48, 0).to_raw()), + bridge_lock: Some(FeeComponents::::new(12, 1).to_raw()), + bridge_unlock: Some(FeeComponents::::new(12, 0).to_raw()), + bridge_sudo_change: Some(FeeComponents::::new(24, 0).to_raw()), + ics20_withdrawal: Some(FeeComponents::::new(24, 0).to_raw()), + ibc_relay: Some(FeeComponents::::new(0, 0).to_raw()), + validator_update: Some(FeeComponents::::new(0, 0).to_raw()), + fee_asset_change: Some(FeeComponents::::new(0, 0).to_raw()), + fee_change: Some(FeeComponents::::new(0, 0).to_raw()), + ibc_relayer_change: Some(FeeComponents::::new(0, 0).to_raw()), + sudo_address_change: Some(FeeComponents::::new(0, 0).to_raw()), + ibc_sudo_change: Some(FeeComponents::::new(0, 0).to_raw()), }), } } diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index 999bd4c547..a2b8ee67b7 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -77,7 +77,6 @@ config = { package = "astria-config", path = "../astria-config", features = [ "tests", ] } insta = { workspace = true, features = ["json"] } -paste = "1.0.15" maplit = "1.0.2" rand_chacha = "0.3.1" tokio = { workspace = true, features = ["test-util"] } diff --git a/crates/astria-sequencer/src/action_handler/impls/bridge_sudo_change.rs b/crates/astria-sequencer/src/action_handler/impls/bridge_sudo_change.rs index 6bdb3ee4f4..a69775cf5f 100644 --- a/crates/astria-sequencer/src/action_handler/impls/bridge_sudo_change.rs +++ b/crates/astria-sequencer/src/action_handler/impls/bridge_sudo_change.rs @@ -83,7 +83,7 @@ mod tests { use astria_core::{ primitive::v1::TransactionId, protocol::{ - fees::v1::BridgeSudoChangeFeeComponents, + fees::v1::FeeComponents, transaction::v1::action::BridgeSudoChange, }, }; @@ -164,10 +164,7 @@ mod tests { }); state.put_base_prefix(ASTRIA_PREFIX.to_string()).unwrap(); state - .put_bridge_sudo_change_fees(BridgeSudoChangeFeeComponents { - base: 10, - multiplier: 0, - }) + .put_fees(FeeComponents::::new(10, 0)) .unwrap(); let fee_asset = test_asset(); diff --git a/crates/astria-sequencer/src/action_handler/impls/bridge_unlock.rs b/crates/astria-sequencer/src/action_handler/impls/bridge_unlock.rs index 10da98c5a4..d2362a5742 100644 --- a/crates/astria-sequencer/src/action_handler/impls/bridge_unlock.rs +++ b/crates/astria-sequencer/src/action_handler/impls/bridge_unlock.rs @@ -109,7 +109,7 @@ mod tests { TransactionId, }, protocol::{ - fees::v1::BridgeUnlockFeeComponents, + fees::v1::FeeComponents, transaction::v1::action::BridgeUnlock, }, }; @@ -235,10 +235,7 @@ mod tests { let transfer_fee = 10; let transfer_amount = 100; state - .put_bridge_unlock_fees(BridgeUnlockFeeComponents { - base: transfer_fee, - multiplier: 0, - }) + .put_fees(FeeComponents::::new(transfer_fee, 0)) .unwrap(); let to_address = astria_address(&[2; 20]); diff --git a/crates/astria-sequencer/src/action_handler/impls/fee_change.rs b/crates/astria-sequencer/src/action_handler/impls/fee_change.rs index d83cf2ed61..f066ded9ea 100644 --- a/crates/astria-sequencer/src/action_handler/impls/fee_change.rs +++ b/crates/astria-sequencer/src/action_handler/impls/fee_change.rs @@ -36,46 +36,46 @@ impl ActionHandler for FeeChange { match self { Self::Transfer(fees) => state - .put_transfer_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put transfer fees"), Self::RollupDataSubmission(fees) => state - .put_rollup_data_submission_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put sequence fees"), Self::Ics20Withdrawal(fees) => state - .put_ics20_withdrawal_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put ics20 withdrawal fees"), Self::InitBridgeAccount(fees) => state - .put_init_bridge_account_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put init bridge account fees"), Self::BridgeLock(fees) => state - .put_bridge_lock_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put bridge lock fees"), Self::BridgeUnlock(fees) => state - .put_bridge_unlock_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put bridge unlock fees"), Self::BridgeSudoChange(fees) => state - .put_bridge_sudo_change_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put bridge sudo change fees"), Self::IbcRelay(fees) => state - .put_ibc_relay_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put ibc relay fees"), Self::ValidatorUpdate(fees) => state - .put_validator_update_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put validator update fees"), Self::FeeAssetChange(fees) => state - .put_fee_asset_change_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put fee asset change fees"), Self::FeeChange(fees) => state - .put_fee_change_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put fee change fees"), Self::IbcRelayerChange(fees) => state - .put_ibc_relayer_change_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put ibc relayer change fees"), Self::SudoAddressChange(fees) => state - .put_sudo_address_change_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put sudo address change fees"), Self::IbcSudoChange(fees) => state - .put_ibc_sudo_change_fees(*fees) + .put_fees(*fees) .wrap_err("failed to put ibc sudo change fees"), } } @@ -83,100 +83,154 @@ impl ActionHandler for FeeChange { #[cfg(test)] mod tests { + use std::fmt::Debug; + use astria_core::{ primitive::v1::TransactionId, - protocol::transaction::v1::action::FeeChange, + protocol::{ + fees::v1::*, + transaction::v1::action::*, + }, }; + use astria_eyre::eyre::Report; + use penumbra_ibc::IbcRelay; use crate::{ action_handler::ActionHandler as _, authority::StateWriteExt as _, - fees::StateReadExt as _, + fees::{ + FeeHandler, + StateReadExt as _, + }, + storage::StoredValue, transaction::{ StateWriteExt as _, TransactionContext, }, }; - /// This macro generates a test named e.g. `transfer_fee_change_action_executes` which asserts - /// that executing a `FeeChange` tx for the given action results in the fees being stored for - /// the given action. - macro_rules! test_fee_change_action { - ( $( $fee_name:tt => $fee_ty:tt ),* $(,)?) => { - $( - paste::item! { - #[tokio::test] - async fn [< $fee_name _fee_change_action_executes >] () { - use astria_core::protocol::fees::v1:: [< $fee_ty FeeComponents >] as Fees; - - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = cnidarium::StateDelta::new(snapshot); - - // Put the context to enable the txs to execute. - state.put_transaction_context(TransactionContext { - address_bytes: [1; 20], - transaction_id: TransactionId::new([0; 32]), - position_in_transaction: 0, - }); - state.put_sudo_address([1; 20]).unwrap(); - - assert!(state - .[< get_ $fee_name _fees >] () - .await - .expect(stringify!(should not error fetching unstored $fee_name fees)) - .is_none()); - - // Execute an initial fee change tx to store the first version of the fees. - let initial_fees = Fees { - base: 1, - multiplier: 2, - }; - let fee_change = FeeChange:: $fee_ty (initial_fees); - fee_change.check_and_execute(&mut state).await.unwrap(); - - let retrieved_fees = state - .[< get_ $fee_name _fees >] () - .await - .expect(stringify!(should not error fetching initial $fee_name fees)) - .expect(stringify!(initial $fee_name fees should be stored)); - assert_eq!(initial_fees, retrieved_fees); - - // Execute a second fee change tx to overwrite the fees. - let new_fees = Fees { - base: 3, - multiplier: 4, - }; - let fee_change = FeeChange:: $fee_ty (new_fees); - fee_change.check_and_execute(&mut state).await.unwrap(); - - let retrieved_fees = state - .[< get_ $fee_name _fees >] () - .await - .expect(stringify!(should not error fetching new $fee_name fees)) - .expect(stringify!(new $fee_name fees should be stored)); - assert_ne!(initial_fees, retrieved_fees); - assert_eq!(new_fees, retrieved_fees); - } - } - )* - }; - } - - test_fee_change_action!( - transfer => Transfer, - rollup_data_submission => RollupDataSubmission, - ics20_withdrawal => Ics20Withdrawal, - init_bridge_account => InitBridgeAccount, - bridge_lock => BridgeLock, - bridge_unlock => BridgeUnlock, - bridge_sudo_change => BridgeSudoChange, - validator_update => ValidatorUpdate, - ibc_relayer_change => IbcRelayerChange, - ibc_relay => IbcRelay, - fee_asset_change => FeeAssetChange, - fee_change => FeeChange, - sudo_address_change => SudoAddressChange, - ibc_sudo_change => IbcSudoChange, - ); + #[tokio::test] + async fn transfer_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn rollup_data_submission_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn ics20_withdrawal_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn init_bridge_account_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn bridge_lock_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn bridge_unlock_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn bridge_sudo_change_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn validator_update_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn ibc_relay_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn ibc_relayer_change_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn fee_asset_change_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn fee_change_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn sudo_address_change_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + #[tokio::test] + async fn ibc_sudo_change_fee_change_action_executes_as_expected() { + test_fee_change_action::().await; + } + + async fn test_fee_change_action<'a, F>() + where + F: FeeHandler, + FeeComponents: TryFrom, Error = Report> + Debug, + FeeChange: From>, + { + let storage = cnidarium::TempStorage::new().await.unwrap(); + let snapshot = storage.latest_snapshot(); + let mut state = cnidarium::StateDelta::new(snapshot); + + // Put the context to enable the txs to execute. + state.put_transaction_context(TransactionContext { + address_bytes: [1; 20], + transaction_id: TransactionId::new([0; 32]), + position_in_transaction: 0, + }); + state.put_sudo_address([1; 20]).unwrap(); + + assert!( + state + .get_fees::() + .await + .expect("should not error fetching unstored action fees") + .is_none() + ); + + // Execute an initial fee change tx to store the first version of the fees. + let initial_fees = FeeComponents::::new(1, 2); + let initial_fee_change = FeeChange::from(initial_fees); + initial_fee_change + .check_and_execute(&mut state) + .await + .unwrap(); + + let retrieved_fees = state + .get_fees::() + .await + .expect("should not error fetching initial action fees") + .expect("initial action fees should be stored"); + assert_eq!(initial_fees, retrieved_fees); + + // Execute a second fee change tx to overwrite the fees. + let new_fees = FeeComponents::::new(3, 4); + let new_fee_change = FeeChange::from(new_fees); + new_fee_change.check_and_execute(&mut state).await.unwrap(); + + let retrieved_fees = state + .get_fees::() + .await + .expect("should not error fetching new action fees") + .expect("new action fees should be stored"); + assert_ne!(initial_fees, retrieved_fees); + assert_eq!(new_fees, retrieved_fees); + } } diff --git a/crates/astria-sequencer/src/action_handler/impls/transaction.rs b/crates/astria-sequencer/src/action_handler/impls/transaction.rs index 29df7cc10a..a5b66bf4fd 100644 --- a/crates/astria-sequencer/src/action_handler/impls/transaction.rs +++ b/crates/astria-sequencer/src/action_handler/impls/transaction.rs @@ -1,8 +1,11 @@ use std::fmt; -use astria_core::protocol::transaction::v1::{ - Action, - Transaction, +use astria_core::protocol::{ + fees::v1::FeeComponents, + transaction::v1::{ + Action, + Transaction, + }, }; use astria_eyre::{ anyhow_to_eyre, @@ -10,6 +13,7 @@ use astria_eyre::{ ensure, Context as _, OptionExt as _, + Report, Result, }, }; @@ -31,6 +35,7 @@ use crate::{ host_interface::AstriaHost, StateReadExt as _, }, + storage::StoredValue, transaction::{ check_balance_for_total_fees_and_transfers, StateWriteExt as _, @@ -271,10 +276,12 @@ impl ActionHandler for Transaction { } } -async fn check_execute_and_pay_fees( - action: &T, - mut state: S, -) -> Result<()> { +async fn check_execute_and_pay_fees<'a, T, S>(action: &T, mut state: S) -> Result<()> +where + T: ActionHandler + FeeHandler + Sync, + FeeComponents: TryFrom, Error = Report>, + S: StateWrite, +{ action.check_and_execute(&mut state).await?; action.check_and_pay_fees(&mut state).await?; Ok(()) diff --git a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs index e2852fd225..7171bce58d 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -6,28 +6,27 @@ use astria_core::{ IbcPrefixed, }, protocol::{ - fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, - }, + fees::v1::FeeComponents, genesis::v1::{ Account, AddressPrefixes, GenesisAppState, }, - transaction::v1::action::ValidatorUpdate, + transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, + }, }, Protobuf, }; @@ -37,6 +36,7 @@ use cnidarium::{ StateDelta, Storage, }; +use penumbra_ibc::IbcRelay; use telemetry::Metrics as _; use crate::{ @@ -70,62 +70,22 @@ pub(crate) fn address_prefixes() -> AddressPrefixes { pub(crate) fn default_fees() -> astria_core::protocol::genesis::v1::GenesisFees { astria_core::protocol::genesis::v1::GenesisFees { - transfer: Some(TransferFeeComponents { - base: 12, - multiplier: 0, - }), - rollup_data_submission: Some(RollupDataSubmissionFeeComponents { - base: 32, - multiplier: 1, - }), - init_bridge_account: Some(InitBridgeAccountFeeComponents { - base: 48, - multiplier: 0, - }), - bridge_lock: Some(BridgeLockFeeComponents { - base: 12, // should reflect transfer fee - multiplier: 1, - }), - bridge_sudo_change: Some(BridgeSudoChangeFeeComponents { - base: 24, - multiplier: 0, - }), - ics20_withdrawal: Some(Ics20WithdrawalFeeComponents { - base: 24, - multiplier: 0, - }), - bridge_unlock: Some(BridgeUnlockFeeComponents { - base: 12, // should reflect transfer fee - multiplier: 0, - }), - ibc_relay: Some(IbcRelayFeeComponents { - base: 0, - multiplier: 0, - }), - validator_update: Some(ValidatorUpdateFeeComponents { - base: 0, - multiplier: 0, - }), - fee_asset_change: Some(FeeAssetChangeFeeComponents { - base: 0, - multiplier: 0, - }), - fee_change: FeeChangeFeeComponents { - base: 0, - multiplier: 0, - }, - ibc_relayer_change: Some(IbcRelayerChangeFeeComponents { - base: 0, - multiplier: 0, - }), - sudo_address_change: Some(SudoAddressChangeFeeComponents { - base: 0, - multiplier: 0, - }), - ibc_sudo_change: Some(IbcSudoChangeFeeComponents { - base: 0, - multiplier: 0, - }), + transfer: Some(FeeComponents::::new(12, 0)), + rollup_data_submission: Some(FeeComponents::::new(32, 1)), + init_bridge_account: Some(FeeComponents::::new(48, 0)), + // should reflect transfer fee + bridge_lock: Some(FeeComponents::::new(12, 1)), + bridge_sudo_change: Some(FeeComponents::::new(24, 0)), + ics20_withdrawal: Some(FeeComponents::::new(24, 0)), + // should reflect transfer fee + bridge_unlock: Some(FeeComponents::::new(12, 0)), + ibc_relay: Some(FeeComponents::::new(0, 0)), + validator_update: Some(FeeComponents::::new(0, 0)), + fee_asset_change: Some(FeeComponents::::new(0, 0)), + fee_change: FeeComponents::::new(0, 0), + ibc_relayer_change: Some(FeeComponents::::new(0, 0)), + sudo_address_change: Some(FeeComponents::::new(0, 0)), + ibc_sudo_change: Some(FeeComponents::::new(0, 0)), } } @@ -287,10 +247,6 @@ pub(crate) fn mock_state_put_account_nonce( state.put_account_nonce(address, nonce).unwrap(); } -#[expect( - clippy::too_many_lines, - reason = "lines come from necessary fees setup" -)] pub(crate) async fn mock_state_getter() -> StateDelta { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); @@ -320,130 +276,88 @@ pub(crate) async fn mock_state_getter() -> StateDelta { .unwrap(); // setup tx fees - // setup tx fees - let transfer_fees = TransferFeeComponents { - base: 0, - multiplier: 0, - }; + let transfer_fees = FeeComponents::::new(0, 0); state - .put_transfer_fees(transfer_fees) + .put_fees(transfer_fees) .wrap_err("failed to initiate transfer fee components") .unwrap(); - let rollup_data_submission_fees = RollupDataSubmissionFeeComponents { - base: MOCK_SEQUENCE_FEE, - multiplier: 0, - }; + let rollup_data_submission_fees = + FeeComponents::::new(MOCK_SEQUENCE_FEE, 0); state - .put_rollup_data_submission_fees(rollup_data_submission_fees) - .wrap_err("failed to initiate sequence action fee components") + .put_fees(rollup_data_submission_fees) + .wrap_err("failed to initiate rollup data submission fee components") .unwrap(); - let ics20_withdrawal_fees = Ics20WithdrawalFeeComponents { - base: 0, - multiplier: 0, - }; + let ics20_withdrawal_fees = FeeComponents::::new(0, 0); state - .put_ics20_withdrawal_fees(ics20_withdrawal_fees) + .put_fees(ics20_withdrawal_fees) .wrap_err("failed to initiate ics20 withdrawal fee components") .unwrap(); - let init_bridge_account_fees = InitBridgeAccountFeeComponents { - base: 0, - multiplier: 0, - }; + let init_bridge_account_fees = FeeComponents::::new(0, 0); state - .put_init_bridge_account_fees(init_bridge_account_fees) + .put_fees(init_bridge_account_fees) .wrap_err("failed to initiate init bridge account fee components") .unwrap(); - let bridge_lock_fees = BridgeLockFeeComponents { - base: 0, - multiplier: 0, - }; + let bridge_lock_fees = FeeComponents::::new(0, 0); state - .put_bridge_lock_fees(bridge_lock_fees) + .put_fees(bridge_lock_fees) .wrap_err("failed to initiate bridge lock fee components") .unwrap(); - let bridge_unlock_fees = BridgeUnlockFeeComponents { - base: 0, - multiplier: 0, - }; + let bridge_unlock_fees = FeeComponents::::new(0, 0); state - .put_bridge_unlock_fees(bridge_unlock_fees) + .put_fees(bridge_unlock_fees) .wrap_err("failed to initiate bridge unlock fee components") .unwrap(); - let bridge_sudo_change_fees = BridgeSudoChangeFeeComponents { - base: 0, - multiplier: 0, - }; + let bridge_sudo_change_fees = FeeComponents::::new(0, 0); state - .put_bridge_sudo_change_fees(bridge_sudo_change_fees) + .put_fees(bridge_sudo_change_fees) .wrap_err("failed to initiate bridge sudo change fee components") .unwrap(); - let ibc_relay_fees = IbcRelayFeeComponents { - base: 0, - multiplier: 0, - }; + let ibc_relay_fees = FeeComponents::::new(0, 0); state - .put_ibc_relay_fees(ibc_relay_fees) + .put_fees(ibc_relay_fees) .wrap_err("failed to initiate ibc relay fee components") .unwrap(); - let validator_update_fees = ValidatorUpdateFeeComponents { - base: 0, - multiplier: 0, - }; + let validator_update_fees = FeeComponents::::new(0, 0); state - .put_validator_update_fees(validator_update_fees) + .put_fees(validator_update_fees) .wrap_err("failed to initiate validator update fee components") .unwrap(); - let fee_asset_change_fees = FeeAssetChangeFeeComponents { - base: 0, - multiplier: 0, - }; + let fee_asset_change_fees = FeeComponents::::new(0, 0); state - .put_fee_asset_change_fees(fee_asset_change_fees) + .put_fees(fee_asset_change_fees) .wrap_err("failed to initiate fee asset change fee components") .unwrap(); - let fee_change_fees = FeeChangeFeeComponents { - base: 0, - multiplier: 0, - }; + let fee_change_fees = FeeComponents::::new(0, 0); state - .put_fee_change_fees(fee_change_fees) + .put_fees(fee_change_fees) .wrap_err("failed to initiate fee change fees fee components") .unwrap(); - let ibc_relayer_change_fees = IbcRelayerChangeFeeComponents { - base: 0, - multiplier: 0, - }; + let ibc_relayer_change_fees = FeeComponents::::new(0, 0); state - .put_ibc_relayer_change_fees(ibc_relayer_change_fees) + .put_fees(ibc_relayer_change_fees) .wrap_err("failed to initiate ibc relayer change fee components") .unwrap(); - let sudo_address_change_fees = SudoAddressChangeFeeComponents { - base: 0, - multiplier: 0, - }; + let sudo_address_change_fees = FeeComponents::::new(0, 0); state - .put_sudo_address_change_fees(sudo_address_change_fees) + .put_fees(sudo_address_change_fees) .wrap_err("failed to initiate sudo address change fee components") .unwrap(); - let ibc_sudo_change_fees = IbcSudoChangeFeeComponents { - base: 0, - multiplier: 0, - }; + let ibc_sudo_change_fees = FeeComponents::::new(0, 0); state - .put_ibc_sudo_change_fees(ibc_sudo_change_fees) + .put_fees(ibc_sudo_change_fees) .wrap_err("failed to initiate ibc sudo change fee components") .unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index adc304d585..4a30b4460b 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use astria_core::{ protocol::{ - fees::v1::TransferFeeComponents, + fees::v1::FeeComponents, genesis::v1::Account, transaction::v1::{ action::{ @@ -55,11 +55,7 @@ async fn trigger_cleaning() { // create tx which will cause mempool cleaning flag to be set let tx_trigger = TransactionBody::builder() .actions(vec![ - FeeChange::Transfer(TransferFeeComponents { - base: 10, - multiplier: 0, - }) - .into(), + FeeChange::Transfer(FeeComponents::::new(10, 0)).into(), ]) .chain_id("test") .try_build() @@ -151,11 +147,7 @@ async fn do_not_trigger_cleaning() { // (wrong sudo signer) let tx_fail = TransactionBody::builder() .actions(vec![ - FeeChange::Transfer(TransferFeeComponents { - base: 10, - multiplier: 0, - }) - .into(), + FeeChange::Transfer(FeeComponents::::new(10, 0)).into(), ]) .chain_id("test") .try_build() @@ -252,11 +244,7 @@ async fn maintenance_recosting_promotes() { // create tx which will enable recost tx to pass let tx_recost = TransactionBody::builder() .actions(vec![ - FeeChange::Transfer(TransferFeeComponents { - base: 10, - multiplier: 0, - }) - .into(), + FeeChange::Transfer(FeeComponents::::new(10, 0)).into(), ]) .chain_id("test") .try_build() diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index b91e107105..c0cd55b1ca 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -284,11 +284,11 @@ async fn app_transfer_block_fees_to_sudo() { // assert that transaction fees were transferred to the block proposer let transfer_base_fee = app .state - .get_transfer_fees() + .get_fees::() .await .expect("should not error fetching transfer fees") .expect("transfer fees should be stored") - .base; + .base(); assert_eq!( app.state .get_account_balance(&astria_address_from_hex_string(JUDY_ADDRESS), &nria()) diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 011d789270..7f5d778e36 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -8,10 +8,7 @@ use astria_core::{ RollupId, }, protocol::{ - fees::v1::{ - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - }, + fees::v1::FeeComponents, genesis::v1::GenesisAppState, transaction::v1::{ action::{ @@ -141,11 +138,11 @@ async fn app_execute_transaction_transfer() { ); let transfer_base = app .state - .get_transfer_fees() + .get_fees::() .await .expect("should not error fetching transfer fees") .expect("transfer fees should be stored") - .base; + .base(); assert_eq!( app.state .get_account_balance(&alice_address, &nria()) @@ -213,11 +210,11 @@ async fn app_execute_transaction_transfer_not_native_token() { let transfer_base = app .state - .get_transfer_fees() + .get_fees::() .await .expect("should not error fetching transfer fees") .expect("transfer fees should be stored") - .base; + .base(); assert_eq!( app.state .get_account_balance(&alice_address, &nria()) @@ -280,10 +277,7 @@ async fn app_execute_transaction_sequence() { let mut app = initialize_app(None, vec![]).await; let mut state_tx = StateDelta::new(app.state.clone()); state_tx - .put_rollup_data_submission_fees(RollupDataSubmissionFeeComponents { - base: 0, - multiplier: 1, - }) + .put_fees(FeeComponents::::new(0, 1)) .unwrap(); app.apply(state_tx); @@ -618,10 +612,7 @@ async fn app_execute_transaction_init_bridge_account_ok() { let mut state_tx = StateDelta::new(app.state.clone()); let fee = 12; // arbitrary state_tx - .put_init_bridge_account_fees(InitBridgeAccountFeeComponents { - base: fee, - multiplier: 0, - }) + .put_fees(FeeComponents::::new(fee, 0)) .unwrap(); app.apply(state_tx); @@ -1014,11 +1005,11 @@ async fn app_execute_transaction_bridge_lock_unlock_action_ok() { // unlock transfer action let transfer_base = app .state - .get_transfer_fees() + .get_fees::() .await .expect("should not error fetching transfer fees") .expect("transfer fees should be stored") - .base; + .base(); state_tx .put_account_balance(&bridge_address, &nria(), transfer_base) .unwrap(); diff --git a/crates/astria-sequencer/src/fees/component.rs b/crates/astria-sequencer/src/fees/component.rs index 03ed7cb36c..e22e4480d7 100644 --- a/crates/astria-sequencer/src/fees/component.rs +++ b/crates/astria-sequencer/src/fees/component.rs @@ -37,96 +37,96 @@ impl Component for FeesComponent { let transfer_fees = app_state.fees().transfer; if let Some(transfer_fees) = transfer_fees { state - .put_transfer_fees(transfer_fees) + .put_fees(transfer_fees) .wrap_err("failed to store transfer fee components")?; } let rollup_data_submission_fees = app_state.fees().rollup_data_submission; if let Some(rollup_data_submission_fees) = rollup_data_submission_fees { state - .put_rollup_data_submission_fees(rollup_data_submission_fees) + .put_fees(rollup_data_submission_fees) .wrap_err("failed to store rollup data submission fee components")?; } let ics20_withdrawal_fees = app_state.fees().ics20_withdrawal; if let Some(ics20_withdrawal_fees) = ics20_withdrawal_fees { state - .put_ics20_withdrawal_fees(ics20_withdrawal_fees) + .put_fees(ics20_withdrawal_fees) .wrap_err("failed to store ics20 withdrawal fee components")?; } let init_bridge_account_fees = app_state.fees().init_bridge_account; if let Some(init_bridge_account_fees) = init_bridge_account_fees { state - .put_init_bridge_account_fees(init_bridge_account_fees) + .put_fees(init_bridge_account_fees) .wrap_err("failed to store init bridge account fee components")?; } let bridge_lock_fees = app_state.fees().bridge_lock; if let Some(bridge_lock_fees) = bridge_lock_fees { state - .put_bridge_lock_fees(bridge_lock_fees) + .put_fees(bridge_lock_fees) .wrap_err("failed to store bridge lock fee components")?; } let bridge_unlock_fees = app_state.fees().bridge_unlock; if let Some(bridge_unlock_fees) = bridge_unlock_fees { state - .put_bridge_unlock_fees(bridge_unlock_fees) + .put_fees(bridge_unlock_fees) .wrap_err("failed to store bridge unlock fee components")?; } let bridge_sudo_change_fees = app_state.fees().bridge_sudo_change; if let Some(bridge_sudo_change_fees) = bridge_sudo_change_fees { state - .put_bridge_sudo_change_fees(bridge_sudo_change_fees) + .put_fees(bridge_sudo_change_fees) .wrap_err("failed to store bridge sudo change fee components")?; } let ibc_relay_fees = app_state.fees().ibc_relay; if let Some(ibc_relay_fees) = ibc_relay_fees { state - .put_ibc_relay_fees(ibc_relay_fees) + .put_fees(ibc_relay_fees) .wrap_err("failed to store ibc relay fee components")?; } let validator_update_fees = app_state.fees().validator_update; if let Some(validator_update_fees) = validator_update_fees { state - .put_validator_update_fees(validator_update_fees) + .put_fees(validator_update_fees) .wrap_err("failed to store validator update fee components")?; } let fee_asset_change_fees = app_state.fees().fee_asset_change; if let Some(fee_asset_change_fees) = fee_asset_change_fees { state - .put_fee_asset_change_fees(fee_asset_change_fees) + .put_fees(fee_asset_change_fees) .wrap_err("failed to store fee asset change fee components")?; } let fee_change_fees = app_state.fees().fee_change; state - .put_fee_change_fees(fee_change_fees) + .put_fees(fee_change_fees) .wrap_err("failed to store fee change fee components")?; let ibc_relayer_change_fees = app_state.fees().ibc_relayer_change; if let Some(ibc_relayer_change_fees) = ibc_relayer_change_fees { state - .put_ibc_relayer_change_fees(ibc_relayer_change_fees) + .put_fees(ibc_relayer_change_fees) .wrap_err("failed to store ibc relayer change fee components")?; } let sudo_address_change_fees = app_state.fees().sudo_address_change; if let Some(sudo_address_change_fees) = sudo_address_change_fees { state - .put_sudo_address_change_fees(sudo_address_change_fees) + .put_fees(sudo_address_change_fees) .wrap_err("failed to store sudo address change fee components")?; } let ibc_sudo_change_fees = app_state.fees().ibc_sudo_change; if let Some(ibc_sudo_change_fees) = ibc_sudo_change_fees { state - .put_ibc_sudo_change_fees(ibc_sudo_change_fees) + .put_fees(ibc_sudo_change_fees) .wrap_err("failed to store ibc sudo change fee components")?; } diff --git a/crates/astria-sequencer/src/fees/mod.rs b/crates/astria-sequencer/src/fees/mod.rs index 9536888e65..ba6557d522 100644 --- a/crates/astria-sequencer/src/fees/mod.rs +++ b/crates/astria-sequencer/src/fees/mod.rs @@ -1,8 +1,8 @@ use astria_core::{ primitive::v1::asset, - protocol::transaction::{ - self, - v1::action::{ + protocol::{ + fees::v1::FeeComponents, + transaction::v1::action::{ BridgeLock, BridgeSudoChange, BridgeUnlock, @@ -10,6 +10,7 @@ use astria_core::{ FeeChange, IbcRelayerChange, IbcSudoChange, + Ics20Withdrawal, InitBridgeAccount, RollupDataSubmission, SudoAddressChange, @@ -22,11 +23,13 @@ use astria_core::{ use astria_eyre::eyre::{ self, ensure, - OptionExt as _, + eyre, + Report, WrapErr as _, }; use cnidarium::StateWrite; use penumbra_ibc::IbcRelay; +use prost::Name; use tracing::{ instrument, Level, @@ -50,17 +53,12 @@ pub(crate) use state_ext::{ StateWriteExt, }; +use crate::storage::StoredValue; + /// The base byte length of a deposit, as determined by /// [`tests::get_base_deposit_fee()`]. const DEPOSIT_BASE_FEE: u128 = 16; -#[async_trait::async_trait] -pub(crate) trait FeeHandler { - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()>; - - fn variable_component(&self) -> u128; -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct Fee { action_name: String, @@ -80,279 +78,384 @@ impl Fee { } #[async_trait::async_trait] -impl FeeHandler for Transfer { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { +pub(crate) trait FeeHandler: Send { + /// The Pascal-case type name, e.g. `RollupDataSubmission`. + // NOTE: We only require this function due to `IbcRelay` not implementing `Protobuf`. + fn name() -> &'static str; + + /// The full name including the protobuf package, e.g. + /// `astria.protocol.transaction.v1.RollupDataSubmission`. + // NOTE: We only require this function due to `IbcRelay` not implementing `Protobuf`. + fn full_name() -> String; + + /// The snake-case type name, e.g. `rollup_data_submission`. + fn snake_case_name() -> &'static str; + + /// The variable value derived from `self` which is multiplied by the `multiplier` of the + /// `FeeComponents` for this action to produce the variable portion of the total fees for this + /// action. + /// + /// Many actions have fixed fees, meaning this method returns `0`. + fn variable_component(&self) -> u128; + + /// The asset to be used to pay the fees. + /// + /// If this method returns `None`, the action is free. + fn fee_asset(&self) -> Option<&asset::Denom>; + + #[instrument(skip_all, err(level = Level::WARN))] + async fn check_and_pay_fees<'a, S>(&self, mut state: S) -> eyre::Result<()> + where + S: StateWrite, + FeeComponents: TryFrom, Error = Report>, + { let fees = state - .get_transfer_fees() + .get_fees::() .await - .wrap_err("error fetching transfer fees")? - .ok_or_eyre("transfer fees not found, so this action is disabled")?; - check_and_pay_fees(self, fees.base, fees.multiplier, state, &self.fee_asset).await + .wrap_err_with(|| format!("error fetching {} fees", Self::name()))? + .ok_or_else(|| { + eyre!( + "{} fees not found, so this action is disabled", + Self::name() + ) + })?; + let Some(fee_asset) = self.fee_asset() else { + // If the action has no associated fee asset, there are no fees to pay. + return Ok(()); + }; + + ensure!( + state + .is_allowed_fee_asset(fee_asset) + .await + .wrap_err("failed to check allowed fee assets in state")?, + "invalid fee asset", + ); + + let variable_fee = self.variable_component().saturating_mul(fees.multiplier()); + let total_fees = fees.base().saturating_add(variable_fee); + let transaction_context = state + .get_transaction_context() + .expect("transaction source must be present in state when executing an action"); + let from = transaction_context.address_bytes(); + let position_in_transaction = transaction_context.position_in_transaction; + + state.add_fee_to_block_fees::<_, Self>(fee_asset, total_fees, position_in_transaction); + state + .decrease_balance(&from, fee_asset, total_fees) + .await + .wrap_err("failed to decrease balance for fee payment")?; + Ok(()) + } +} + +impl FeeHandler for Transfer { + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "transfer" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) + } } -#[async_trait::async_trait] impl FeeHandler for BridgeLock { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - let fees = state - .get_bridge_lock_fees() - .await - .wrap_err("error fetching bridge lock fees")? - .ok_or_eyre("bridge lock fees not found, so this action is disabled")?; - check_and_pay_fees(self, fees.base, fees.multiplier, state, &self.fee_asset).await + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "bridge_lock" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { base_deposit_fee(&self.asset, &self.destination_chain_address) } + + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) + } } -#[async_trait::async_trait] impl FeeHandler for BridgeSudoChange { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - let fees = state - .get_bridge_sudo_change_fees() - .await - .wrap_err("error fetching bridge sudo change fees")? - .ok_or_eyre("bridge sudo change fees not found, so this action is disabled")?; - check_and_pay_fees(self, fees.base, fees.multiplier, state, &self.fee_asset).await + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "bridge_sudo_change" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) + } } -#[async_trait::async_trait] impl FeeHandler for BridgeUnlock { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - let fees = state - .get_bridge_unlock_fees() - .await - .wrap_err("error fetching bridge unlock fees")? - .ok_or_eyre("bridge unlock fees not found, so this action is disabled")?; - check_and_pay_fees(self, fees.base, fees.multiplier, state, &self.fee_asset).await + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "bridge_unlock" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) + } } -#[async_trait::async_trait] impl FeeHandler for InitBridgeAccount { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - let fees = state - .get_init_bridge_account_fees() - .await - .wrap_err("error fetching init bridge account fees")? - .ok_or_eyre("init bridge account fees not found, so this action is disabled")?; - check_and_pay_fees(self, fees.base, fees.multiplier, state, &self.fee_asset).await + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "init_bridge_account" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) + } } -#[async_trait::async_trait] -impl FeeHandler for transaction::v1::action::Ics20Withdrawal { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - let fees = state - .get_ics20_withdrawal_fees() - .await - .wrap_err("error fetching ics20 withdrawal fees")? - .ok_or_eyre("ics20 withdrawal fees not found, so this action is disabled")?; - check_and_pay_fees(self, fees.base, fees.multiplier, state, &self.fee_asset).await +impl FeeHandler for Ics20Withdrawal { + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "ics20_withdrawal" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) + } } -#[async_trait::async_trait] impl FeeHandler for RollupDataSubmission { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - let fees = state - .get_rollup_data_submission_fees() - .await - .wrap_err("error fetching rollup data submission fees")? - .ok_or_eyre("rollup data submission fees not found, so this action is disabled")?; - check_and_pay_fees(self, fees.base, fees.multiplier, state, &self.fee_asset).await + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "rollup_data_submission" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { u128::try_from(self.data.len()) .expect("converting a usize to a u128 should work on any currently existing machine") } + + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) + } } -#[async_trait::async_trait] impl FeeHandler for ValidatorUpdate { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - state - .get_validator_update_fees() - .await - .wrap_err("error fetching validator update fees")? - .ok_or_eyre("validator update fees not found, so this action is disabled")?; - Ok(()) + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "validator_update" } fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + None + } } -#[async_trait::async_trait] impl FeeHandler for SudoAddressChange { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - state - .get_sudo_address_change_fees() - .await - .wrap_err("error fetching sudo address change fees")? - .ok_or_eyre("sudo address change fees not found, so this action is disabled")?; - Ok(()) + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "sudo_address_change" } fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + None + } } -#[async_trait::async_trait] impl FeeHandler for FeeChange { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - state - .get_fee_change_fees() - .await - .wrap_err("error fetching fee change fees")? - .ok_or_eyre("fee change fees not found, so this action is disabled")?; - Ok(()) + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "fee_change" } fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + None + } } -#[async_trait::async_trait] impl FeeHandler for IbcSudoChange { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - state - .get_ibc_sudo_change_fees() - .await - .wrap_err("error fetching ibc sudo change fees")? - .ok_or_eyre("ibc sudo change fees not found, so this action is disabled")?; - Ok(()) + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "ibc_sudo_change" } fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + None + } } -#[async_trait::async_trait] impl FeeHandler for IbcRelayerChange { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - state - .get_ibc_relayer_change_fees() - .await - .wrap_err("error fetching ibc relayer change fees")? - .ok_or_eyre("ibc relayer change fees not found, so this action is disabled")?; - Ok(()) + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "ibc_relayer_change" } fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + None + } } -#[async_trait::async_trait] impl FeeHandler for FeeAssetChange { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - state - .get_fee_asset_change_fees() - .await - .wrap_err("error fetching fee asset change fees")? - .ok_or_eyre("fee asset change fees not found, so this action is disabled")?; - Ok(()) + fn name() -> &'static str { + ::Raw::NAME + } + + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "fee_asset_change" } fn variable_component(&self) -> u128 { 0 } + + fn fee_asset(&self) -> Option<&asset::Denom> { + None + } } -#[async_trait::async_trait] impl FeeHandler for IbcRelay { - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { - state - .get_ibc_relay_fees() - .await - .wrap_err("error fetching ibc relay fees")? - .ok_or_eyre("ibc relay fees not found, so this action is disabled")?; - Ok(()) + fn name() -> &'static str { + penumbra_proto::penumbra::core::component::ibc::v1::IbcRelay::NAME + } + + fn full_name() -> String { + penumbra_proto::penumbra::core::component::ibc::v1::IbcRelay::full_name() + } + + fn snake_case_name() -> &'static str { + "ibc_relay" } fn variable_component(&self) -> u128 { 0 } -} -#[instrument(skip_all, err(level = Level::WARN))] -async fn check_and_pay_fees( - act: &T, - base: u128, - multiplier: u128, - mut state: S, - fee_asset: &asset::Denom, -) -> eyre::Result<()> { - let total_fees = base.saturating_add(act.variable_component().saturating_mul(multiplier)); - let transaction_context = state - .get_transaction_context() - .expect("transaction source must be present in state when executing an action"); - let from = transaction_context.address_bytes(); - let position_in_transaction = transaction_context.position_in_transaction; - - ensure!( - state - .is_allowed_fee_asset(fee_asset) - .await - .wrap_err("failed to check allowed fee assets in state")?, - "invalid fee asset", - ); - state.add_fee_to_block_fees::<_, T>(fee_asset, total_fees, position_in_transaction); - state - .decrease_balance(&from, fee_asset, total_fees) - .await - .wrap_err("failed to decrease balance for fee payment")?; - Ok(()) + fn fee_asset(&self) -> Option<&asset::Denom> { + None + } } /// Returns a modified byte length of the deposit event. Length is calculated with reasonable values diff --git a/crates/astria-sequencer/src/fees/query.rs b/crates/astria-sequencer/src/fees/query.rs index b2a74e18e6..d4b28dc768 100644 --- a/crates/astria-sequencer/src/fees/query.rs +++ b/crates/astria-sequencer/src/fees/query.rs @@ -12,7 +12,23 @@ use astria_core::{ protocol::{ abci::AbciErrorCode, asset::v1::AllowedFeeAssetsResponse, + fees::v1::FeeComponents, transaction::v1::{ + action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, + }, Action, TransactionBody, }, @@ -21,7 +37,9 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, + eyre, OptionExt as _, + Report, WrapErr as _, }; use cnidarium::{ @@ -32,6 +50,7 @@ use futures::{ FutureExt as _, StreamExt as _, }; +use penumbra_ibc::IbcRelay; use prost::{ Message as _, Name as _, @@ -51,13 +70,14 @@ use tracing::{ warn, }; -use super::{ - FeeHandler, - StateReadExt as _, -}; use crate::{ app::StateReadExt as _, assets::StateReadExt as _, + fees::{ + FeeHandler, + StateReadExt as _, + }, + storage::StoredValue, }; async fn find_trace_prefixed_or_return_ibc( @@ -260,204 +280,102 @@ pub(crate) async fn get_fees_for_transaction( tx: &TransactionBody, state: &S, ) -> eyre::Result> { - let transfer_fees = OnceCell::new(); - let rollup_data_submission_fees = OnceCell::new(); - let ics20_withdrawal_fees = OnceCell::new(); - let init_bridge_account_fees = OnceCell::new(); - let bridge_lock_fees = OnceCell::new(); - let bridge_unlock_fees = OnceCell::new(); - let bridge_sudo_change_fees = OnceCell::new(); - let validator_update_fees = OnceCell::new(); - let sudo_address_change_fees = OnceCell::new(); - let ibc_sudo_change_fees = OnceCell::new(); - let ibc_relay_fees = OnceCell::new(); - let ibc_relayer_change_fees = OnceCell::new(); - let fee_asset_change_fees = OnceCell::new(); - let fee_change_fees = OnceCell::new(); + let transfer_fees: OnceCell>> = OnceCell::new(); + let rollup_data_submission_fees: OnceCell>> = + OnceCell::new(); + let ics20_withdrawal_fees: OnceCell>> = OnceCell::new(); + let init_bridge_account_fees: OnceCell>> = + OnceCell::new(); + let bridge_lock_fees: OnceCell>> = OnceCell::new(); + let bridge_unlock_fees: OnceCell>> = OnceCell::new(); + let bridge_sudo_change_fees: OnceCell>> = + OnceCell::new(); + let validator_update_fees: OnceCell>> = OnceCell::new(); + let sudo_address_change_fees: OnceCell>> = + OnceCell::new(); + let ibc_sudo_change_fees: OnceCell>> = OnceCell::new(); + let ibc_relay_fees: OnceCell>> = OnceCell::new(); + let ibc_relayer_change_fees: OnceCell>> = + OnceCell::new(); + let fee_asset_change_fees: OnceCell>> = OnceCell::new(); + let fee_change_fees: OnceCell>> = OnceCell::new(); let mut fees_by_asset = HashMap::new(); for action in tx.actions() { match action { Action::Transfer(act) => { - let transfer_fees = transfer_fees - .get_or_try_init(|| async { state.get_transfer_fees().await }) - .await - .wrap_err("failed to get transfer fees")? - .ok_or_eyre("fees not found for `Transfer` action, hence it is disabled")?; - calculate_and_add_fees( - act, - act.fee_asset.to_ibc_prefixed(), - &mut fees_by_asset, - transfer_fees.base, - transfer_fees.multiplier, - ); + let fees = get_or_init_fees(state, &transfer_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } Action::RollupDataSubmission(act) => { - let rollup_data_submission_fees = rollup_data_submission_fees - .get_or_try_init(|| async { state.get_rollup_data_submission_fees().await }) - .await - .wrap_err("failed to get rollup data submission fees")? - .ok_or_eyre( - "fees not found for `RollupDataSubmission` action, hence it is disabled", - )?; - calculate_and_add_fees( - act, - act.fee_asset.to_ibc_prefixed(), - &mut fees_by_asset, - rollup_data_submission_fees.base, - rollup_data_submission_fees.multiplier, - ); + let fees = get_or_init_fees(state, &rollup_data_submission_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } Action::Ics20Withdrawal(act) => { - let ics20_withdrawal_fees = ics20_withdrawal_fees - .get_or_try_init(|| async { state.get_ics20_withdrawal_fees().await }) - .await - .wrap_err("failed to get ics20 withdrawal fees")? - .ok_or_eyre( - "fees not found for `Ics20Withdrawal` action, hence it is disabled", - )?; - calculate_and_add_fees( - act, - act.fee_asset.to_ibc_prefixed(), - &mut fees_by_asset, - ics20_withdrawal_fees.base, - ics20_withdrawal_fees.multiplier, - ); + let fees = get_or_init_fees(state, &ics20_withdrawal_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } Action::InitBridgeAccount(act) => { - let init_bridge_account_fees = init_bridge_account_fees - .get_or_try_init(|| async { state.get_init_bridge_account_fees().await }) - .await - .wrap_err("failed to get init bridge account fees")? - .ok_or_eyre( - "fees not found for `InitBridgeAccount` action, hence it is disabled", - )?; - calculate_and_add_fees( - act, - act.fee_asset.to_ibc_prefixed(), - &mut fees_by_asset, - init_bridge_account_fees.base, - init_bridge_account_fees.multiplier, - ); + let fees = get_or_init_fees(state, &init_bridge_account_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } Action::BridgeLock(act) => { - let bridge_lock_fees = bridge_lock_fees - .get_or_try_init(|| async { state.get_bridge_lock_fees().await }) - .await - .wrap_err("failed to get bridge lock fees")? - .ok_or_eyre("fees not found for `BridgeLock` action, hence it is disabled")?; - calculate_and_add_fees( - act, - act.fee_asset.to_ibc_prefixed(), - &mut fees_by_asset, - bridge_lock_fees.base, - bridge_lock_fees.multiplier, - ); + let fees = get_or_init_fees(state, &bridge_lock_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } Action::BridgeUnlock(act) => { - let bridge_unlock_fees = bridge_unlock_fees - .get_or_try_init(|| async { state.get_bridge_unlock_fees().await }) - .await - .wrap_err("failed to get bridge unlock fees")? - .ok_or_eyre("fees not found for `BridgeUnlock` action, hence it is disabled")?; - calculate_and_add_fees( - act, - act.fee_asset.to_ibc_prefixed(), - &mut fees_by_asset, - bridge_unlock_fees.base, - bridge_unlock_fees.multiplier, - ); + let fees = get_or_init_fees(state, &bridge_unlock_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } Action::BridgeSudoChange(act) => { - let bridge_sudo_change_fees = bridge_sudo_change_fees - .get_or_try_init(|| async { state.get_bridge_sudo_change_fees().await }) - .await - .wrap_err("failed to get bridge sudo change fees")? - .ok_or_eyre( - "fees not found for `BridgeSudoChange` action, hence it is disabled", - )?; - calculate_and_add_fees( - act, - act.fee_asset.to_ibc_prefixed(), - &mut fees_by_asset, - bridge_sudo_change_fees.base, - bridge_sudo_change_fees.multiplier, - ); + let fees = get_or_init_fees(state, &bridge_sudo_change_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } - Action::ValidatorUpdate(_) => { - validator_update_fees - .get_or_try_init(|| async { state.get_validator_update_fees().await }) - .await - .wrap_err("failed to get validator update fees")? - .ok_or_eyre( - "fees not found for `ValidatorUpdate` action, hence it is disabled", - )?; + Action::ValidatorUpdate(act) => { + let fees = get_or_init_fees(state, &validator_update_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } - Action::SudoAddressChange(_) => { - sudo_address_change_fees - .get_or_try_init(|| async { state.get_sudo_address_change_fees().await }) - .await - .wrap_err("failed to get sudo address change fees")? - .ok_or_eyre( - "fees not found for `SudoAddressChange` action, hence it is disabled", - )?; + Action::SudoAddressChange(act) => { + let fees = get_or_init_fees(state, &sudo_address_change_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } - Action::IbcSudoChange(_) => { - ibc_sudo_change_fees - .get_or_try_init(|| async { state.get_ibc_sudo_change_fees().await }) - .await - .wrap_err("failed to get ibc sudo change fees")? - .ok_or_eyre( - "fees not found for `IbcSudoChange` action, hence it is disabled", - )?; + Action::IbcSudoChange(act) => { + let fees = get_or_init_fees(state, &ibc_sudo_change_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } - Action::Ibc(_) => { - ibc_relay_fees - .get_or_try_init(|| async { state.get_ibc_relay_fees().await }) - .await - .wrap_err("failed to get ibc relay fees")? - .ok_or_eyre("fees not found for `IbcRelay` action, hence it is disabled")?; + Action::Ibc(act) => { + let fees = get_or_init_fees(state, &ibc_relay_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } - Action::IbcRelayerChange(_) => { - ibc_relayer_change_fees - .get_or_try_init(|| async { state.get_ibc_relayer_change_fees().await }) - .await - .wrap_err("failed to get ibc relayer change fees")? - .ok_or_eyre( - "fees not found for `IbcRelayerChange` action, hence it is disabled", - )?; + Action::IbcRelayerChange(act) => { + let fees = get_or_init_fees(state, &ibc_relayer_change_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } - Action::FeeAssetChange(_) => { - fee_asset_change_fees - .get_or_try_init(|| async { state.get_fee_asset_change_fees().await }) - .await - .wrap_err("failed to get fee asset change fees")? - .ok_or_eyre( - "fees not found for `FeeAssetChange` action, hence it is disabled", - )?; + Action::FeeAssetChange(act) => { + let fees = get_or_init_fees(state, &fee_asset_change_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } - Action::FeeChange(_) => { - fee_change_fees - .get_or_try_init(|| async { state.get_fee_change_fees().await }) - .await - .wrap_err("failed to get fee change fees")? - .ok_or_eyre( - "fees not found for `FeeChange` action, which cannot be disabled", - )?; + Action::FeeChange(act) => { + let fees = get_or_init_fees(state, &fee_change_fees).await?; + calculate_and_add_fees(act, &mut fees_by_asset, fees); } } } Ok(fees_by_asset) } -fn calculate_and_add_fees( - act: &T, - fee_asset: asset::IbcPrefixed, +fn calculate_and_add_fees( + action: &F, fees_by_asset: &mut HashMap, - base: u128, - multiplier: u128, + fees: &FeeComponents, ) { - let total_fees = base.saturating_add(multiplier.saturating_mul(act.variable_component())); + let Some(fee_asset) = action.fee_asset().map(Denom::to_ibc_prefixed) else { + // If there's no fee asset, we don't charge fees. + return; + }; + let base = fees.base(); + let multiplier = fees.multiplier(); + let total_fees = base.saturating_add(multiplier.saturating_mul(action.variable_component())); fees_by_asset .entry(fee_asset) .and_modify(|amt| *amt = amt.saturating_add(total_fees)) @@ -498,6 +416,29 @@ fn preprocess_fees_request(request: &request::Query) -> Result( + state: &S, + fee_components: &'a OnceCell>>, +) -> eyre::Result<&'a FeeComponents> +where + F: FeeHandler, + FeeComponents: TryFrom, Error = Report>, + S: StateRead, +{ + let fees = fee_components + .get_or_try_init(|| async { state.get_fees::().await }) + .await + .wrap_err_with(|| format!("failed to get fees for `{}` action", F::snake_case_name()))? + .as_ref() + .ok_or_else(|| { + eyre!( + "fees not found for `{}` action, hence it is disabled", + F::snake_case_name() + ) + })?; + Ok(fees) +} + #[derive(serde::Serialize)] struct AllFeeComponents { transfer: FetchResult, @@ -524,13 +465,13 @@ enum FetchResult { Component(FeeComponent), } -impl From>> for FetchResult -where - FeeComponent: From, -{ - fn from(value: eyre::Result>) -> Self { +impl From>>> for FetchResult { + fn from(value: eyre::Result>>) -> Self { match value { - Ok(Some(val)) => Self::Component(val.into()), + Ok(Some(val)) => Self::Component(FeeComponent { + base: val.base(), + multiplier: val.multiplier(), + }), Ok(None) => Self::Missing("not set"), Err(err) => Self::Err(err.to_string()), } @@ -546,30 +487,30 @@ async fn get_all_fee_components(state: &S) -> AllFeeComponents { bridge_lock, bridge_unlock, bridge_sudo_change, - ibc_relay, validator_update, - fee_asset_change, - fee_change, - ibc_relayer_change, sudo_address_change, ibc_sudo_change, + ibc_relay, + ibc_relayer_change, + fee_asset_change, + fee_change, ) = join!( - state.get_transfer_fees().map(fetch_to_response), + state.get_fees::().map(FetchResult::from), state - .get_rollup_data_submission_fees() - .map(fetch_to_response), - state.get_ics20_withdrawal_fees().map(fetch_to_response), - state.get_init_bridge_account_fees().map(fetch_to_response), - state.get_bridge_lock_fees().map(fetch_to_response), - state.get_bridge_unlock_fees().map(fetch_to_response), - state.get_bridge_sudo_change_fees().map(fetch_to_response), - state.get_ibc_relay_fees().map(fetch_to_response), - state.get_validator_update_fees().map(fetch_to_response), - state.get_fee_asset_change_fees().map(fetch_to_response), - state.get_fee_change_fees().map(fetch_to_response), - state.get_ibc_relayer_change_fees().map(fetch_to_response), - state.get_sudo_address_change_fees().map(fetch_to_response), - state.get_ibc_sudo_change_fees().map(fetch_to_response), + .get_fees::() + .map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), + state.get_fees::().map(FetchResult::from), ); AllFeeComponents { transfer, @@ -589,46 +530,8 @@ async fn get_all_fee_components(state: &S) -> AllFeeComponents { } } -fn fetch_to_response(value: eyre::Result>) -> FetchResult -where - FeeComponent: From, -{ - value.into() -} - #[derive(serde::Serialize)] struct FeeComponent { base: u128, multiplier: u128, } - -macro_rules! impl_from_domain_fee_component { - ( $($dt:ty ),* $(,)?) => { - $( - impl From<$dt> for FeeComponent { - fn from(val: $dt) -> Self { - Self { - base: val.base, - multiplier: val.multiplier, - } - } - } - )* - } -} -impl_from_domain_fee_component!( - astria_core::protocol::fees::v1::BridgeLockFeeComponents, - astria_core::protocol::fees::v1::BridgeSudoChangeFeeComponents, - astria_core::protocol::fees::v1::BridgeUnlockFeeComponents, - astria_core::protocol::fees::v1::FeeAssetChangeFeeComponents, - astria_core::protocol::fees::v1::FeeChangeFeeComponents, - astria_core::protocol::fees::v1::IbcRelayFeeComponents, - astria_core::protocol::fees::v1::IbcRelayerChangeFeeComponents, - astria_core::protocol::fees::v1::IbcSudoChangeFeeComponents, - astria_core::protocol::fees::v1::Ics20WithdrawalFeeComponents, - astria_core::protocol::fees::v1::InitBridgeAccountFeeComponents, - astria_core::protocol::fees::v1::RollupDataSubmissionFeeComponents, - astria_core::protocol::fees::v1::SudoAddressChangeFeeComponents, - astria_core::protocol::fees::v1::TransferFeeComponents, - astria_core::protocol::fees::v1::ValidatorUpdateFeeComponents, -); diff --git a/crates/astria-sequencer/src/fees/state_ext.rs b/crates/astria-sequencer/src/fees/state_ext.rs index d3a74ba65f..d62d682f86 100644 --- a/crates/astria-sequencer/src/fees/state_ext.rs +++ b/crates/astria-sequencer/src/fees/state_ext.rs @@ -10,27 +10,12 @@ use std::{ use astria_core::{ primitive::v1::asset, - protocol::fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, - }, - Protobuf, + protocol::fees::v1::FeeComponents, }; use astria_eyre::{ anyhow_to_eyre, eyre::{ + Report, Result, WrapErr as _, }, @@ -46,12 +31,9 @@ use tendermint::abci::Event; use tracing::instrument; use super::{ - storage::{ + storage::keys::{ self, - keys::{ - self, - extract_asset_from_allowed_asset_key, - }, + extract_asset_from_allowed_asset_key, }, Fee, FeeHandler, @@ -97,256 +79,26 @@ pub(crate) trait StateReadExt: StateRead { } #[instrument(skip_all)] - async fn get_transfer_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::TRANSFER) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw transfer fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::TransferFeeComponentsStorage::try_from(value) - .map(|fees| Some(TransferFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_rollup_data_submission_fees( - &self, - ) -> Result> { - let bytes = self - .get_raw(keys::ROLLUP_DATA_SUBMISSION) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw sequence fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::RollupDataSubmissionFeeComponentsStorage::try_from(value) - .map(|fees| Some(RollupDataSubmissionFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_ics20_withdrawal_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::ICS20_WITHDRAWAL) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw ics20 withdrawal fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::Ics20WithdrawalFeeComponentsStorage::try_from(value) - .map(|fees| Some(Ics20WithdrawalFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_init_bridge_account_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::INIT_BRIDGE_ACCOUNT) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw init bridge account fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::InitBridgeAccountFeeComponentsStorage::try_from(value) - .map(|fees| Some(InitBridgeAccountFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_bridge_lock_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::BRIDGE_LOCK) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw bridge lock fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::BridgeLockFeeComponentsStorage::try_from(value) - .map(|fees| Some(BridgeLockFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_bridge_unlock_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::BRIDGE_UNLOCK) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw bridge unlock fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::BridgeUnlockFeeComponentsStorage::try_from(value) - .map(|fees| Some(BridgeUnlockFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_bridge_sudo_change_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::BRIDGE_SUDO_CHANGE) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw bridge sudo change fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::BridgeSudoChangeFeeComponentsStorage::try_from(value) - .map(|fees| Some(BridgeSudoChangeFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_ibc_relay_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::IBC_RELAY) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw ibc relay fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::IbcRelayFeeComponentsStorage::try_from(value) - .map(|fees| Some(IbcRelayFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_validator_update_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::VALIDATOR_UPDATE) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw validator update fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::ValidatorUpdateFeeComponentsStorage::try_from(value) - .map(|fees| Some(ValidatorUpdateFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_fee_asset_change_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::FEE_ASSET_CHANGE) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw fee asset change fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::FeeAssetChangeFeeComponentsStorage::try_from(value) - .map(|fees| Some(FeeAssetChangeFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_fee_change_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::FEE_CHANGE) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw fee change fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::FeeChangeFeeComponentsStorage::try_from(value) - .map(|fees| Some(FeeChangeFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_ibc_relayer_change_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::IBC_RELAYER_CHANGE) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw ibc relayer change fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::IbcRelayerChangeFeeComponentsStorage::try_from(value) - .map(|fees| Some(IbcRelayerChangeFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_sudo_address_change_fees(&self) -> Result> { - let bytes = self - .get_raw(keys::SUDO_ADDRESS_CHANGE) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw sudo address change fee components from state")?; - let Some(bytes) = bytes else { - return Ok(None); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::SudoAddressChangeFeeComponentsStorage::try_from(value) - .map(|fees| Some(SudoAddressChangeFeeComponents::from(fees))) - }) - .wrap_err("invalid fees bytes") - } - - #[instrument(skip_all)] - async fn get_ibc_sudo_change_fees(&self) -> Result> { + async fn get_fees<'a, F>(&self) -> Result>> + where + F: FeeHandler + ?Sized, + FeeComponents: TryFrom, Error = Report>, + { let bytes = self - .get_raw(keys::IBC_SUDO_CHANGE) + .get_raw(&keys::name::()) .await .map_err(anyhow_to_eyre) - .wrap_err("failed reading raw ibc sudo change fee components from state")?; + .wrap_err_with(|| { + format!( + "failed reading raw {} fee components from state", + F::snake_case_name() + ) + })?; let Some(bytes) = bytes else { return Ok(None); }; StoredValue::deserialize(&bytes) - .and_then(|value| { - storage::IbcSudoChangeFeeComponentsStorage::try_from(value) - .map(|fees| Some(IbcSudoChangeFeeComponents::from(fees))) - }) + .and_then(|value| FeeComponents::::try_from(value).map(Some)) .wrap_err("invalid fees bytes") } @@ -378,7 +130,7 @@ impl StateReadExt for T {} pub(crate) trait StateWriteExt: StateWrite { /// Constructs and adds `Fee` object to the block fees vec. #[instrument(skip_all)] - fn add_fee_to_block_fees<'a, TAsset, T: FeeHandler + Protobuf>( + fn add_fee_to_block_fees<'a, TAsset, F: FeeHandler + ?Sized>( &mut self, asset: &'a TAsset, amount: u128, @@ -390,7 +142,7 @@ pub(crate) trait StateWriteExt: StateWrite { let current_fees: Option> = self.object_get(keys::BLOCK); let fee = Fee { - action_name: T::full_name(), + action_name: F::full_name(), asset: asset::IbcPrefixed::from(asset).into(), amount, position_in_transaction, @@ -411,133 +163,15 @@ pub(crate) trait StateWriteExt: StateWrite { } #[instrument(skip_all)] - fn put_transfer_fees(&mut self, fees: TransferFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::TransferFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::TRANSFER.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_rollup_data_submission_fees( - &mut self, - fees: RollupDataSubmissionFeeComponents, - ) -> Result<()> { - let bytes = StoredValue::from(storage::RollupDataSubmissionFeeComponentsStorage::from( - fees, - )) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::ROLLUP_DATA_SUBMISSION.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_ics20_withdrawal_fees(&mut self, fees: Ics20WithdrawalFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::Ics20WithdrawalFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::ICS20_WITHDRAWAL.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_init_bridge_account_fees(&mut self, fees: InitBridgeAccountFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::InitBridgeAccountFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::INIT_BRIDGE_ACCOUNT.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_bridge_lock_fees(&mut self, fees: BridgeLockFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::BridgeLockFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::BRIDGE_LOCK.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_bridge_unlock_fees(&mut self, fees: BridgeUnlockFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::BridgeUnlockFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::BRIDGE_UNLOCK.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_bridge_sudo_change_fees(&mut self, fees: BridgeSudoChangeFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::BridgeSudoChangeFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::BRIDGE_SUDO_CHANGE.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_ibc_relay_fees(&mut self, fees: IbcRelayFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::IbcRelayFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::IBC_RELAY.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_validator_update_fees(&mut self, fees: ValidatorUpdateFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::ValidatorUpdateFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::VALIDATOR_UPDATE.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_fee_asset_change_fees(&mut self, fees: FeeAssetChangeFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::FeeAssetChangeFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::FEE_ASSET_CHANGE.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_fee_change_fees(&mut self, fees: FeeChangeFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::FeeChangeFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::FEE_CHANGE.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_ibc_relayer_change_fees(&mut self, fees: IbcRelayerChangeFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::IbcRelayerChangeFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::IBC_RELAYER_CHANGE.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_sudo_address_change_fees(&mut self, fees: SudoAddressChangeFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::SudoAddressChangeFeeComponentsStorage::from(fees)) - .serialize() - .wrap_err("failed to serialize fees")?; - self.put_raw(keys::SUDO_ADDRESS_CHANGE.to_string(), bytes); - Ok(()) - } - - #[instrument(skip_all)] - fn put_ibc_sudo_change_fees(&mut self, fees: IbcSudoChangeFeeComponents) -> Result<()> { - let bytes = StoredValue::from(storage::IbcSudoChangeFeeComponentsStorage::from(fees)) + fn put_fees<'a, F>(&mut self, fees: FeeComponents) -> Result<()> + where + F: FeeHandler, + StoredValue<'a>: From>, + { + let bytes = StoredValue::from(fees) .serialize() .wrap_err("failed to serialize fees")?; - self.put_raw(keys::IBC_SUDO_CHANGE.to_string(), bytes); + self.put_raw(keys::name::(), bytes); Ok(()) } @@ -582,14 +216,18 @@ fn construct_tx_fee_event(fee: &Fee) -> Event { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::{ + collections::HashSet, + fmt::Debug, + }; - use astria_core::protocol::transaction::v1::action::Transfer; + use astria_core::protocol::transaction::v1::action::*; use cnidarium::StateDelta; use futures::{ StreamExt as _, TryStreamExt as _, }; + use penumbra_ibc::IbcRelay; use tokio::pin; use super::*; @@ -672,230 +310,90 @@ mod tests { ); } - #[tokio::test] - async fn transfer_fees_round_trip() { + async fn fees_round_trip<'a, F>() + where + F: FeeHandler, + FeeComponents: TryFrom, Error = Report> + Debug, + StoredValue<'a>: From>, + { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - let fee_components = TransferFeeComponents { - base: 123, - multiplier: 1, - }; + let fee_components = FeeComponents::::new(123, 1); + state.put_fees(fee_components).unwrap(); + let retrieved_fees = state.get_fees().await.unwrap(); + assert_eq!(retrieved_fees, Some(fee_components)); + } - state.put_transfer_fees(fee_components).unwrap(); - let retrieved_fee = state.get_transfer_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + #[tokio::test] + async fn transfer_fees_round_trip() { + fees_round_trip::().await; } #[tokio::test] async fn rollup_data_submission_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = RollupDataSubmissionFeeComponents { - base: 123, - multiplier: 1, - }; - - state - .put_rollup_data_submission_fees(fee_components) - .unwrap(); - let retrieved_fee = state.get_rollup_data_submission_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn ics20_withdrawal_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = Ics20WithdrawalFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_ics20_withdrawal_fees(fee_components).unwrap(); - let retrieved_fee = state.get_ics20_withdrawal_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn init_bridge_account_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = InitBridgeAccountFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_init_bridge_account_fees(fee_components).unwrap(); - let retrieved_fee = state.get_init_bridge_account_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn bridge_lock_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = BridgeLockFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_bridge_lock_fees(fee_components).unwrap(); - let retrieved_fee = state.get_bridge_lock_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn bridge_unlock_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = BridgeUnlockFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_bridge_unlock_fees(fee_components).unwrap(); - let retrieved_fee = state.get_bridge_unlock_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn bridge_sudo_change_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = BridgeSudoChangeFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_bridge_sudo_change_fees(fee_components).unwrap(); - let retrieved_fee = state.get_bridge_sudo_change_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn ibc_relay_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = IbcRelayFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_ibc_relay_fees(fee_components).unwrap(); - let retrieved_fee = state.get_ibc_relay_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn validator_update_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = ValidatorUpdateFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_validator_update_fees(fee_components).unwrap(); - let retrieved_fee = state.get_validator_update_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn fee_asset_change_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = FeeAssetChangeFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_fee_asset_change_fees(fee_components).unwrap(); - let retrieved_fee = state.get_fee_asset_change_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn fee_change_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = FeeChangeFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_fee_change_fees(fee_components).unwrap(); - let retrieved_fee = state.get_fee_change_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn ibc_relayer_change_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = IbcRelayerChangeFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_ibc_relayer_change_fees(fee_components).unwrap(); - let retrieved_fee = state.get_ibc_relayer_change_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn sudo_address_change_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = SudoAddressChangeFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_sudo_address_change_fees(fee_components).unwrap(); - let retrieved_fee = state.get_sudo_address_change_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] async fn ibc_sudo_change_fees_round_trip() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let fee_components = IbcSudoChangeFeeComponents { - base: 123, - multiplier: 1, - }; - - state.put_ibc_sudo_change_fees(fee_components).unwrap(); - let retrieved_fee = state.get_ibc_sudo_change_fees().await.unwrap(); - assert_eq!(retrieved_fee, Some(fee_components)); + fees_round_trip::().await; } #[tokio::test] diff --git a/crates/astria-sequencer/src/fees/storage/keys.rs b/crates/astria-sequencer/src/fees/storage/keys.rs index 946c9fbef5..b9584a9004 100644 --- a/crates/astria-sequencer/src/fees/storage/keys.rs +++ b/crates/astria-sequencer/src/fees/storage/keys.rs @@ -7,24 +7,16 @@ use astria_eyre::eyre::{ Context as _, }; -use crate::storage::keys::Asset; - -pub(in crate::fees) const TRANSFER: &str = "fees/transfer"; -pub(in crate::fees) const ICS20_WITHDRAWAL: &str = "fees/ics20_withdrawal"; -pub(in crate::fees) const INIT_BRIDGE_ACCOUNT: &str = "fees/init_bridge_account"; -pub(in crate::fees) const BRIDGE_LOCK: &str = "fees/bridge_lock"; -pub(in crate::fees) const BRIDGE_UNLOCK: &str = "fees/bridge_unlock"; -pub(in crate::fees) const BRIDGE_SUDO_CHANGE: &str = "fees/bridge_sudo_change"; -pub(in crate::fees) const IBC_RELAY: &str = "fees/ibc_relay"; -pub(in crate::fees) const VALIDATOR_UPDATE: &str = "fees/validator_update"; -pub(in crate::fees) const FEE_ASSET_CHANGE: &str = "fees/fee_asset_change"; -pub(in crate::fees) const FEE_CHANGE: &str = "fees/fee_change"; -pub(in crate::fees) const IBC_RELAYER_CHANGE: &str = "fees/ibc_relayer_change"; -pub(in crate::fees) const ROLLUP_DATA_SUBMISSION: &str = "fees/rollup_data_submission"; -pub(in crate::fees) const SUDO_ADDRESS_CHANGE: &str = "fees/sudo_address_change"; -pub(in crate::fees) const IBC_SUDO_CHANGE: &str = "fees/ibc_sudo_change"; +use crate::{ + fees::FeeHandler, + storage::keys::Asset, +}; + pub(in crate::fees) const BLOCK: &str = "fees/block"; // NOTE: `BLOCK` is only used in the ephemeral store. pub(in crate::fees) const ALLOWED_ASSET_PREFIX: &str = "fees/allowed_asset/"; +pub(in crate::fees) fn name() -> String { + format!("fees/{}", F::snake_case_name()) +} pub(in crate::fees) fn allowed_asset<'a, TAsset>(asset: &'a TAsset) -> String where @@ -51,8 +43,26 @@ fn extract_asset_from_key(key: &str, prefix: &str) -> eyre::Result #[cfg(test)] mod tests { - use astria_core::primitive::v1::asset::Denom; + use astria_core::{ + primitive::v1::asset::Denom, + protocol::transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, + }, + }; use insta::assert_snapshot; + use penumbra_ibc::IbcRelay; use super::*; @@ -64,42 +74,46 @@ mod tests { #[test] fn keys_should_not_change() { - // NOTE: This helper struct is just to avoid having 14 snapshot files to contend with. // NOTE: `BLOCK` is only used in the ephemeral store, so isn't included here. - assert_snapshot!("bridge_lock_fees_key", BRIDGE_LOCK); - assert_snapshot!("bridge_sudo_change_fees_key", BRIDGE_SUDO_CHANGE); - assert_snapshot!("bridge_unlock_fees_key", BRIDGE_UNLOCK); - assert_snapshot!("fee_asset_change_fees_key", FEE_ASSET_CHANGE); + + fn check() { + assert_snapshot!(format!("{}_fees_key", F::snake_case_name()), name::()); + } + + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); assert_snapshot!("allowed_asset_prefix", ALLOWED_ASSET_PREFIX); - assert_snapshot!("fee_change_fees_key", FEE_CHANGE); - assert_snapshot!("ibc_relay_fees_key", IBC_RELAY); - assert_snapshot!("ibc_relayer_change_fees_key", IBC_RELAYER_CHANGE); - assert_snapshot!("ibc_sudo_change_fees_key", IBC_SUDO_CHANGE); - assert_snapshot!("ics20_withdrawal_fees_key", ICS20_WITHDRAWAL); - assert_snapshot!("init_bridge_account_fees_key", INIT_BRIDGE_ACCOUNT); - assert_snapshot!("rollup_data_submission_fees_key", ROLLUP_DATA_SUBMISSION); - assert_snapshot!("sudo_address_change_fees_key", SUDO_ADDRESS_CHANGE); - assert_snapshot!("transer_fees_key", TRANSFER); - assert_snapshot!("validator_update_fees_key", VALIDATOR_UPDATE); assert_snapshot!("allowed_asset_key", allowed_asset(&test_asset())); } #[test] fn keys_should_have_component_prefix() { - assert!(TRANSFER.starts_with(COMPONENT_PREFIX)); - assert!(ROLLUP_DATA_SUBMISSION.starts_with(COMPONENT_PREFIX)); - assert!(ICS20_WITHDRAWAL.starts_with(COMPONENT_PREFIX)); - assert!(INIT_BRIDGE_ACCOUNT.starts_with(COMPONENT_PREFIX)); - assert!(BRIDGE_LOCK.starts_with(COMPONENT_PREFIX)); - assert!(BRIDGE_UNLOCK.starts_with(COMPONENT_PREFIX)); - assert!(BRIDGE_SUDO_CHANGE.starts_with(COMPONENT_PREFIX)); - assert!(IBC_RELAY.starts_with(COMPONENT_PREFIX)); - assert!(VALIDATOR_UPDATE.starts_with(COMPONENT_PREFIX)); - assert!(FEE_ASSET_CHANGE.starts_with(COMPONENT_PREFIX)); - assert!(FEE_CHANGE.starts_with(COMPONENT_PREFIX)); - assert!(IBC_RELAYER_CHANGE.starts_with(COMPONENT_PREFIX)); - assert!(SUDO_ADDRESS_CHANGE.starts_with(COMPONENT_PREFIX)); - assert!(IBC_SUDO_CHANGE.starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); + assert!(name::().starts_with(COMPONENT_PREFIX)); assert!(ALLOWED_ASSET_PREFIX.starts_with(COMPONENT_PREFIX)); assert!(allowed_asset(&test_asset()).starts_with(COMPONENT_PREFIX)); } diff --git a/crates/astria-sequencer/src/fees/storage/mod.rs b/crates/astria-sequencer/src/fees/storage/mod.rs index 2de95a747b..df15ffb7a2 100644 --- a/crates/astria-sequencer/src/fees/storage/mod.rs +++ b/crates/astria-sequencer/src/fees/storage/mod.rs @@ -2,19 +2,3 @@ pub(super) mod keys; mod values; pub(crate) use values::Value; -pub(super) use values::{ - BridgeLockFeeComponentsStorage, - BridgeSudoChangeFeeComponentsStorage, - BridgeUnlockFeeComponentsStorage, - FeeAssetChangeFeeComponentsStorage, - FeeChangeFeeComponentsStorage, - IbcRelayFeeComponentsStorage, - IbcRelayerChangeFeeComponentsStorage, - IbcSudoChangeFeeComponentsStorage, - Ics20WithdrawalFeeComponentsStorage, - InitBridgeAccountFeeComponentsStorage, - RollupDataSubmissionFeeComponentsStorage, - SudoAddressChangeFeeComponentsStorage, - TransferFeeComponentsStorage, - ValidatorUpdateFeeComponentsStorage, -}; diff --git a/crates/astria-sequencer/src/fees/storage/snapshots/astria_sequencer__fees__storage__keys__tests__transer_fees_key.snap b/crates/astria-sequencer/src/fees/storage/snapshots/astria_sequencer__fees__storage__keys__tests__transfer_fees_key.snap similarity index 100% rename from crates/astria-sequencer/src/fees/storage/snapshots/astria_sequencer__fees__storage__keys__tests__transer_fees_key.snap rename to crates/astria-sequencer/src/fees/storage/snapshots/astria_sequencer__fees__storage__keys__tests__transfer_fees_key.snap diff --git a/crates/astria-sequencer/src/fees/storage/values.rs b/crates/astria-sequencer/src/fees/storage/values.rs index c3dcc9fbe1..138b2f8cbd 100644 --- a/crates/astria-sequencer/src/fees/storage/values.rs +++ b/crates/astria-sequencer/src/fees/storage/values.rs @@ -1,24 +1,27 @@ -use astria_core::protocol::fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, +use astria_core::protocol::{ + fees::v1::FeeComponents as DomainFeeComponents, + transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, + }, }; use astria_eyre::eyre::bail; use borsh::{ BorshDeserialize, BorshSerialize, }; +use penumbra_ibc::IbcRelay; #[derive(Debug, BorshSerialize, BorshDeserialize)] pub(crate) struct Value(ValueImpl); @@ -29,178 +32,81 @@ pub(crate) struct Value(ValueImpl); reason = "want to make it clear that these are fees and not actions" )] enum ValueImpl { - TransferFees(TransferFeeComponentsStorage), - SequenceFees(RollupDataSubmissionFeeComponentsStorage), - Ics20WithdrawalFees(Ics20WithdrawalFeeComponentsStorage), - InitBridgeAccountFees(InitBridgeAccountFeeComponentsStorage), - BridgeLockFees(BridgeLockFeeComponentsStorage), - BridgeUnlockFees(BridgeUnlockFeeComponentsStorage), - BridgeSudoChangeFees(BridgeSudoChangeFeeComponentsStorage), - IbcRelayFees(IbcRelayFeeComponentsStorage), - ValidatorUpdateFees(ValidatorUpdateFeeComponentsStorage), - FeeAssetChangeFees(FeeAssetChangeFeeComponentsStorage), - FeeChangeFees(FeeChangeFeeComponentsStorage), - IbcRelayerChangeFees(IbcRelayerChangeFeeComponentsStorage), - IbcSudoChangeFees(IbcSudoChangeFeeComponentsStorage), - SudoAddressChangeFees(SudoAddressChangeFeeComponentsStorage), -} - -macro_rules! impl_from_for_fee_component{ - ( $( $domain_ty:ty => $storage_ty:ty),* $(,)? ) => { - $( - impl From<$domain_ty> for $storage_ty { - fn from(val: $domain_ty) -> Self { - Self{base: val.base, multiplier: val.multiplier} - } - } - impl From<$storage_ty> for $domain_ty { - fn from(val: $storage_ty) -> Self { - Self{base: val.base, multiplier: val.multiplier} - } - } - )* - } + TransferFees(FeeComponents), + RollupDataSubmissionFees(FeeComponents), + Ics20WithdrawalFees(FeeComponents), + InitBridgeAccountFees(FeeComponents), + BridgeLockFees(FeeComponents), + BridgeUnlockFees(FeeComponents), + BridgeSudoChangeFees(FeeComponents), + IbcRelayFees(FeeComponents), + ValidatorUpdateFees(FeeComponents), + FeeAssetChangeFees(FeeComponents), + FeeChangeFees(FeeComponents), + IbcRelayerChangeFees(FeeComponents), + IbcSudoChangeFees(FeeComponents), + SudoAddressChangeFees(FeeComponents), } macro_rules! impl_from_for_fee_storage { - ( $( $storage_ty:ty => $value_impl:ident),* $(,)? ) => { + ( $( $domain_ty:ty => $value_impl:ident),* $(,)? ) => { $( - impl<'a> From<$storage_ty> for crate::storage::StoredValue<'a> { - fn from(fees: $storage_ty) -> Self { - crate::storage::StoredValue::Fees(Value(ValueImpl::$value_impl(fees))) + impl<'a> From<$domain_ty> for crate::storage::StoredValue<'a> { + fn from(fees: $domain_ty) -> Self { + crate::storage::StoredValue::Fees(Value(ValueImpl::$value_impl(fees.into()))) } } - impl<'a> TryFrom> for $storage_ty { + impl<'a> TryFrom> for $domain_ty { type Error = astria_eyre::eyre::Error; - fn try_from(value: crate::storage::StoredValue<'a>) -> Result { - let crate::storage::StoredValue::Fees(Value(ValueImpl::$value_impl(fees))) = value else { - let value_impl_ty = concat!("ValueImpl::", stringify!($value_impl)); - bail!( - "fees stored value type mismatch: expected {value_impl_ty}, found {value:?}" - ); - }; - Ok(fees) - } + fn try_from(value: crate::storage::StoredValue<'a>) -> Result { + let crate::storage::StoredValue::Fees(Value(ValueImpl::$value_impl(fees))) = value else { + let value_impl_ty = concat!("ValueImpl::", stringify!($value_impl)); + bail!( + "fees stored value type mismatch: expected {value_impl_ty}, found {value:?}" + ); + }; + Ok(fees.into()) + } } )* } } #[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct TransferFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct RollupDataSubmissionFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct Ics20WithdrawalFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct InitBridgeAccountFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct BridgeLockFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct BridgeUnlockFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct BridgeSudoChangeFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct IbcRelayFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct ValidatorUpdateFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct FeeAssetChangeFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct FeeChangeFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, -} - -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct IbcRelayerChangeFeeComponentsStorage { +pub(in crate::fees) struct FeeComponents { pub base: u128, pub multiplier: u128, } -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct IbcSudoChangeFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, +impl From> for FeeComponents { + fn from(fees: DomainFeeComponents) -> Self { + Self { + base: fees.base(), + multiplier: fees.multiplier(), + } + } } -#[derive(Debug, BorshSerialize, BorshDeserialize)] -pub(in crate::fees) struct SudoAddressChangeFeeComponentsStorage { - pub base: u128, - pub multiplier: u128, +impl From for DomainFeeComponents { + fn from(fees: FeeComponents) -> Self { + Self::new(fees.base, fees.multiplier) + } } -impl_from_for_fee_component!( - TransferFeeComponents => TransferFeeComponentsStorage, - RollupDataSubmissionFeeComponents => RollupDataSubmissionFeeComponentsStorage, - Ics20WithdrawalFeeComponents => Ics20WithdrawalFeeComponentsStorage, - InitBridgeAccountFeeComponents => InitBridgeAccountFeeComponentsStorage, - BridgeLockFeeComponents => BridgeLockFeeComponentsStorage, - BridgeUnlockFeeComponents => BridgeUnlockFeeComponentsStorage, - BridgeSudoChangeFeeComponents => BridgeSudoChangeFeeComponentsStorage, - IbcRelayFeeComponents => IbcRelayFeeComponentsStorage, - ValidatorUpdateFeeComponents => ValidatorUpdateFeeComponentsStorage, - FeeAssetChangeFeeComponents => FeeAssetChangeFeeComponentsStorage, - FeeChangeFeeComponents => FeeChangeFeeComponentsStorage, - IbcRelayerChangeFeeComponents => IbcRelayerChangeFeeComponentsStorage, - IbcSudoChangeFeeComponents => IbcSudoChangeFeeComponentsStorage, - SudoAddressChangeFeeComponents => SudoAddressChangeFeeComponentsStorage, -); - impl_from_for_fee_storage!( - TransferFeeComponentsStorage => TransferFees, - RollupDataSubmissionFeeComponentsStorage => SequenceFees, - Ics20WithdrawalFeeComponentsStorage => Ics20WithdrawalFees, - InitBridgeAccountFeeComponentsStorage => InitBridgeAccountFees, - BridgeLockFeeComponentsStorage => BridgeLockFees, - BridgeUnlockFeeComponentsStorage => BridgeUnlockFees, - BridgeSudoChangeFeeComponentsStorage => BridgeSudoChangeFees, - IbcRelayFeeComponentsStorage => IbcRelayFees, - ValidatorUpdateFeeComponentsStorage => ValidatorUpdateFees, - FeeAssetChangeFeeComponentsStorage => FeeAssetChangeFees, - FeeChangeFeeComponentsStorage => FeeChangeFees, - IbcRelayerChangeFeeComponentsStorage => IbcRelayerChangeFees, - IbcSudoChangeFeeComponentsStorage => IbcSudoChangeFees, - SudoAddressChangeFeeComponentsStorage => SudoAddressChangeFees, + DomainFeeComponents => TransferFees, + DomainFeeComponents => RollupDataSubmissionFees, + DomainFeeComponents => Ics20WithdrawalFees, + DomainFeeComponents => InitBridgeAccountFees, + DomainFeeComponents => BridgeLockFees, + DomainFeeComponents => BridgeUnlockFees, + DomainFeeComponents => BridgeSudoChangeFees, + DomainFeeComponents => IbcRelayFees, + DomainFeeComponents => ValidatorUpdateFees, + DomainFeeComponents => FeeAssetChangeFees, + DomainFeeComponents => FeeChangeFees, + DomainFeeComponents => IbcRelayerChangeFees, + DomainFeeComponents => IbcSudoChangeFees, + DomainFeeComponents => SudoAddressChangeFees, ); diff --git a/crates/astria-sequencer/src/fees/tests.rs b/crates/astria-sequencer/src/fees/tests.rs index ca43c492c9..8a6f2202ff 100644 --- a/crates/astria-sequencer/src/fees/tests.rs +++ b/crates/astria-sequencer/src/fees/tests.rs @@ -11,13 +11,7 @@ use astria_core::{ TRANSACTION_ID_LEN, }, protocol::{ - fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - TransferFeeComponents, - }, + fees::v1::FeeComponents, transaction::v1::{ action::{ BridgeLock, @@ -80,10 +74,7 @@ async fn ensure_correct_block_fees_transfer() { let mut state = StateDelta::new(snapshot); let transfer_base = 1; state - .put_transfer_fees(TransferFeeComponents { - base: transfer_base, - multiplier: 0, - }) + .put_fees(FeeComponents::::new(transfer_base, 0)) .unwrap(); let alice = get_alice_signing_key(); @@ -120,10 +111,7 @@ async fn ensure_correct_block_fees_sequence() { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); state - .put_rollup_data_submission_fees(RollupDataSubmissionFeeComponents { - base: 1, - multiplier: 1, - }) + .put_fees(FeeComponents::::new(1, 1)) .unwrap(); let alice = get_alice_signing_key(); @@ -161,10 +149,10 @@ async fn ensure_correct_block_fees_init_bridge_acct() { let mut state = StateDelta::new(snapshot); let init_bridge_account_base = 1; state - .put_init_bridge_account_fees(InitBridgeAccountFeeComponents { - base: init_bridge_account_base, - multiplier: 0, - }) + .put_fees(FeeComponents::::new( + init_bridge_account_base, + 0, + )) .unwrap(); let alice = get_alice_signing_key(); @@ -212,16 +200,13 @@ async fn ensure_correct_block_fees_bridge_lock() { let bridge_lock_byte_cost_multiplier = 1; state - .put_transfer_fees(TransferFeeComponents { - base: transfer_base, - multiplier: 0, - }) + .put_fees(FeeComponents::::new(transfer_base, 0)) .unwrap(); state - .put_bridge_lock_fees(BridgeLockFeeComponents { - base: transfer_base, - multiplier: bridge_lock_byte_cost_multiplier, - }) + .put_fees(FeeComponents::::new( + transfer_base, + bridge_lock_byte_cost_multiplier, + )) .unwrap(); state .put_bridge_account_rollup_id(&bridge_address, rollup_id) @@ -283,10 +268,7 @@ async fn ensure_correct_block_fees_bridge_sudo_change() { let sudo_change_base = 1; state - .put_bridge_sudo_change_fees(BridgeSudoChangeFeeComponents { - base: sudo_change_base, - multiplier: 0, - }) + .put_fees(FeeComponents::::new(sudo_change_base, 0)) .unwrap(); state .put_bridge_account_sudo_address(&bridge_address, alice_address) @@ -338,17 +320,12 @@ async fn bridge_lock_fee_calculation_works_as_expected() { }); state.put_base_prefix(ASTRIA_PREFIX.to_string()).unwrap(); - let transfer_fees = TransferFeeComponents { - base: transfer_fee, - multiplier: 0, - }; - state.put_transfer_fees(transfer_fees).unwrap(); - - let bridge_lock_fees = BridgeLockFeeComponents { - base: transfer_fee, - multiplier: 2, - }; - state.put_bridge_lock_fees(bridge_lock_fees).unwrap(); + state + .put_fees(FeeComponents::::new(transfer_fee, 0)) + .unwrap(); + state + .put_fees(FeeComponents::::new(transfer_fee, 2)) + .unwrap(); let bridge_address = astria_address(&[1; 20]); let asset = test_asset(); diff --git a/crates/astria-sequencer/src/service/info/mod.rs b/crates/astria-sequencer/src/service/info/mod.rs index 6254f58632..e093514cae 100644 --- a/crates/astria-sequencer/src/service/info/mod.rs +++ b/crates/astria-sequencer/src/service/info/mod.rs @@ -175,21 +175,21 @@ mod tests { protocol::{ account::v1::BalanceResponse, asset::v1::DenomResponse, - fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, + fees::v1::FeeComponents, + transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, }, }, }; @@ -197,14 +197,10 @@ mod tests { StateDelta, StateWrite, }; + use penumbra_ibc::IbcRelay; use prost::Message as _; - use tendermint::v0_38::abci::{ - request, - InfoRequest, - InfoResponse, - }; - use super::Info; + use super::*; use crate::{ accounts::StateWriteExt as _, address::{ @@ -497,88 +493,46 @@ mod tests { fn write_all_the_fees(mut state: S) { state - .put_bridge_lock_fees(BridgeLockFeeComponents { - base: 1, - multiplier: 1, - }) + .put_fees(FeeComponents::::new(1, 1)) .unwrap(); state - .put_bridge_unlock_fees(BridgeUnlockFeeComponents { - base: 2, - multiplier: 2, - }) + .put_fees(FeeComponents::::new(2, 2)) .unwrap(); state - .put_bridge_sudo_change_fees(BridgeSudoChangeFeeComponents { - base: 3, - multiplier: 3, - }) + .put_fees(FeeComponents::::new(3, 3)) .unwrap(); state - .put_fee_asset_change_fees(FeeAssetChangeFeeComponents { - base: 4, - multiplier: 4, - }) + .put_fees(FeeComponents::::new(4, 4)) .unwrap(); state - .put_fee_change_fees(FeeChangeFeeComponents { - base: 5, - multiplier: 5, - }) + .put_fees(FeeComponents::::new(5, 5)) .unwrap(); state - .put_init_bridge_account_fees(InitBridgeAccountFeeComponents { - base: 6, - multiplier: 6, - }) + .put_fees(FeeComponents::::new(6, 6)) .unwrap(); state - .put_ibc_relay_fees(IbcRelayFeeComponents { - base: 7, - multiplier: 7, - }) + .put_fees(FeeComponents::::new(7, 7)) .unwrap(); state - .put_ibc_relayer_change_fees(IbcRelayerChangeFeeComponents { - base: 8, - multiplier: 8, - }) + .put_fees(FeeComponents::::new(8, 8)) .unwrap(); state - .put_ibc_sudo_change_fees(IbcSudoChangeFeeComponents { - base: 9, - multiplier: 9, - }) + .put_fees(FeeComponents::::new(9, 9)) .unwrap(); state - .put_ics20_withdrawal_fees(Ics20WithdrawalFeeComponents { - base: 10, - multiplier: 10, - }) + .put_fees(FeeComponents::::new(10, 10)) .unwrap(); state - .put_rollup_data_submission_fees(RollupDataSubmissionFeeComponents { - base: 11, - multiplier: 11, - }) + .put_fees(FeeComponents::::new(11, 11)) .unwrap(); state - .put_sudo_address_change_fees(SudoAddressChangeFeeComponents { - base: 12, - multiplier: 12, - }) + .put_fees(FeeComponents::::new(12, 12)) .unwrap(); state - .put_transfer_fees(TransferFeeComponents { - base: 13, - multiplier: 13, - }) + .put_fees(FeeComponents::::new(13, 13)) .unwrap(); state - .put_validator_update_fees(ValidatorUpdateFeeComponents { - base: 14, - multiplier: 14, - }) + .put_fees(FeeComponents::::new(14, 14)) .unwrap(); } } diff --git a/crates/astria-sequencer/src/test_utils.rs b/crates/astria-sequencer/src/test_utils.rs index 65c2ff327f..d9d7934239 100644 --- a/crates/astria-sequencer/src/test_utils.rs +++ b/crates/astria-sequencer/src/test_utils.rs @@ -1,9 +1,10 @@ -use astria_core::primitive::v1::{ - Address, - Bech32, +use astria_core::{ + primitive::v1::{ + Address, + Bech32, + }, + protocol::transaction::v1::action::RollupDataSubmission, }; -#[cfg(test)] -use astria_core::protocol::fees::v1::RollupDataSubmissionFeeComponents; use crate::benchmark_and_test_utils::ASTRIA_COMPAT_PREFIX; @@ -29,22 +30,20 @@ pub(crate) async fn calculate_rollup_data_submission_fee_from_state< data: &[u8], state: &S, ) -> u128 { - let RollupDataSubmissionFeeComponents { - base, - multiplier, - } = state - .get_rollup_data_submission_fees() + let fees = state + .get_fees::() .await .expect("should not error fetching rollup data submission fees") .expect("rollup data submission fees should be stored"); - base.checked_add( - multiplier - .checked_mul( - data.len() - .try_into() - .expect("a usize should always convert to a u128"), - ) - .expect("fee multiplication should not overflow"), - ) - .expect("fee addition should not overflow") + fees.base() + .checked_add( + fees.multiplier() + .checked_mul( + data.len() + .try_into() + .expect("a usize should always convert to a u128"), + ) + .expect("fee multiplication should not overflow"), + ) + .expect("fee addition should not overflow") } diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index 3e00c7b0f4..bff86e838e 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -132,17 +132,14 @@ mod tests { ADDRESS_LEN, }, protocol::{ - fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - TransferFeeComponents, - }, + fees::v1::FeeComponents, transaction::v1::{ action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + Ics20Withdrawal, + InitBridgeAccount, RollupDataSubmission, Transfer, }, @@ -174,7 +171,6 @@ mod tests { }; #[tokio::test] - #[expect(clippy::too_many_lines, reason = "it's a test")] async fn check_balance_total_fees_transfers_ok() { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); @@ -182,67 +178,26 @@ mod tests { state_tx.put_base_prefix("astria".to_string()).unwrap(); state_tx.put_native_asset(nria()).unwrap(); - let transfer_fees = TransferFeeComponents { - base: 12, - multiplier: 0, - }; state_tx - .put_transfer_fees(transfer_fees) - .wrap_err("failed to initiate transfer fee components") + .put_fees(FeeComponents::::new(12, 0)) .unwrap(); - - let rollup_data_submission_fees = RollupDataSubmissionFeeComponents { - base: 0, - multiplier: 1, - }; state_tx - .put_rollup_data_submission_fees(rollup_data_submission_fees) - .wrap_err("failed to initiate sequence action fee components") + .put_fees(FeeComponents::::new(0, 1)) .unwrap(); - - let ics20_withdrawal_fees = Ics20WithdrawalFeeComponents { - base: 1, - multiplier: 0, - }; state_tx - .put_ics20_withdrawal_fees(ics20_withdrawal_fees) - .wrap_err("failed to initiate ics20 withdrawal fee components") + .put_fees(FeeComponents::::new(1, 0)) .unwrap(); - - let init_bridge_account_fees = InitBridgeAccountFeeComponents { - base: 12, - multiplier: 0, - }; state_tx - .put_init_bridge_account_fees(init_bridge_account_fees) - .wrap_err("failed to initiate init bridge account fee components") + .put_fees(FeeComponents::::new(12, 0)) .unwrap(); - - let bridge_lock_fees = BridgeLockFeeComponents { - base: 0, - multiplier: 1, - }; state_tx - .put_bridge_lock_fees(bridge_lock_fees) - .wrap_err("failed to initiate bridge lock fee components") + .put_fees(FeeComponents::::new(0, 1)) .unwrap(); - - let bridge_unlock_fees = BridgeUnlockFeeComponents { - base: 0, - multiplier: 0, - }; state_tx - .put_bridge_unlock_fees(bridge_unlock_fees) - .wrap_err("failed to initiate bridge unlock fee components") + .put_fees(FeeComponents::::new(0, 0)) .unwrap(); - - let bridge_sudo_change_fees = BridgeSudoChangeFeeComponents { - base: 24, - multiplier: 0, - }; state_tx - .put_bridge_sudo_change_fees(bridge_sudo_change_fees) - .wrap_err("failed to initiate bridge sudo change fee components") + .put_fees(FeeComponents::::new(24, 0)) .unwrap(); let other_asset = "other".parse::().unwrap(); @@ -251,11 +206,11 @@ mod tests { let amount = 100; let data = Bytes::from_static(&[0; 32]); let transfer_fee = state_tx - .get_transfer_fees() + .get_fees::() .await .expect("should not error fetching transfer fees") .expect("transfer fees should be stored") - .base; + .base(); state_tx .increase_balance( &state_tx @@ -307,7 +262,6 @@ mod tests { } #[tokio::test] - #[expect(clippy::too_many_lines, reason = "it's a test")] async fn check_balance_total_fees_and_transfers_insufficient_other_asset_balance() { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); @@ -315,67 +269,26 @@ mod tests { state_tx.put_base_prefix(ASTRIA_PREFIX.to_string()).unwrap(); state_tx.put_native_asset(nria()).unwrap(); - let transfer_fees = TransferFeeComponents { - base: 12, - multiplier: 0, - }; state_tx - .put_transfer_fees(transfer_fees) - .wrap_err("failed to initiate transfer fee components") + .put_fees(FeeComponents::::new(12, 0)) .unwrap(); - - let rollup_data_submission_fees = RollupDataSubmissionFeeComponents { - base: 0, - multiplier: 1, - }; state_tx - .put_rollup_data_submission_fees(rollup_data_submission_fees) - .wrap_err("failed to initiate sequence action fee components") + .put_fees(FeeComponents::::new(0, 1)) .unwrap(); - - let ics20_withdrawal_fees = Ics20WithdrawalFeeComponents { - base: 1, - multiplier: 0, - }; state_tx - .put_ics20_withdrawal_fees(ics20_withdrawal_fees) - .wrap_err("failed to initiate ics20 withdrawal fee components") + .put_fees(FeeComponents::::new(1, 0)) .unwrap(); - - let init_bridge_account_fees = InitBridgeAccountFeeComponents { - base: 12, - multiplier: 0, - }; state_tx - .put_init_bridge_account_fees(init_bridge_account_fees) - .wrap_err("failed to initiate init bridge account fee components") + .put_fees(FeeComponents::::new(12, 0)) .unwrap(); - - let bridge_lock_fees = BridgeLockFeeComponents { - base: 0, - multiplier: 1, - }; state_tx - .put_bridge_lock_fees(bridge_lock_fees) - .wrap_err("failed to initiate bridge lock fee components") + .put_fees(FeeComponents::::new(0, 1)) .unwrap(); - - let bridge_unlock_fees = BridgeUnlockFeeComponents { - base: 0, - multiplier: 0, - }; state_tx - .put_bridge_unlock_fees(bridge_unlock_fees) - .wrap_err("failed to initiate bridge unlock fee components") + .put_fees(FeeComponents::::new(0, 0)) .unwrap(); - - let bridge_sudo_change_fees = BridgeSudoChangeFeeComponents { - base: 24, - multiplier: 0, - }; state_tx - .put_bridge_sudo_change_fees(bridge_sudo_change_fees) - .wrap_err("failed to initiate bridge sudo change fee components") + .put_fees(FeeComponents::::new(24, 0)) .unwrap(); let other_asset = "other".parse::().unwrap(); @@ -384,11 +297,11 @@ mod tests { let amount = 100; let data = Bytes::from_static(&[0; 32]); let transfer_fee = state_tx - .get_transfer_fees() + .get_fees::() .await .expect("should not error fetching transfer fees") .expect("transfer fees should be stored") - .base; + .base(); state_tx .increase_balance( &state_tx