From f6193ab2d96148a696637775a1a8ed12c6b175f4 Mon Sep 17 00:00:00 2001 From: Fraser Hutchison Date: Thu, 7 Nov 2024 16:39:28 +0000 Subject: [PATCH] further refactor of fees --- crates/astria-core/src/protocol/fees/v1.rs | 153 ++--- crates/astria-core/src/protocol/genesis/v1.rs | 111 +--- .../transaction/v1/action/group/tests.rs | 5 +- .../src/genesis_example.rs | 113 +--- .../src/app/benchmark_and_test_utils.rs | 175 ++---- .../src/app/tests_app/mempool.rs | 18 +- .../astria-sequencer/src/app/tests_app/mod.rs | 4 +- .../src/app/tests_execute_transaction.rs | 22 +- .../src/bridge/bridge_sudo_change_action.rs | 5 +- .../src/bridge/bridge_unlock_action.rs | 5 +- crates/astria-sequencer/src/fees/access.rs | 54 -- crates/astria-sequencer/src/fees/action.rs | 90 +-- crates/astria-sequencer/src/fees/component.rs | 28 +- crates/astria-sequencer/src/fees/mod.rs | 509 ++++++++-------- crates/astria-sequencer/src/fees/query.rs | 156 ++--- crates/astria-sequencer/src/fees/state_ext.rs | 543 ++---------------- .../astria-sequencer/src/fees/storage/keys.rs | 106 ++-- .../astria-sequencer/src/fees/storage/mod.rs | 16 - ...rage__keys__tests__transfer_fees_key.snap} | 0 .../src/fees/storage/values.rs | 206 ++----- crates/astria-sequencer/src/fees/tests.rs | 53 +- crates/astria-sequencer/src/test_utils.rs | 39 +- .../src/transaction/checks.rs | 120 +--- .../astria-sequencer/src/transaction/mod.rs | 21 +- 24 files changed, 772 insertions(+), 1780 deletions(-) delete mode 100644 crates/astria-sequencer/src/fees/access.rs rename crates/astria-sequencer/src/fees/storage/snapshots/{astria_sequencer__fees__storage__keys__tests__transer_fees_key.snap => astria_sequencer__fees__storage__keys__tests__transfer_fees_key.snap} (100%) diff --git a/crates/astria-core/src/protocol/fees/v1.rs b/crates/astria-core/src/protocol/fees/v1.rs index 400a63651..263980227 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::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()), @@ -83,89 +110,79 @@ impl_protobuf_for_fee_components!( IbcSudoChangeFeeComponents => 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, +pub struct FeeComponents { + base: u128, + multiplier: u128, + _phantom: PhantomData, } -#[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, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct BridgeUnlockFeeComponents { - 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 BridgeSudoChangeFeeComponents { - pub base: u128, - pub multiplier: u128, -} + #[must_use] + pub fn base(&self) -> u128 { + self.base + } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct IbcRelayFeeComponents { - pub base: u128, - pub multiplier: u128, + #[must_use] + pub fn multiplier(&self) -> u128 { + self.multiplier + } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct ValidatorUpdateFeeComponents { - 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 FeeAssetChangeFeeComponents { - pub base: u128, - pub multiplier: u128, +impl Debug for FeeComponents { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("IbcRelayFeeComponents") + .field("base", &self.base) + .field("multiplier", &self.multiplier) + .finish() + } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct FeeChangeFeeComponents { - pub base: u128, - pub multiplier: u128, +impl Clone for FeeComponents { + fn clone(&self) -> Self { + *self + } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct IbcRelayerChangeFeeComponents { - pub base: u128, - pub multiplier: u128, -} +impl Copy for FeeComponents {} -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct SudoAddressChangeFeeComponents { - 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(Clone, Copy, Debug, PartialEq)] -pub struct IbcSudoChangeFeeComponents { - pub base: u128, - pub multiplier: u128, -} +pub type TransferFeeComponents = FeeComponents; +pub type RollupDataSubmissionFeeComponents = FeeComponents; +pub type Ics20WithdrawalFeeComponents = FeeComponents; +pub type InitBridgeAccountFeeComponents = FeeComponents; +pub type BridgeLockFeeComponents = FeeComponents; +pub type BridgeUnlockFeeComponents = FeeComponents; +pub type BridgeSudoChangeFeeComponents = FeeComponents; +pub type IbcRelayFeeComponents = FeeComponents; +pub type ValidatorUpdateFeeComponents = FeeComponents; +pub type FeeAssetChangeFeeComponents = FeeComponents; +pub type FeeChangeFeeComponents = FeeComponents; +pub type IbcRelayerChangeFeeComponents = FeeComponents; +pub type SudoAddressChangeFeeComponents = FeeComponents; +pub type IbcSudoChangeFeeComponents = FeeComponents; #[derive(Debug, Clone)] pub struct TransactionFeeResponse { diff --git a/crates/astria-core/src/protocol/genesis/v1.rs b/crates/astria-core/src/protocol/genesis/v1.rs index 69bb2269b..a4a799056 100644 --- a/crates/astria-core/src/protocol/genesis/v1.rs +++ b/crates/astria-core/src/protocol/genesis/v1.rs @@ -826,7 +826,6 @@ mod tests { .unwrap() } - #[expect(clippy::too_many_lines, reason = "for testing purposes")] fn proto_genesis_state() -> raw::GenesisAppState { raw::GenesisAppState { accounts: vec![ @@ -859,104 +858,22 @@ mod tests { }), allowed_fee_assets: vec!["nria".into()], fees: Some(raw::GenesisFees { - transfer: Some( - TransferFeeComponents { - base: 12, - multiplier: 0, - } - .to_raw(), - ), + transfer: Some(TransferFeeComponents::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(), + RollupDataSubmissionFeeComponents::new(32, 1).to_raw(), ), + init_bridge_account: Some(InitBridgeAccountFeeComponents::new(48, 0).to_raw()), + bridge_lock: Some(BridgeLockFeeComponents::new(12, 1).to_raw()), + bridge_unlock: Some(BridgeUnlockFeeComponents::new(12, 0).to_raw()), + bridge_sudo_change: Some(BridgeSudoChangeFeeComponents::new(24, 0).to_raw()), + ics20_withdrawal: Some(Ics20WithdrawalFeeComponents::new(24, 0).to_raw()), + ibc_relay: Some(IbcRelayFeeComponents::new(0, 0).to_raw()), + validator_update: Some(ValidatorUpdateFeeComponents::new(0, 0).to_raw()), + fee_asset_change: Some(FeeAssetChangeFeeComponents::new(0, 0).to_raw()), + fee_change: Some(FeeChangeFeeComponents::new(0, 0).to_raw()), + ibc_relayer_change: Some(IbcRelayerChangeFeeComponents::new(0, 0).to_raw()), + sudo_address_change: Some(SudoAddressChangeFeeComponents::new(0, 0).to_raw()), + ibc_sudo_change: Some(IbcSudoChangeFeeComponents::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 6eb546ab9..4fd3dadef 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 @@ -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(TransferFeeComponents::new(100, 0))), Action::FeeAssetChange(FeeAssetChange::Addition(asset)), Action::IbcRelayerChange(IbcRelayerChange::Addition(address)), ]; diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index a464fd26c..15769a2d7 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -90,7 +90,6 @@ fn address_prefixes() -> AddressPrefixes { } } -#[expect(clippy::too_many_lines, reason = "all lines reasonably necessary")] fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::GenesisAppState { astria_core::generated::protocol::genesis::v1::GenesisAppState { accounts: accounts().into_iter().map(Protobuf::into_raw).collect(), @@ -107,104 +106,20 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::Genes }), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: Some(GenesisFees { - transfer: Some( - TransferFeeComponents { - base: 12, - multiplier: 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(), - ), + transfer: Some(TransferFeeComponents::new(12, 0).to_raw()), + rollup_data_submission: Some(RollupDataSubmissionFeeComponents::new(32, 1).to_raw()), + init_bridge_account: Some(InitBridgeAccountFeeComponents::new(48, 0).to_raw()), + bridge_lock: Some(BridgeLockFeeComponents::new(12, 1).to_raw()), + bridge_unlock: Some(BridgeUnlockFeeComponents::new(12, 0).to_raw()), + bridge_sudo_change: Some(BridgeSudoChangeFeeComponents::new(24, 0).to_raw()), + ics20_withdrawal: Some(Ics20WithdrawalFeeComponents::new(24, 0).to_raw()), + ibc_relay: Some(IbcRelayFeeComponents::new(0, 0).to_raw()), + validator_update: Some(ValidatorUpdateFeeComponents::new(0, 0).to_raw()), + fee_asset_change: Some(FeeAssetChangeFeeComponents::new(0, 0).to_raw()), + fee_change: Some(FeeChangeFeeComponents::new(0, 0).to_raw()), + ibc_relayer_change: Some(IbcRelayerChangeFeeComponents::new(0, 0).to_raw()), + sudo_address_change: Some(SudoAddressChangeFeeComponents::new(0, 0).to_raw()), + ibc_sudo_change: Some(IbcSudoChangeFeeComponents::new(0, 0).to_raw()), }), } } 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 6c46e5c6e..084b999ee 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -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(TransferFeeComponents::new(12, 0)), + rollup_data_submission: Some(RollupDataSubmissionFeeComponents::new(32, 1)), + init_bridge_account: Some(InitBridgeAccountFeeComponents::new(48, 0)), + // should reflect transfer fee + bridge_lock: Some(BridgeLockFeeComponents::new(12, 1)), + bridge_sudo_change: Some(BridgeSudoChangeFeeComponents::new(24, 0)), + ics20_withdrawal: Some(Ics20WithdrawalFeeComponents::new(24, 0)), + // should reflect transfer fee + bridge_unlock: Some(BridgeUnlockFeeComponents::new(12, 0)), + ibc_relay: Some(IbcRelayFeeComponents::new(0, 0)), + validator_update: Some(ValidatorUpdateFeeComponents::new(0, 0)), + fee_asset_change: Some(FeeAssetChangeFeeComponents::new(0, 0)), + fee_change: FeeChangeFeeComponents::new(0, 0), + ibc_relayer_change: Some(IbcRelayerChangeFeeComponents::new(0, 0)), + sudo_address_change: Some(SudoAddressChangeFeeComponents::new(0, 0)), + ibc_sudo_change: Some(IbcSudoChangeFeeComponents::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,87 @@ 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 = TransferFeeComponents::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 = RollupDataSubmissionFeeComponents::new(MOCK_SEQUENCE_FEE, 0); state - .put_rollup_data_submission_fees(rollup_data_submission_fees) + .put_fees(rollup_data_submission_fees) .wrap_err("failed to initiate sequence action fee components") .unwrap(); - let ics20_withdrawal_fees = Ics20WithdrawalFeeComponents { - base: 0, - multiplier: 0, - }; + let ics20_withdrawal_fees = Ics20WithdrawalFeeComponents::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 = InitBridgeAccountFeeComponents::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 = BridgeLockFeeComponents::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 = BridgeUnlockFeeComponents::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 = BridgeSudoChangeFeeComponents::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 = IbcRelayFeeComponents::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 = ValidatorUpdateFeeComponents::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 = FeeAssetChangeFeeComponents::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 = FeeChangeFeeComponents::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 = IbcRelayerChangeFeeComponents::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 = SudoAddressChangeFeeComponents::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 = IbcSudoChangeFeeComponents::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 adc304d58..9877fad2a 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -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(TransferFeeComponents::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(TransferFeeComponents::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(TransferFeeComponents::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 1ca46cec4..6f73510bc 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 7f4d99d65..ba9a83efc 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -136,11 +136,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()) @@ -208,11 +208,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()) @@ -275,10 +275,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(RollupDataSubmissionFeeComponents::new(0, 1)) .unwrap(); app.apply(state_tx); @@ -613,10 +610,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(InitBridgeAccountFeeComponents::new(fee, 0)) .unwrap(); app.apply(state_tx); @@ -1009,11 +1003,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/bridge/bridge_sudo_change_action.rs b/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs index 655694abf..68ef2fe1c 100644 --- a/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs @@ -159,10 +159,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(BridgeSudoChangeFeeComponents::new(10, 0)) .unwrap(); let fee_asset = test_asset(); diff --git a/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs b/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs index 52fcb2b40..f5358c305 100644 --- a/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs @@ -236,10 +236,7 @@ mod tests { let transfer_fee = 10; let transfer_amount = 100; state - .put_bridge_unlock_fees(BridgeUnlockFeeComponents { - base: transfer_fee, - multiplier: 0, - }) + .put_fees(BridgeUnlockFeeComponents::new(transfer_fee, 0)) .unwrap(); let to_address = astria_address(&[2; 20]); diff --git a/crates/astria-sequencer/src/fees/access.rs b/crates/astria-sequencer/src/fees/access.rs deleted file mode 100644 index 73b970dae..000000000 --- a/crates/astria-sequencer/src/fees/access.rs +++ /dev/null @@ -1,54 +0,0 @@ -use astria_core::protocol::fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, -}; - -pub(crate) trait FeeComponents { - fn base(&self) -> u128; - fn multiplier(&self) -> u128; -} - -macro_rules! impl_fee_components { - ($($fee_components:tt),* $(,)?) => { - $( - impl FeeComponents for $fee_components { - fn base(&self) -> u128 { - self.base - } - - fn multiplier(&self) -> u128 { - self.multiplier - } - } - )* - }; -} - -impl_fee_components!( - TransferFeeComponents, - RollupDataSubmissionFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - BridgeLockFeeComponents, - BridgeUnlockFeeComponents, - BridgeSudoChangeFeeComponents, - ValidatorUpdateFeeComponents, - IbcRelayerChangeFeeComponents, - IbcRelayFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - SudoAddressChangeFeeComponents, - IbcSudoChangeFeeComponents, -); diff --git a/crates/astria-sequencer/src/fees/action.rs b/crates/astria-sequencer/src/fees/action.rs index 4cf64cde6..61abd0fa1 100644 --- a/crates/astria-sequencer/src/fees/action.rs +++ b/crates/astria-sequencer/src/fees/action.rs @@ -43,46 +43,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"), } } @@ -135,6 +135,8 @@ impl ActionHandler for FeeAssetChange { #[cfg(test)] mod tests { + use std::fmt::Debug; + use astria_core::{ primitive::v1::TransactionId, protocol::{ @@ -142,15 +144,17 @@ mod tests { transaction::v1::action::*, }, }; + use astria_eyre::eyre::ErrReport; use penumbra_ibc::IbcRelay; use crate::{ app::ActionHandler as _, authority::StateWriteExt as _, fees::{ - access::FeeComponents, FeeHandler, + StateReadExt as _, }, + storage::StoredValue, transaction::{ StateWriteExt as _, TransactionContext, @@ -161,15 +165,9 @@ mod tests { ($fee_ty:tt) => { paste::item! { { - let initial_fees = [< $fee_ty FeeComponents >] { - base: 1, - multiplier: 2, - }; + let initial_fees = [< $fee_ty FeeComponents >] ::new(1, 2); let initial_fee_change = FeeChange::$fee_ty(initial_fees); - let new_fees = [< $fee_ty FeeComponents >] { - base: 3, - multiplier: 4, - }; + let new_fees = [< $fee_ty FeeComponents >] ::new(3, 4); let new_fee_change = FeeChange::$fee_ty(new_fees); (initial_fees, initial_fee_change, new_fees, new_fee_change) } @@ -181,7 +179,7 @@ mod tests { async fn transfer_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(Transfer); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -194,7 +192,7 @@ mod tests { async fn rollup_data_submission_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(RollupDataSubmission); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -207,7 +205,7 @@ mod tests { async fn ics_20_withdrawal_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(Ics20Withdrawal); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -220,7 +218,7 @@ mod tests { async fn init_bridge_account_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(InitBridgeAccount); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -233,7 +231,7 @@ mod tests { async fn bridge_lock_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(BridgeLock); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -246,7 +244,7 @@ mod tests { async fn bridge_unlock_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(BridgeUnlock); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -259,7 +257,7 @@ mod tests { async fn bridge_sudo_change_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(BridgeSudoChange); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -272,7 +270,7 @@ mod tests { async fn validator_update_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(ValidatorUpdate); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -285,7 +283,7 @@ mod tests { async fn ibc_relay_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(IbcRelay); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -298,7 +296,7 @@ mod tests { async fn ibc_relayer_change_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(IbcRelayerChange); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -311,7 +309,7 @@ mod tests { async fn fee_asset_change_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(FeeAssetChange); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -324,7 +322,7 @@ mod tests { async fn fee_change_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(FeeChange); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -337,7 +335,7 @@ mod tests { async fn sudo_address_change_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(SudoAddressChange); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -350,7 +348,7 @@ mod tests { async fn ibc_sudo_change_fee_change_action_executes_as_expected() { let (initial_fees, initial_fee_change, new_fees, new_fee_change) = get_default_fees_and_fee_changes!(IbcSudoChange); - test_fee_change_action::( + test_fee_change_action::( initial_fees, initial_fee_change, new_fees, @@ -359,13 +357,14 @@ mod tests { .await; } - async fn test_fee_change_action, T>( - initial_fees: T, + async fn test_fee_change_action<'a, F>( + initial_fees: FeeComponents, initial_fee_change: FeeChange, - new_fees: T, + new_fees: FeeComponents, new_fee_change: FeeChange, ) where - T: FeeComponents + std::fmt::Debug + std::cmp::PartialEq, + F: FeeHandler, + FeeComponents: TryFrom, Error = ErrReport> + Debug, { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); @@ -380,7 +379,8 @@ mod tests { state.put_sudo_address([1; 20]).unwrap(); assert!( - F::fee_components(&state) + state + .get_fees::() .await .expect("should not error fetching unstored action fees") .is_none() @@ -392,7 +392,8 @@ mod tests { .await .unwrap(); - let retrieved_fees = F::fee_components(&state) + let retrieved_fees = state + .get_fees::() .await .expect("should not error fetching initial action fees") .expect("initial action fees should be stored"); @@ -401,7 +402,8 @@ mod tests { // Execute a second fee change tx to overwrite the fees. new_fee_change.check_and_execute(&mut state).await.unwrap(); - let retrieved_fees = F::fee_components(&state) + let retrieved_fees = state + .get_fees::() .await .expect("should not error fetching new action fees") .expect("new action fees should be stored"); diff --git a/crates/astria-sequencer/src/fees/component.rs b/crates/astria-sequencer/src/fees/component.rs index 03ed7cb36..e22e4480d 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 b5234f13d..016de3e68 100644 --- a/crates/astria-sequencer/src/fees/mod.rs +++ b/crates/astria-sequencer/src/fees/mod.rs @@ -1,39 +1,21 @@ -use access::FeeComponents; use astria_core::{ primitive::v1::asset, protocol::{ - fees::v1::{ - BridgeLockFeeComponents, - BridgeSudoChangeFeeComponents, - BridgeUnlockFeeComponents, - FeeAssetChangeFeeComponents, - FeeChangeFeeComponents, - IbcRelayFeeComponents, - IbcRelayerChangeFeeComponents, - IbcSudoChangeFeeComponents, - Ics20WithdrawalFeeComponents, - InitBridgeAccountFeeComponents, - RollupDataSubmissionFeeComponents, - SudoAddressChangeFeeComponents, - TransferFeeComponents, - ValidatorUpdateFeeComponents, - }, - transaction::{ - self, - v1::action::{ - BridgeLock, - BridgeSudoChange, - BridgeUnlock, - FeeAssetChange, - FeeChange, - IbcRelayerChange, - IbcSudoChange, - InitBridgeAccount, - RollupDataSubmission, - SudoAddressChange, - Transfer, - ValidatorUpdate, - }, + fees::v1::FeeComponents, + transaction::v1::action::{ + BridgeLock, + BridgeSudoChange, + BridgeUnlock, + FeeAssetChange, + FeeChange, + IbcRelayerChange, + IbcSudoChange, + Ics20Withdrawal, + InitBridgeAccount, + RollupDataSubmission, + SudoAddressChange, + Transfer, + ValidatorUpdate, }, }, Protobuf, @@ -41,14 +23,13 @@ use astria_core::{ use astria_eyre::eyre::{ self, ensure, - OptionExt as _, + eyre, + ErrReport, WrapErr as _, }; -use cnidarium::{ - StateRead, - StateWrite, -}; +use cnidarium::StateWrite; use penumbra_ibc::IbcRelay; +use prost::Name; use tracing::{ instrument, Level, @@ -59,7 +40,6 @@ use crate::{ transaction::StateReadExt as _, }; -pub(crate) mod access; pub(crate) mod action; pub(crate) mod component; pub(crate) mod query; @@ -74,21 +54,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 { - type FeeComponents: FeeComponents; - - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()>; - - fn variable_component(&self) -> u128; - - async fn fee_components(state: S) -> eyre::Result>; -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct Fee { action_name: String, @@ -108,381 +79,379 @@ impl Fee { } #[async_trait::async_trait] -impl FeeHandler for Transfer { - type FeeComponents = TransferFeeComponents; +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; + + fn variable_component(&self) -> u128; - #[instrument(skip_all, err)] - async fn check_and_pay_fees(&self, state: S) -> eyre::Result<()> { + 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 = ErrReport>, + { let fees = state - .get_transfer_fees() + .get_fees::() + .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(()); + }; + + 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 source_action_index = transaction_context.source_action_index; + + 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::<_, Self>(fee_asset, total_fees, source_action_index) + .wrap_err("failed to add to block fees")?; + state + .decrease_balance(&from, fee_asset, total_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("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 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_transfer_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) } } -#[async_trait::async_trait] impl FeeHandler for BridgeLock { - type FeeComponents = BridgeLockFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 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) } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_bridge_lock_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) } } -#[async_trait::async_trait] impl FeeHandler for BridgeSudoChange { - type FeeComponents = BridgeSudoChangeFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "bridge_sudo_change" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_bridge_sudo_change_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) } } -#[async_trait::async_trait] impl FeeHandler for BridgeUnlock { - type FeeComponents = BridgeUnlockFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "bridge_unlock" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_bridge_unlock_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) } } -#[async_trait::async_trait] impl FeeHandler for InitBridgeAccount { - type FeeComponents = InitBridgeAccountFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "init_bridge_account" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_init_bridge_account_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) } } -#[async_trait::async_trait] -impl FeeHandler for transaction::v1::action::Ics20Withdrawal { - type FeeComponents = Ics20WithdrawalFeeComponents; +impl FeeHandler for Ics20Withdrawal { + fn name() -> &'static str { + ::Raw::NAME + } - #[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 + fn full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "ics20_withdrawal" } - #[instrument(skip_all)] fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_ics20_withdrawal_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) } } -#[async_trait::async_trait] impl FeeHandler for RollupDataSubmission { - type FeeComponents = RollupDataSubmissionFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 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") } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_rollup_data_submission_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + Some(&self.fee_asset) } } -#[async_trait::async_trait] impl FeeHandler for ValidatorUpdate { - type FeeComponents = ValidatorUpdateFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "validator_update" } fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_validator_update_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + None } } -#[async_trait::async_trait] impl FeeHandler for SudoAddressChange { - type FeeComponents = SudoAddressChangeFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "sudo_address_change" } fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_sudo_address_change_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + None } } -#[async_trait::async_trait] impl FeeHandler for FeeChange { - type FeeComponents = FeeChangeFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "fee_change" } fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_fee_change_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + None } } -#[async_trait::async_trait] impl FeeHandler for IbcSudoChange { - type FeeComponents = IbcSudoChangeFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "ibc_sudo_change" } fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_ibc_sudo_change_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + None } } -#[async_trait::async_trait] impl FeeHandler for IbcRelayerChange { - type FeeComponents = IbcRelayerChangeFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "ibc_relayer_change" } fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_ibc_relayer_change_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + None } } -#[async_trait::async_trait] impl FeeHandler for FeeAssetChange { - type FeeComponents = FeeAssetChangeFeeComponents; + fn name() -> &'static str { + ::Raw::NAME + } - #[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 full_name() -> String { + ::full_name() + } + + fn snake_case_name() -> &'static str { + "fee_asset_change" } fn variable_component(&self) -> u128 { 0 } - #[instrument(skip_all)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_fee_asset_change_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + None } } -#[async_trait::async_trait] impl FeeHandler for IbcRelay { - type FeeComponents = IbcRelayFeeComponents; + fn name() -> &'static str { + penumbra_proto::penumbra::core::component::ibc::v1::IbcRelay::NAME + } - #[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 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)] - async fn fee_components(state: S) -> eyre::Result> { - state.get_ibc_relay_fees().await + fn fee_asset(&self) -> Option<&asset::Denom> { + None } } -#[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 source_action_index = transaction_context.source_action_index; - - 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, source_action_index) - .wrap_err("failed to add to block fees")?; - state - .decrease_balance(&from, fee_asset, total_fees) - .await - .wrap_err("failed to decrease balance for fee payment")?; - Ok(()) -} - /// Returns a modified byte length of the deposit event. Length is calculated with reasonable values /// for all fields except `asset` and `destination_chain_address`, ergo it may not be representative /// of on-wire length. diff --git a/crates/astria-sequencer/src/fees/query.rs b/crates/astria-sequencer/src/fees/query.rs index 9434e9491..2e8ee8d61 100644 --- a/crates/astria-sequencer/src/fees/query.rs +++ b/crates/astria-sequencer/src/fees/query.rs @@ -12,6 +12,7 @@ use astria_core::{ protocol::{ abci::AbciErrorCode, asset::v1::AllowedFeeAssetsResponse, + fees::v1::FeeComponents, transaction::v1::{ action::{ BridgeLock, @@ -36,6 +37,8 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, + eyre, + ErrReport, OptionExt as _, WrapErr as _, }; @@ -70,10 +73,10 @@ use crate::{ app::StateReadExt as _, assets::StateReadExt as _, fees::{ - access::FeeComponents, FeeHandler, StateReadExt as _, }, + storage::StoredValue, }; async fn find_trace_prefixed_or_return_ibc( @@ -238,27 +241,31 @@ 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 = - get_or_init_fees::(state, &transfer_fees, "Transfer").await?; + let transfer_fees = get_or_init_fees(state, &transfer_fees).await?; calculate_and_add_fees( act, act.fee_asset.to_ibc_prefixed(), @@ -267,12 +274,8 @@ pub(crate) async fn get_fees_for_transaction( ); } Action::RollupDataSubmission(act) => { - let rollup_data_submission_fees = get_or_init_fees::( - state, - &rollup_data_submission_fees, - "RollupDataSubmission", - ) - .await?; + let rollup_data_submission_fees = + get_or_init_fees(state, &rollup_data_submission_fees).await?; calculate_and_add_fees( act, act.fee_asset.to_ibc_prefixed(), @@ -281,12 +284,7 @@ pub(crate) async fn get_fees_for_transaction( ); } Action::Ics20Withdrawal(act) => { - let ics20_withdrawal_fees = get_or_init_fees::( - state, - &ics20_withdrawal_fees, - "Ics20Withdrawal", - ) - .await?; + let ics20_withdrawal_fees = get_or_init_fees(state, &ics20_withdrawal_fees).await?; calculate_and_add_fees( act, act.fee_asset.to_ibc_prefixed(), @@ -295,12 +293,8 @@ pub(crate) async fn get_fees_for_transaction( ); } Action::InitBridgeAccount(act) => { - let init_bridge_account_fees = get_or_init_fees::( - state, - &init_bridge_account_fees, - "InitBridgeAccount", - ) - .await?; + let init_bridge_account_fees = + get_or_init_fees(state, &init_bridge_account_fees).await?; calculate_and_add_fees( act, act.fee_asset.to_ibc_prefixed(), @@ -309,9 +303,7 @@ pub(crate) async fn get_fees_for_transaction( ); } Action::BridgeLock(act) => { - let bridge_lock_fees = - get_or_init_fees::(state, &bridge_lock_fees, "BridgeLock") - .await?; + let bridge_lock_fees = get_or_init_fees(state, &bridge_lock_fees).await?; calculate_and_add_fees( act, act.fee_asset.to_ibc_prefixed(), @@ -320,12 +312,7 @@ pub(crate) async fn get_fees_for_transaction( ); } Action::BridgeUnlock(act) => { - let bridge_unlock_fees = get_or_init_fees::( - state, - &bridge_unlock_fees, - "BridgeUnlock", - ) - .await?; + let bridge_unlock_fees = get_or_init_fees(state, &bridge_unlock_fees).await?; calculate_and_add_fees( act, act.fee_asset.to_ibc_prefixed(), @@ -334,12 +321,8 @@ pub(crate) async fn get_fees_for_transaction( ); } Action::BridgeSudoChange(act) => { - let bridge_sudo_change_fees = get_or_init_fees::( - state, - &bridge_sudo_change_fees, - "BridgeSudoChange", - ) - .await?; + let bridge_sudo_change_fees = + get_or_init_fees(state, &bridge_sudo_change_fees).await?; calculate_and_add_fees( act, act.fee_asset.to_ibc_prefixed(), @@ -348,65 +331,40 @@ pub(crate) async fn get_fees_for_transaction( ); } Action::ValidatorUpdate(_) => { - get_or_init_fees::( - state, - &validator_update_fees, - "ValidatorUpdate", - ) - .await?; + get_or_init_fees(state, &validator_update_fees).await?; } Action::SudoAddressChange(_) => { - get_or_init_fees::( - state, - &sudo_address_change_fees, - "SudoAddressChange", - ) - .await?; + get_or_init_fees(state, &sudo_address_change_fees).await?; } Action::IbcSudoChange(_) => { - get_or_init_fees::( - state, - &ibc_sudo_change_fees, - "IbcSudoChange", - ) - .await?; + get_or_init_fees(state, &ibc_sudo_change_fees).await?; } Action::Ibc(_) => { - get_or_init_fees::(state, &ibc_relay_fees, "IbcRelay").await?; + get_or_init_fees(state, &ibc_relay_fees).await?; } Action::IbcRelayerChange(_) => { - get_or_init_fees::( - state, - &ibc_relayer_change_fees, - "IbcRelayerChange", - ) - .await?; + get_or_init_fees(state, &ibc_relayer_change_fees).await?; } Action::FeeAssetChange(_) => { - get_or_init_fees::( - state, - &fee_asset_change_fees, - "FeeAssetChange", - ) - .await?; + get_or_init_fees(state, &fee_asset_change_fees).await?; } Action::FeeChange(_) => { - get_or_init_fees::(state, &fee_change_fees, "FeeChange").await?; + get_or_init_fees(state, &fee_change_fees).await?; } } } Ok(fees_by_asset) } -fn calculate_and_add_fees( - act: &T, +fn calculate_and_add_fees( + action: &F, fee_asset: asset::IbcPrefixed, fees_by_asset: &mut HashMap, - fees: &F, + fees: &FeeComponents, ) { let base = fees.base(); let multiplier = fees.multiplier(); - let total_fees = base.saturating_add(multiplier.saturating_mul(act.variable_component())); + 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)) @@ -447,23 +405,25 @@ fn preprocess_fees_request(request: &request::Query) -> Result( +async fn get_or_init_fees<'a, F, S>( state: &S, - cell: &'a OnceCell>, - action_name: &str, -) -> eyre::Result<&'a T> + fee_components: &'a OnceCell>>, +) -> eyre::Result<&'a FeeComponents> where - Act: FeeHandler, + F: FeeHandler, + FeeComponents: TryFrom, Error = ErrReport>, S: StateRead, - T: FeeComponents, { - let fees = cell - .get_or_try_init(|| async { Act::fee_components(state).await }) + let fees = fee_components + .get_or_try_init(|| async { state.get_fees::().await }) .await - .wrap_err(format!("failed to get fees for `{action_name}` action"))? + .wrap_err_with(|| format!("failed to get fees for `{}` action", F::snake_case_name()))? .as_ref() - .ok_or_eyre(format!( - "fees not found for `{action_name}` action, hence it is disabled", - ))?; + .ok_or_else(|| { + eyre!( + "fees not found for `{}` action, hence it is disabled", + F::snake_case_name() + ) + })?; Ok(fees) } diff --git a/crates/astria-sequencer/src/fees/state_ext.rs b/crates/astria-sequencer/src/fees/state_ext.rs index 28a5e76eb..049cc0508 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::{ + ErrReport, Result, WrapErr as _, }, @@ -49,12 +34,9 @@ use tendermint::abci::{ 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, @@ -100,256 +82,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, T>(&self) -> Result>> + where + T: FeeHandler + ?Sized, + FeeComponents: TryFrom, Error = ErrReport>, + { 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", + T::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") } @@ -381,7 +133,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, @@ -394,7 +146,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, source_action_index, @@ -416,133 +168,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, T>(&mut self, fees: FeeComponents) -> Result<()> + where + T: 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(()) } @@ -590,10 +224,7 @@ mod tests { }; use astria_core::protocol::transaction::v1::action::*; - use cnidarium::{ - Snapshot, - StateDelta, - }; + use cnidarium::StateDelta; use futures::{ StreamExt as _, TryStreamExt as _, @@ -602,10 +233,7 @@ mod tests { use tokio::pin; use super::*; - use crate::{ - app::benchmark_and_test_utils::initialize_app_with_storage, - fees::access::FeeComponents, - }; + use crate::app::benchmark_and_test_utils::initialize_app_with_storage; fn asset_0() -> asset::Denom { "asset_0".parse().unwrap() @@ -619,17 +247,6 @@ mod tests { "asset_2".parse().unwrap() } - macro_rules! get_fee_components { - ($fee_ty:tt) => { - paste::item! { - [< $fee_ty FeeComponents>] { - base: 123, - multiplier: 1, - } - } - }; - } - #[tokio::test] async fn block_fee_read_and_increase() { let (_, storage) = initialize_app_with_storage(None, vec![]).await; @@ -701,118 +318,90 @@ mod tests { ); } + async fn fees_round_trip<'a, T>() + where + T: FeeHandler, + FeeComponents: TryFrom, Error = ErrReport> + 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 = 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)); + } + #[tokio::test] async fn transfer_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(Transfer); - state.put_transfer_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn rollup_data_submission_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(RollupDataSubmission); - state - .put_rollup_data_submission_fees(fee_components) - .unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn ics20_withdrawal_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(Ics20Withdrawal); - state.put_ics20_withdrawal_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn init_bridge_account_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(InitBridgeAccount); - state.put_init_bridge_account_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn bridge_lock_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(BridgeLock); - state.put_bridge_lock_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn bridge_unlock_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(BridgeUnlock); - state.put_bridge_unlock_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn bridge_sudo_change_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(BridgeSudoChange); - state.put_bridge_sudo_change_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn ibc_relay_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(IbcRelay); - state.put_ibc_relay_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn validator_update_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(ValidatorUpdate); - state.put_validator_update_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn fee_asset_change_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(FeeAssetChange); - state.put_fee_asset_change_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn fee_change_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(FeeChange); - state.put_fee_change_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn ibc_relayer_change_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(IbcRelayerChange); - state.put_ibc_relayer_change_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn sudo_address_change_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(SudoAddressChange); - state.put_sudo_address_change_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] async fn ibc_sudo_change_fees_round_trip() { - let mut state = get_default_state().await; - let fee_components = get_fee_components!(IbcSudoChange); - state.put_ibc_sudo_change_fees(fee_components).unwrap(); - assert_correct_fees::(state, fee_components).await; + fees_round_trip::().await; } #[tokio::test] @@ -933,20 +522,4 @@ mod tests { "delete for allowed fee asset did not behave as expected" ); } - - async fn get_default_state() -> StateDelta { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - StateDelta::new(snapshot) - } - - async fn assert_correct_fees(state: S, fee_components: F) - where - Act: FeeHandler, - F: FeeComponents + PartialEq + Debug, - S: StateRead, - { - let retrieved_fees = Act::fee_components(state).await.unwrap(); - assert_eq!(retrieved_fees, Some(fee_components)); - } } diff --git a/crates/astria-sequencer/src/fees/storage/keys.rs b/crates/astria-sequencer/src/fees/storage/keys.rs index 946c9fbef..af30aa16e 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/{}", T::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", T::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 2de95a747..df15ffb7a 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 c3dcc9fbe..451ec5be2 100644 --- a/crates/astria-sequencer/src/fees/storage/values.rs +++ b/crates/astria-sequencer/src/fees/storage/values.rs @@ -4,6 +4,7 @@ use astria_core::protocol::fees::v1::{ BridgeUnlockFeeComponents, FeeAssetChangeFeeComponents, FeeChangeFeeComponents, + FeeComponents as DomainFeeComponents, IbcRelayFeeComponents, IbcRelayerChangeFeeComponents, IbcSudoChangeFeeComponents, @@ -29,178 +30,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), + SequenceFees(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, + TransferFeeComponents => TransferFees, + RollupDataSubmissionFeeComponents => SequenceFees, + Ics20WithdrawalFeeComponents => Ics20WithdrawalFees, + InitBridgeAccountFeeComponents => InitBridgeAccountFees, + BridgeLockFeeComponents => BridgeLockFees, + BridgeUnlockFeeComponents => BridgeUnlockFees, + BridgeSudoChangeFeeComponents => BridgeSudoChangeFees, + IbcRelayFeeComponents => IbcRelayFees, + ValidatorUpdateFeeComponents => ValidatorUpdateFees, + FeeAssetChangeFeeComponents => FeeAssetChangeFees, + FeeChangeFeeComponents => FeeChangeFees, + IbcRelayerChangeFeeComponents => IbcRelayerChangeFees, + IbcSudoChangeFeeComponents => IbcSudoChangeFees, + SudoAddressChangeFeeComponents => SudoAddressChangeFees, ); diff --git a/crates/astria-sequencer/src/fees/tests.rs b/crates/astria-sequencer/src/fees/tests.rs index f486683a9..69aa3f50f 100644 --- a/crates/astria-sequencer/src/fees/tests.rs +++ b/crates/astria-sequencer/src/fees/tests.rs @@ -79,10 +79,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(TransferFeeComponents::new(transfer_base, 0)) .unwrap(); let alice = get_alice_signing_key(); @@ -119,10 +116,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(RollupDataSubmissionFeeComponents::new(1, 1)) .unwrap(); let alice = get_alice_signing_key(); @@ -160,10 +154,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(InitBridgeAccountFeeComponents::new( + init_bridge_account_base, + 0, + )) .unwrap(); let alice = get_alice_signing_key(); @@ -211,16 +205,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(TransferFeeComponents::new(transfer_base, 0)) .unwrap(); state - .put_bridge_lock_fees(BridgeLockFeeComponents { - base: transfer_base, - multiplier: bridge_lock_byte_cost_multiplier, - }) + .put_fees(BridgeLockFeeComponents::new( + transfer_base, + bridge_lock_byte_cost_multiplier, + )) .unwrap(); state .put_bridge_account_rollup_id(&bridge_address, rollup_id) @@ -282,10 +273,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(BridgeSudoChangeFeeComponents::new(sudo_change_base, 0)) .unwrap(); state .put_bridge_account_sudo_address(&bridge_address, alice_address) @@ -337,17 +325,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(TransferFeeComponents::new(transfer_fee, 0)) + .unwrap(); + state + .put_fees(BridgeLockFeeComponents::new(transfer_fee, 2)) + .unwrap(); let bridge_address = astria_address(&[1; 20]); let asset = test_asset(); diff --git a/crates/astria-sequencer/src/test_utils.rs b/crates/astria-sequencer/src/test_utils.rs index 65c2ff327..d9d793423 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 3e00c7b0f..c60de8003 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -174,7 +174,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 +181,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(TransferFeeComponents::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(RollupDataSubmissionFeeComponents::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(Ics20WithdrawalFeeComponents::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(InitBridgeAccountFeeComponents::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(BridgeLockFeeComponents::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(BridgeUnlockFeeComponents::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(BridgeSudoChangeFeeComponents::new(24, 0)) .unwrap(); let other_asset = "other".parse::().unwrap(); @@ -251,11 +209,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 +265,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 +272,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(TransferFeeComponents::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(RollupDataSubmissionFeeComponents::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(Ics20WithdrawalFeeComponents::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(InitBridgeAccountFeeComponents::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(BridgeLockFeeComponents::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(BridgeUnlockFeeComponents::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(BridgeSudoChangeFeeComponents::new(24, 0)) .unwrap(); let other_asset = "other".parse::().unwrap(); @@ -384,11 +300,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 diff --git a/crates/astria-sequencer/src/transaction/mod.rs b/crates/astria-sequencer/src/transaction/mod.rs index d430cca11..a56ebd6ab 100644 --- a/crates/astria-sequencer/src/transaction/mod.rs +++ b/crates/astria-sequencer/src/transaction/mod.rs @@ -3,14 +3,18 @@ mod state_ext; use std::fmt; -use astria_core::protocol::transaction::v1::{ - action::Action, - Transaction, +use astria_core::protocol::{ + fees::v1::FeeComponents, + transaction::v1::{ + action::Action, + Transaction, + }, }; use astria_eyre::{ anyhow_to_eyre, eyre::{ ensure, + ErrReport, OptionExt as _, Result, WrapErr as _, @@ -49,6 +53,7 @@ use crate::{ host_interface::AstriaHost, StateReadExt as _, }, + storage::StoredValue, }; #[derive(Debug)] @@ -285,10 +290,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 = ErrReport>, + S: StateWrite, +{ action.check_and_execute(&mut state).await?; action.check_and_pay_fees(&mut state).await?; Ok(())