From 3cd390ab41fa8457ffd51f021137775c04180c70 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Wed, 18 Sep 2024 10:40:38 -0400 Subject: [PATCH 01/18] ready for review --- .../src/bridge_withdrawer/submitter/mod.rs | 17 +- .../astria-cli/src/commands/bridge/submit.rs | 19 +- crates/astria-cli/src/commands/sequencer.rs | 19 +- crates/astria-composer/src/executor/mod.rs | 22 +- crates/astria-core/src/protocol/test_utils.rs | 19 +- .../transaction/v1alpha1/action_groups.rs | 229 ++++++++ .../src/protocol/transaction/v1alpha1/mod.rs | 136 ++++- .../src/sequencerblock/v1alpha1/block.rs | 2 +- .../astria-sequencer-client/src/tests/http.rs | 19 +- crates/astria-sequencer/src/app/mod.rs | 13 +- ...ransaction_with_every_action_snapshot.snap | 58 +- crates/astria-sequencer/src/app/test_utils.rs | 19 +- .../src/app/tests_app/mempool.rs | 129 +++-- .../astria-sequencer/src/app/tests_app/mod.rs | 141 +++-- .../src/app/tests_block_fees.rs | 105 ++-- .../src/app/tests_breaking_changes.rs | 327 +++++++++-- .../src/app/tests_execute_transaction.rs | 530 ++++++++++-------- .../astria-sequencer/src/benchmark_utils.rs | 28 +- .../src/proposal/commitment.rs | 52 +- .../astria-sequencer/src/service/consensus.rs | 19 +- .../src/transaction/checks.rs | 20 +- 21 files changed, 1342 insertions(+), 581 deletions(-) create mode 100644 crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs index 952153b7b7..b4789b6535 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs @@ -154,13 +154,16 @@ impl Submitter { .wrap_err("failed to get nonce from sequencer")?; debug!(nonce, "fetched latest nonce"); - let unsigned = UnsignedTransaction { - actions, - params: TransactionParams::builder() - .nonce(nonce) - .chain_id(sequencer_chain_id) - .build(), - }; + let unsigned = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(nonce) + .chain_id(sequencer_chain_id) + .build(), + ) + .build() + .wrap_err("failed to build unsigned transaction")?; // sign transaction let signed = unsigned.into_signed(signer.signing_key()); diff --git a/crates/astria-cli/src/commands/bridge/submit.rs b/crates/astria-cli/src/commands/bridge/submit.rs index c332a5a6d1..c3a531894a 100644 --- a/crates/astria-cli/src/commands/bridge/submit.rs +++ b/crates/astria-cli/src/commands/bridge/submit.rs @@ -129,14 +129,17 @@ async fn submit_transaction( .await .wrap_err("failed to get nonce")?; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(nonce_res.nonce) - .chain_id(chain_id) - .build(), - actions, - } - .into_signed(signing_key); + let tx = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(nonce_res.nonce) + .chain_id(chain_id) + .build(), + ) + .build() + .expect("failed to build transaction from actions") + .into_signed(signing_key); let res = client .submit_transaction_sync(tx) .await diff --git a/crates/astria-cli/src/commands/sequencer.rs b/crates/astria-cli/src/commands/sequencer.rs index 46259e4763..75221d66c9 100644 --- a/crates/astria-cli/src/commands/sequencer.rs +++ b/crates/astria-cli/src/commands/sequencer.rs @@ -475,14 +475,17 @@ async fn submit_transaction( .await .wrap_err("failed to get nonce")?; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(nonce_res.nonce) - .chain_id(chain_id) - .build(), - actions: vec![action], - } - .into_signed(&sequencer_key); + let tx = UnsignedTransaction::builder() + .actions(vec![action]) + .params( + TransactionParams::builder() + .nonce(nonce_res.nonce) + .chain_id(chain_id) + .build(), + ) + .build() + .expect("failed to build transaction from actions") + .into_signed(&sequencer_key); let res = sequencer_client .submit_transaction_sync(tx) .await diff --git a/crates/astria-composer/src/executor/mod.rs b/crates/astria-composer/src/executor/mod.rs index 0f2533cd79..19aa778050 100644 --- a/crates/astria-composer/src/executor/mod.rs +++ b/crates/astria-composer/src/executor/mod.rs @@ -681,11 +681,12 @@ impl Future for SubmitFut { .nonce(*this.nonce) .chain_id(&*this.chain_id) .build(); - let tx = UnsignedTransaction { - actions: this.bundle.clone().into_actions(), - params, - } - .into_signed(this.signing_key); + let tx = UnsignedTransaction::builder() + .actions(this.bundle.clone().into_actions()) + .params(params) + .build() + .expect("failed to build transaction from actions") + .into_signed(this.signing_key); info!( nonce.actual = *this.nonce, bundle = %telemetry::display::json(&SizedBundleReport(this.bundle)), @@ -759,11 +760,12 @@ impl Future for SubmitFut { .nonce(*this.nonce) .chain_id(&*this.chain_id) .build(); - let tx = UnsignedTransaction { - actions: this.bundle.clone().into_actions(), - params, - } - .into_signed(this.signing_key); + let tx = UnsignedTransaction::builder() + .actions(this.bundle.clone().into_actions()) + .params(params) + .build() + .expect("failed to build transaction from actions") + .into_signed(this.signing_key); info!( nonce.resubmission = *this.nonce, bundle = %telemetry::display::json(&SizedBundleReport(this.bundle)), diff --git a/crates/astria-core/src/protocol/test_utils.rs b/crates/astria-core/src/protocol/test_utils.rs index d5a3a3b8c6..f4cfe2988b 100644 --- a/crates/astria-core/src/protocol/test_utils.rs +++ b/crates/astria-core/src/protocol/test_utils.rs @@ -104,16 +104,19 @@ impl ConfigureSequencerBlock { let txs = if actions.is_empty() { vec![] } else { - let unsigned_transaction = UnsignedTransaction { - actions, - params: TransactionParams::builder() - .nonce(1) - .chain_id(chain_id.clone()) - .build(), - }; + let unsigned_transaction = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(1) + .chain_id(chain_id.clone()) + .build(), + ) + .build() + .expect("failed to build unsigned transaction"); + vec![unsigned_transaction.into_signed(&signing_key)] }; - let mut deposits_map: HashMap> = HashMap::new(); for deposit in deposits { if let Some(entry) = deposits_map.get_mut(&deposit.rollup_id) { diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs new file mode 100644 index 0000000000..fbf963c297 --- /dev/null +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs @@ -0,0 +1,229 @@ +use super::{ + action::{ + BridgeLockAction, + BridgeSudoChangeAction, + BridgeUnlockAction, + FeeAssetChangeAction, + FeeChangeAction, + IbcRelayerChangeAction, + Ics20Withdrawal, + InitBridgeAccountAction, + SequenceAction, + SudoAddressChangeAction, + TransferAction, + ValidatorUpdate, + }, + Action, +}; +trait Sealed {} + +trait BelongsToGroup: Sealed { + fn belongs_to_group(&self) -> ActionGroup; +} + +macro_rules! impl_belong_to_group { + ($($act:ty),*$(,)?) => { + $( + impl Sealed for $act {} + + impl BelongsToGroup for $act { + fn belongs_to_group(&self) -> ActionGroup { + Self::ACTION_GROUP.into() + } + } + )* + } +} + +impl SequenceAction { + const ACTION_GROUP: BundlableGeneral = BundlableGeneral; +} + +impl TransferAction { + const ACTION_GROUP: BundlableGeneral = BundlableGeneral; +} + +impl ValidatorUpdate { + const ACTION_GROUP: BundlableGeneral = BundlableGeneral; +} + +impl SudoAddressChangeAction { + const ACTION_GROUP: Sudo = Sudo; +} + +impl IbcRelayerChangeAction { + const ACTION_GROUP: BundlableSudo = BundlableSudo; +} + +impl Ics20Withdrawal { + const ACTION_GROUP: BundlableGeneral = BundlableGeneral; +} + +impl FeeAssetChangeAction { + const ACTION_GROUP: BundlableSudo = BundlableSudo; +} + +impl InitBridgeAccountAction { + const ACTION_GROUP: General = General; +} + +impl BridgeLockAction { + const ACTION_GROUP: BundlableGeneral = BundlableGeneral; +} + +impl BridgeUnlockAction { + const ACTION_GROUP: BundlableGeneral = BundlableGeneral; +} + +impl BridgeSudoChangeAction { + const ACTION_GROUP: General = General; +} + +impl FeeChangeAction { + const ACTION_GROUP: BundlableSudo = BundlableSudo; +} + +impl_belong_to_group!( + SequenceAction, + TransferAction, + ValidatorUpdate, + SudoAddressChangeAction, + IbcRelayerChangeAction, + Ics20Withdrawal, + InitBridgeAccountAction, + BridgeLockAction, + BridgeUnlockAction, + BridgeSudoChangeAction, + FeeChangeAction, + FeeAssetChangeAction +); + +impl Sealed for Action {} +impl BelongsToGroup for Action { + fn belongs_to_group(&self) -> ActionGroup { + match self { + Action::Sequence(act) => act.belongs_to_group(), + Action::Transfer(act) => act.belongs_to_group(), + Action::ValidatorUpdate(act) => act.belongs_to_group(), + Action::SudoAddressChange(act) => act.belongs_to_group(), + Action::IbcRelayerChange(act) => act.belongs_to_group(), + Action::Ics20Withdrawal(act) => act.belongs_to_group(), + Action::InitBridgeAccount(act) => act.belongs_to_group(), + Action::BridgeLock(act) => act.belongs_to_group(), + Action::BridgeUnlock(act) => act.belongs_to_group(), + Action::BridgeSudoChange(act) => act.belongs_to_group(), + Action::FeeChange(act) => act.belongs_to_group(), + Action::FeeAssetChange(act) => act.belongs_to_group(), + Action::Ibc(_) => BundlableGeneral.into(), /* Can't use implement on act + * directly since it lives in a externa + * crate */ + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BundlableGeneral; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct General; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BundlableSudo; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Sudo; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ActionGroup { + BundlableGeneral(BundlableGeneral), + General(General), + BundlableSudo(BundlableSudo), + Sudo(Sudo), +} + +impl From for ActionGroup { + fn from(val: BundlableGeneral) -> ActionGroup { + ActionGroup::BundlableGeneral(val) + } +} + +impl From for ActionGroup { + fn from(val: General) -> ActionGroup { + ActionGroup::General(val) + } +} + +impl From for ActionGroup { + fn from(val: BundlableSudo) -> ActionGroup { + ActionGroup::BundlableSudo(val) + } +} + +impl From for ActionGroup { + fn from(val: Sudo) -> ActionGroup { + ActionGroup::Sudo(val) + } +} + +#[derive(Debug, thiserror::Error)] +enum ActionGroupErrorKind { + #[error("input contains mixed action types")] + Mixed, + #[error("input attempted to bundle non bundleable action type")] + NotBundleable, +} + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct ActionGroupError(ActionGroupErrorKind); +impl ActionGroupError { + fn mixed() -> Self { + Self(ActionGroupErrorKind::Mixed) + } + + fn not_bundlable() -> Self { + Self(ActionGroupErrorKind::NotBundleable) + } +} + +/// Invariants: `group` is set if `inner` is not empty. +#[derive(Clone, Debug)] +pub(super) struct Actions { + group: Option, + inner: Vec, +} + +impl Actions { + pub(super) fn actions(&self) -> &[Action] { + &self.inner + } + + #[must_use] + pub(super) fn into_actions(self) -> Vec { + self.inner + } + + pub(super) fn group(&self) -> &Option { + &self.group + } + + pub(super) fn from_list_of_actions(actions: Vec) -> Result { + let mut group = None; + for action in &actions { + if group.is_none() { + group = Some(action.belongs_to_group()); + } else if group != Some(action.belongs_to_group()) { + return Err(ActionGroupError::mixed()); + } + } + + // assert size constraints on non-bundlable action groups + if (Some(ActionGroup::General(General)) == group || Some(ActionGroup::Sudo(Sudo)) == group) + && actions.len() > 1 + { + return Err(ActionGroupError::not_bundlable()); + } + Ok(Self { + group, + inner: actions, + }) + } +} diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index d534c9c548..f1b569dca9 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -1,3 +1,7 @@ +use action_groups::{ + ActionGroup, + Actions, +}; use bytes::Bytes; use prost::{ Message as _, @@ -21,6 +25,7 @@ use crate::{ }; pub mod action; +pub mod action_groups; pub use action::Action; #[derive(Debug, thiserror::Error)] @@ -198,7 +203,12 @@ impl SignedTransaction { #[must_use] pub fn actions(&self) -> &[Action] { - &self.transaction.actions + self.transaction.actions.actions() + } + + #[must_use] + pub fn group(&self) -> &Option { + self.transaction.actions.group() } #[must_use] @@ -226,14 +236,85 @@ impl SignedTransaction { } } +pub struct UnsignedTransactionBuilder { + actions: Vec, + params: TransactionParams, +} + +impl UnsignedTransactionBuilder { + fn new() -> Self { + Self { + actions: Vec::new(), + params: TransactionParams::from_raw(raw::TransactionParams { + nonce: 0, + chain_id: String::new(), + }), + } + } +} + +impl UnsignedTransactionBuilder { + #[must_use] + pub fn actions(self, actions: Vec) -> Self { + Self { + actions, + ..self + } + } + + #[must_use] + pub fn params(self, params: TransactionParams) -> Self { + Self { + params, + ..self + } + } + + /// Builds the `UnsignedTransaction` from the builder. + /// + /// # Errors + /// + /// This function will return an error if the actions list contains actions + /// of different `ActionGroup` types or violated an `ActionGroup`'s bundling constraints. + pub fn build(self) -> Result { + let Self { + actions, + params, + } = self; + + let actions = Actions::from_list_of_actions(actions) + .map_err(UnsignedTransactionError::action_group)?; + + Ok(UnsignedTransaction { + actions, + params, + }) + } +} + #[derive(Clone, Debug)] #[allow(clippy::module_name_repetitions)] pub struct UnsignedTransaction { - pub actions: Vec, - pub params: TransactionParams, + actions: Actions, + params: TransactionParams, } impl UnsignedTransaction { + #[must_use] + pub fn builder() -> UnsignedTransactionBuilder { + UnsignedTransactionBuilder::new() + } + + #[must_use] + pub fn into_actions(self) -> Vec { + self.actions.into_actions() + } + + #[must_use] + pub fn actions(&self) -> &[Action] { + self.actions.actions() + } + #[must_use] pub fn nonce(&self) -> u32 { self.params.nonce @@ -262,7 +343,11 @@ impl UnsignedTransaction { actions, params, } = self; - let actions = actions.into_iter().map(Action::into_raw).collect(); + let actions = actions + .into_actions() + .into_iter() + .map(Action::into_raw) + .collect(); raw::UnsignedTransaction { actions, params: Some(params.into_raw()), @@ -283,7 +368,7 @@ impl UnsignedTransaction { actions, params, } = self; - let actions = actions.iter().map(Action::to_raw).collect(); + let actions = actions.actions().iter().map(Action::to_raw).collect(); let params = params.clone().into_raw(); raw::UnsignedTransaction { actions, @@ -317,10 +402,10 @@ impl UnsignedTransaction { .collect::>() .map_err(UnsignedTransactionError::action)?; - Ok(Self { - actions, - params, - }) + UnsignedTransactionBuilder::new() + .actions(actions) + .params(params) + .build() } /// Attempt to convert from a protobuf [`pbjson_types::Any`]. @@ -362,6 +447,10 @@ impl UnsignedTransactionError { fn decode_any(inner: prost::DecodeError) -> Self { Self(UnsignedTransactionErrorKind::DecodeAny(inner)) } + + fn action_group(inner: action_groups::ActionGroupError) -> Self { + Self(UnsignedTransactionErrorKind::ActionGroup(inner)) + } } #[derive(Debug, thiserror::Error)] @@ -381,6 +470,8 @@ enum UnsignedTransactionErrorKind { raw::UnsignedTransaction::type_url() )] DecodeAny(#[source] prost::DecodeError), + #[error("failed to construct valid `Actions` from list of actions")] + ActionGroup(#[source] action_groups::ActionGroupError), } pub struct TransactionParamsBuilder> { @@ -557,10 +648,7 @@ enum TransactionFeeResponseErrorKind { mod test { use super::*; use crate::{ - primitive::v1::{ - asset, - Address, - }, + primitive::v1::Address, protocol::transaction::v1alpha1::action::TransferAction, }; const ASTRIA_ADDRESS_PREFIX: &str = "astria"; @@ -598,10 +686,12 @@ mod test { nonce: 1, chain_id: "test-1".to_string(), }); - let unsigned = UnsignedTransaction { - actions: vec![transfer.into()], - params, - }; + + let unsigned = UnsignedTransaction::builder() + .actions(vec![transfer.into()]) + .params(params) + .build() + .expect("failed to build unsigned transaction"); let tx = SignedTransaction { signature, @@ -635,12 +725,14 @@ mod test { nonce: 1, chain_id: "test-1".to_string(), }); - let unsigned = UnsignedTransaction { - actions: vec![transfer.into()], - params, - }; - let signed_tx = unsigned.into_signed(&signing_key); + let unsigned_tx = UnsignedTransaction::builder() + .actions(vec![transfer.into()]) + .params(params) + .build() + .expect("failed to build unsigned transaction"); + + let signed_tx = unsigned_tx.into_signed(&signing_key); let raw = signed_tx.to_raw(); // `try_from_raw` verifies the signature diff --git a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs index 8a677956af..0d1903f20e 100644 --- a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs @@ -735,7 +735,7 @@ impl SequencerBlock { .map_err(SequencerBlockError::signed_transaction_protobuf_decode)?; let signed_tx = SignedTransaction::try_from_raw(raw_tx) .map_err(SequencerBlockError::raw_signed_transaction_conversion)?; - for action in signed_tx.into_unsigned().actions { + for action in signed_tx.into_unsigned().into_actions() { // XXX: The fee asset is dropped. We shjould explain why that's ok. if let action::Action::Sequence(action::SequenceAction { rollup_id, diff --git a/crates/astria-sequencer-client/src/tests/http.rs b/crates/astria-sequencer-client/src/tests/http.rs index 5bfbc89f00..7c0752ad22 100644 --- a/crates/astria-sequencer-client/src/tests/http.rs +++ b/crates/astria-sequencer-client/src/tests/http.rs @@ -157,14 +157,17 @@ fn create_signed_transaction() -> SignedTransaction { } .into(), ]; - UnsignedTransaction { - params: TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - actions, - } - .into_signed(&alice_key) + UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .chain_id("test") + .nonce(1) + .build(), + ) + .build() + .unwrap() + .into_signed(&alice_key) } #[tokio::test] diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 66a9e2c5a8..2513a9a8f8 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -25,6 +25,7 @@ use astria_core::{ genesis::v1alpha1::GenesisAppState, transaction::v1alpha1::{ action::ValidatorUpdate, + action_groups::ActionGroup, Action, SignedTransaction, }, @@ -537,7 +538,7 @@ impl App { // check if tx's sequence data will fit into sequence block let tx_sequence_data_bytes = tx .unsigned_transaction() - .actions + .actions() .iter() .filter_map(Action::as_sequence) .fold(0usize, |acc, seq| acc.saturating_add(seq.data.len())); @@ -657,7 +658,7 @@ impl App { // check if tx's sequence data will fit into sequence block let tx_sequence_data_bytes = tx .unsigned_transaction() - .actions + .actions() .iter() .filter_map(Action::as_sequence) .fold(0usize, |acc, seq| acc.saturating_add(seq.data.len())); @@ -1024,10 +1025,10 @@ impl App { // flag mempool for cleaning if we ran a fee change action self.recost_mempool = self.recost_mempool - || signed_tx - .actions() - .iter() - .any(|action| matches!(action, Action::FeeAssetChange(_) | Action::FeeChange(_))); + || matches!(signed_tx.group(), Some(ActionGroup::BundlableSudo(_))) + && signed_tx.actions().iter().any(|action| { + matches!(action, Action::FeeAssetChange(_) | Action::FeeChange(_)) + }); Ok(state_tx.apply().1) } diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index 3e900c2423..5fc3a6fb73 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 18, - 206, - 200, - 101, - 160, - 129, - 124, - 188, - 249, - 181, + 233, + 91, + 186, + 227, + 243, + 121, + 116, + 152, + 3, + 78, + 219, + 120, + 70, + 57, + 166, + 123, + 68, 44, - 218, - 209, - 251, - 208, - 119, - 122, + 54, + 18, + 61, + 104, + 123, + 99, + 61, + 221, 211, - 105, + 2, + 208, + 148, 201, - 75, - 214, - 56, - 145, - 239, - 132, - 48, - 0, - 79, - 13, - 53, - 156 + 235 ] diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index afe705fb6e..678a9090e7 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -223,20 +223,23 @@ pub(crate) fn mock_tx( signer: &SigningKey, rollup_name: &str, ) -> Arc { - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(nonce) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(rollup_name.as_bytes()), data: Bytes::from_static(&[0x99]), fee_asset: denom_0(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(nonce) + .chain_id("test") + .build(), + ) + .build() + .expect("Failed to build unsigned transaction"); Arc::new(tx.into_signed(signer)) } diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index 65d327af80..807451cd6d 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -48,23 +48,25 @@ async fn trigger_cleaning() { let (mut app, storage) = initialize_app_with_storage(None, vec![]).await; app.prepare_commit(storage.clone()).await.unwrap(); app.commit(storage.clone()).await; - let sudo = get_judy_signing_key(); // create tx which will cause mempool cleaning flag to be set - let tx_trigger = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_trigger = UnsignedTransaction::builder() + .actions(vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, } .into(), - ], - } - .into_signed(&sudo); + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&get_judy_signing_key()); app.mempool .insert( @@ -147,24 +149,25 @@ async fn do_not_trigger_cleaning() { app.prepare_commit(storage.clone()).await.unwrap(); app.commit(storage.clone()).await; - let alice = get_alice_signing_key(); - // create tx which will fail execution and not trigger flag // (wrong sudo signer) - let tx_fail = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_fail = UnsignedTransaction::builder() + .actions(vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, } .into(), - ], - } - .into_signed(&alice); + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&get_alice_signing_key()); app.mempool .insert( @@ -224,12 +227,8 @@ async fn maintenance_recosting_promotes() { // create tx which will not be included in block due to // having insufficient funds (transaction will be recosted to enable) - let tx_fail_recost_funds = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_fail_recost_funds = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: astria_address_from_hex_string(CAROL_ADDRESS), amount: 1u128, @@ -237,9 +236,16 @@ async fn maintenance_recosting_promotes() { fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&get_bob_signing_key()); + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&get_bob_signing_key()); let mut bob_funds = HashMap::new(); bob_funds.insert(nria().into(), 11); @@ -256,20 +262,23 @@ async fn maintenance_recosting_promotes() { .unwrap(); // create tx which will enable recost tx to pass - let tx_recost = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_recost = UnsignedTransaction::builder() + .actions(vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, // originally 12 } .into(), - ], - } - .into_signed(&get_judy_signing_key()); + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&get_judy_signing_key()); let mut judy_funds = HashMap::new(); judy_funds.insert(nria().into(), 0); @@ -397,12 +406,8 @@ async fn maintenance_funds_added_promotes() { // create tx that will not be included in block due to // having no funds (will be sent transfer to then enable) - let tx_fail_transfer_funds = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_fail_transfer_funds = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: astria_address_from_hex_string(BOB_ADDRESS), amount: 10u128, @@ -410,9 +415,16 @@ async fn maintenance_funds_added_promotes() { fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&get_carol_signing_key()); + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&get_carol_signing_key()); let mut carol_funds = HashMap::new(); carol_funds.insert(nria().into(), 0); @@ -429,12 +441,8 @@ async fn maintenance_funds_added_promotes() { .unwrap(); // create tx which will enable no funds to pass - let tx_fund = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_fund = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: astria_address_from_hex_string(CAROL_ADDRESS), amount: 22u128, @@ -442,9 +450,16 @@ async fn maintenance_funds_added_promotes() { fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&get_alice_signing_key()); + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&get_alice_signing_key()); let mut alice_funds = HashMap::new(); alice_funds.insert(nria().into(), 100); diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 1c0253308e..3bae8d1acc 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -231,12 +231,8 @@ async fn app_transfer_block_fees_to_sudo() { // transfer funds from Alice to Bob; use native token for fee payment let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let amount = 333_333; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount, @@ -244,8 +240,15 @@ async fn app_transfer_block_fees_to_sudo() { fee_asset: nria().into(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -321,13 +324,17 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { data: Bytes::from_static(b"hello world"), fee_asset: nria().into(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![lock_action.into(), sequence_action.into()], - }; + + let tx = UnsignedTransaction::builder() + .actions(vec![lock_action.into(), sequence_action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -414,13 +421,17 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { data: Bytes::from_static(b"hello world"), fee_asset: nria().into(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![lock_action.into(), sequence_action.into()], - }; + + let tx = UnsignedTransaction::builder() + .actions(vec![lock_action.into(), sequence_action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -551,36 +562,43 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { // create txs which will cause cometBFT overflow let alice = get_alice_signing_key(); - let tx_pass = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_pass = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&alice); - let tx_overflow = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - actions: vec![ + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&alice); + + let tx_overflow = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&alice); + ]) + .params( + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&alice); app.mempool .insert( @@ -643,36 +661,43 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { // create txs which will cause sequencer overflow (max is currently 256_000 bytes) let alice = get_alice_signing_key(); - let tx_pass = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_pass = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 200_000]), fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&alice); - let tx_overflow = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - actions: vec![ + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&alice); + + let tx_overflow = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&alice); + ]) + .params( + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .build() + .expect("failed to build unsigned transaction") + .into_signed(&alice); app.mempool .insert( diff --git a/crates/astria-sequencer/src/app/tests_block_fees.rs b/crates/astria-sequencer/src/app/tests_block_fees.rs index 8ea3ce86b1..eb38fe8a71 100644 --- a/crates/astria-sequencer/src/app/tests_block_fees.rs +++ b/crates/astria-sequencer/src/app/tests_block_fees.rs @@ -53,12 +53,8 @@ async fn transaction_execution_records_fee_event() { let alice = get_alice_signing_key(); let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let value = 333_333; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: value, @@ -66,9 +62,15 @@ async fn transaction_execution_records_fee_event() { fee_asset: nria().into(), } .into(), - ], - }; - + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let events = app.execute_transaction(signed_tx).await.unwrap(); @@ -114,13 +116,16 @@ async fn ensure_correct_block_fees_transfer() { .into(), ]; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions, - }; + let tx = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -155,13 +160,16 @@ async fn ensure_correct_block_fees_sequence() { .into(), ]; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions, - }; + let tx = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -198,13 +206,16 @@ async fn ensure_correct_block_fees_init_bridge_acct() { .into(), ]; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions, - }; + let tx = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -252,13 +263,16 @@ async fn ensure_correct_block_fees_bridge_lock() { .into(), ]; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions, - }; + let tx = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); @@ -314,13 +328,16 @@ async fn ensure_correct_block_fees_bridge_sudo_change() { .into(), ]; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions, - }; + let tx = UnsignedTransaction::builder() + .actions(actions) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index dac0c289fe..586496b35b 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -23,7 +23,10 @@ use astria_core::{ BridgeLockAction, BridgeSudoChangeAction, BridgeUnlockAction, + FeeChange, + FeeChangeAction, IbcRelayerChangeAction, + Ics20Withdrawal, SequenceAction, TransferAction, ValidatorUpdate, @@ -37,6 +40,7 @@ use astria_core::{ Protobuf, }; use cnidarium::StateDelta; +use ibc_types::core::client::Height; use prost::{ bytes::Bytes, Message as _, @@ -111,13 +115,17 @@ async fn app_finalize_block_snapshot() { data: Bytes::from_static(b"hello world"), fee_asset: nria().into(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![lock_action.into(), sequence_action.into()], - }; + + let tx = UnsignedTransaction::builder() + .actions(vec![lock_action.into(), sequence_action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -201,12 +209,8 @@ async fn app_execute_transaction_with_every_action_snapshot() { let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx_bundlable_general = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: 333_333, @@ -221,6 +225,18 @@ async fn app_execute_transaction_with_every_action_snapshot() { } .into(), Action::ValidatorUpdate(update.clone()), + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); + + let tx_bundlable_sudo = UnsignedTransaction::builder() + .actions(vec![ IbcRelayerChangeAction::Addition(bob_address).into(), IbcRelayerChangeAction::Addition(carol_address).into(), IbcRelayerChangeAction::Removal(bob_address).into(), @@ -228,22 +244,47 @@ async fn app_execute_transaction_with_every_action_snapshot() { FeeAssetChangeAction::Addition("test-0".parse().unwrap()).into(), FeeAssetChangeAction::Addition("test-1".parse().unwrap()).into(), FeeAssetChangeAction::Removal("test-0".parse().unwrap()).into(), + ]) + .params( + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); + + let tx_sudo = UnsignedTransaction::builder() + .actions(vec![ SudoAddressChangeAction { new_address: bob_address, } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); - let signed_tx = Arc::new(tx.into_signed(&alice)); - app.execute_transaction(signed_tx).await.unwrap(); + let signed_tx_general_bundlable = Arc::new(tx_bundlable_general.into_signed(&alice)); + app.execute_transaction(signed_tx_general_bundlable) + .await + .unwrap(); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let signed_tx_sudo_bundlable = Arc::new(tx_bundlable_sudo.into_signed(&alice)); + app.execute_transaction(signed_tx_sudo_bundlable) + .await + .unwrap(); + + let signed_tx_sudo = Arc::new(tx_sudo.into_signed(&alice)); + app.execute_transaction(signed_tx_sudo).await.unwrap(); + + let tx = UnsignedTransaction::builder() + .actions(vec![ InitBridgeAccountAction { rollup_id, asset: nria().into(), @@ -252,17 +293,20 @@ async fn app_execute_transaction_with_every_action_snapshot() { withdrawer_address: None, } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .chain_id("test") - .nonce(1) - .build(), - actions: vec![ + let tx_bridge_bundlable = UnsignedTransaction::builder() + .actions(vec![ BridgeLockAction { to: bridge_address, amount: 100, @@ -281,6 +325,21 @@ async fn app_execute_transaction_with_every_action_snapshot() { rollup_withdrawal_event_id: "a-rollup-defined-hash".to_string(), } .into(), + ]) + .params( + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); + + let signed_tx = Arc::new(tx_bridge_bundlable.into_signed(&bridge)); + app.execute_transaction(signed_tx).await.unwrap(); + + let tx_bridge = UnsignedTransaction::builder() + .actions(vec![ BridgeSudoChangeAction { bridge_address, new_sudo_address: Some(bob_address), @@ -288,10 +347,17 @@ async fn app_execute_transaction_with_every_action_snapshot() { fee_asset: nria().into(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); - let signed_tx = Arc::new(tx.into_signed(&bridge)); + let signed_tx = Arc::new(tx_bridge.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); let sudo_address = app.state.get_sudo_address().await.unwrap(); @@ -302,3 +368,196 @@ async fn app_execute_transaction_with_every_action_snapshot() { insta::assert_json_snapshot!(app.app_hash.as_bytes()); } + +// Note: this test ensures that all actions are in their expected +// bundlable vs non-bundlable category. Tests all actions except +// IbcRelay. +// +// If any PR changes the bundlable status of an action, the PR must +// be marked as breaking. +#[allow(clippy::too_many_lines)] +#[tokio::test] +async fn app_transaction_bundle_categories() { + use astria_core::protocol::transaction::v1alpha1::action::{ + FeeAssetChangeAction, + InitBridgeAccountAction, + SudoAddressChangeAction, + }; + + let bridge = get_bridge_signing_key(); + let bridge_address = astria_address(&bridge.address_bytes()); + let bob_address = astria_address_from_hex_string(BOB_ADDRESS); + let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); + + assert!( + UnsignedTransaction::builder() + .actions(vec![ + TransferAction { + to: bob_address, + amount: 333_333, + asset: nria().into(), + fee_asset: nria().into(), + } + .into(), + SequenceAction { + rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), + data: Bytes::from_static(b"hello world"), + fee_asset: nria().into(), + } + .into(), + Action::ValidatorUpdate(ValidatorUpdate { + power: 100, + verification_key: crate::test_utils::verification_key(1), + }), + BridgeLockAction { + to: bridge_address, + amount: 100, + asset: nria().into(), + fee_asset: nria().into(), + destination_chain_address: "nootwashere".to_string(), + } + .into(), + BridgeUnlockAction { + to: bob_address, + amount: 10, + fee_asset: nria().into(), + memo: String::new(), + bridge_address: astria_address(&bridge.address_bytes()), + rollup_block_number: 1, + rollup_withdrawal_event_id: "a-rollup-defined-hash".to_string(), + } + .into(), + Ics20Withdrawal { + amount: 1, + denom: nria().into(), + bridge_address: None, + destination_chain_address: "test".to_string(), + return_address: bob_address, + timeout_height: Height::new(1, 1).unwrap(), + timeout_time: 1, + source_channel: "channel-0".to_string().parse().unwrap(), + fee_asset: nria().into(), + memo: String::new(), + use_compat_address: false, + } + .into(), + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .is_ok(), + "should be able to construct general bundle" + ); + + assert!( + UnsignedTransaction::builder() + .actions(vec![ + IbcRelayerChangeAction::Addition(bob_address).into(), + FeeAssetChangeAction::Removal("test-0".parse().unwrap()).into(), + FeeChangeAction { + fee_change: FeeChange::TransferBaseFee, + new_value: 10, + } + .into(), + ]) + .params( + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .build() + .is_ok(), + "should be able to construct sudo bundle" + ); + + assert!( + UnsignedTransaction::builder() + .actions(vec![ + SudoAddressChangeAction { + new_address: bob_address, + } + .into(), + SudoAddressChangeAction { + new_address: bob_address, + } + .into(), + ]) + .params( + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .build() + .unwrap_err() + .to_string() + .contains("failed to construct valid `Actions`") + ); + + assert!( + UnsignedTransaction::builder() + .actions(vec![ + InitBridgeAccountAction { + rollup_id, + asset: nria().into(), + fee_asset: nria().into(), + sudo_address: None, + withdrawer_address: None, + } + .into(), + InitBridgeAccountAction { + rollup_id, + asset: nria().into(), + fee_asset: nria().into(), + sudo_address: None, + withdrawer_address: None, + } + .into(), + ]) + .params( + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .build() + .unwrap_err() + .to_string() + .contains("failed to construct valid `Actions`") + ); + + assert!( + UnsignedTransaction::builder() + .actions(vec![ + BridgeSudoChangeAction { + bridge_address, + new_sudo_address: Some(bob_address), + new_withdrawer_address: Some(bob_address), + fee_asset: nria().into(), + } + .into(), + BridgeSudoChangeAction { + bridge_address, + new_sudo_address: Some(bob_address), + new_withdrawer_address: Some(bob_address), + fee_asset: nria().into(), + } + .into(), + ]) + .params( + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .build() + .unwrap_err() + .to_string() + .contains("failed to construct valid `Actions`") + ); +} diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index e2cd67b1e4..f6498eecaf 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -103,12 +103,8 @@ async fn app_execute_transaction_transfer() { let alice_address = astria_address(&alice.address_bytes()); let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let value = 333_333; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: value, @@ -116,8 +112,15 @@ async fn app_execute_transaction_transfer() { fee_asset: crate::test_utils::nria().into(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -160,12 +163,8 @@ async fn app_execute_transaction_transfer_not_native_token() { // transfer funds from Alice to Bob; use native token for fee payment let bob_address = astria_address_from_hex_string(BOB_ADDRESS); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: value, @@ -173,8 +172,15 @@ async fn app_execute_transaction_transfer_not_native_token() { fee_asset: nria().into(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -225,12 +231,8 @@ async fn app_execute_transaction_transfer_balance_too_low_for_fee() { let bob = astria_address_from_hex_string(BOB_ADDRESS); // 0-value transfer; only fee is deducted from sender - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob, amount: 0, @@ -238,8 +240,15 @@ async fn app_execute_transaction_transfer_balance_too_low_for_fee() { fee_asset: nria().into(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&keypair)); let res = app @@ -266,20 +275,23 @@ async fn app_execute_transaction_sequence() { let data = Bytes::from_static(b"hello world"); let fee = calculate_fee_from_state(&data, &app.state).await.unwrap(); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -301,20 +313,23 @@ async fn app_execute_transaction_invalid_fee_asset() { let alice = get_alice_signing_key(); let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: test_asset(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -332,13 +347,16 @@ async fn app_execute_transaction_validator_update() { verification_key: crate::test_utils::verification_key(1), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::ValidatorUpdate(update.clone())], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![Action::ValidatorUpdate(update.clone())]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -359,13 +377,18 @@ async fn app_execute_transaction_ibc_relayer_change_addition() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![IbcRelayerChangeAction::Addition(alice_address).into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![Action::IbcRelayerChange( + IbcRelayerChangeAction::Addition(alice_address), + )]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -387,14 +410,16 @@ async fn app_execute_transaction_ibc_relayer_change_deletion() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![IbcRelayerChangeAction::Removal(alice_address).into()], - }; - + let tx = UnsignedTransaction::builder() + .actions(vec![IbcRelayerChangeAction::Removal(alice_address).into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); assert_eq!(app.state.get_account_nonce(alice_address).await.unwrap(), 1); @@ -417,13 +442,16 @@ async fn app_execute_transaction_ibc_relayer_change_invalid() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![IbcRelayerChangeAction::Removal(alice_address).into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![IbcRelayerChangeAction::Removal(alice_address).into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -438,15 +466,18 @@ async fn app_execute_transaction_sudo_address_change() { let new_address = astria_address_from_hex_string(BOB_ADDRESS); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::SudoAddressChange(SudoAddressChangeAction { + let tx = UnsignedTransaction::builder() + .actions(vec![Action::SudoAddressChange(SudoAddressChangeAction { new_address, - })], - }; + })]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -476,15 +507,18 @@ async fn app_execute_transaction_sudo_address_change_error() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::SudoAddressChange(SudoAddressChangeAction { + let tx = UnsignedTransaction::builder() + .actions(vec![Action::SudoAddressChange(SudoAddressChangeAction { new_address: alice_address, - })], - }; + })]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let res = app @@ -505,15 +539,18 @@ async fn app_execute_transaction_fee_asset_change_addition() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::FeeAssetChange(FeeAssetChangeAction::Addition( - test_asset(), - ))], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![Action::FeeAssetChange( + FeeAssetChangeAction::Addition(test_asset()), + )]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -538,15 +575,18 @@ async fn app_execute_transaction_fee_asset_change_removal() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( + let tx = UnsignedTransaction::builder() + .actions(vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( test_asset(), - ))], - }; + ))]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -563,15 +603,18 @@ async fn app_execute_transaction_fee_asset_change_invalid() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( + let tx = UnsignedTransaction::builder() + .actions(vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( nria().into(), - ))], - }; + ))]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let res = app @@ -604,13 +647,17 @@ async fn app_execute_transaction_init_bridge_account_ok() { sudo_address: None, withdrawer_address: None, }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![action.into()], - }; + + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -660,14 +707,16 @@ async fn app_execute_transaction_init_bridge_account_account_already_registered( sudo_address: None, withdrawer_address: None, }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - - actions: vec![action.into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -679,13 +728,17 @@ async fn app_execute_transaction_init_bridge_account_account_already_registered( sudo_address: None, withdrawer_address: None, }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![action.into()], - }; + + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -716,13 +769,16 @@ async fn app_execute_transaction_bridge_lock_action_ok() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![action.into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -795,13 +851,16 @@ async fn app_execute_transaction_bridge_lock_action_invalid_for_eoa() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![action.into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -816,20 +875,24 @@ async fn app_execute_transaction_invalid_nonce() { // create tx with invalid nonce 1 let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - actions: vec![ + + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - }; + ]) + .params( + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let response = app.execute_transaction(signed_tx).await; @@ -863,21 +926,23 @@ async fn app_execute_transaction_invalid_chain_id() { // create tx with invalid nonce 1 let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("wrong-chain") - .build(), - actions: vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - }; - + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("wrong-chain") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let response = app.execute_transaction(signed_tx).await; @@ -920,12 +985,8 @@ async fn app_stateful_check_fails_insufficient_total_balance() { .unwrap(); // transfer just enough to cover single sequence fee with data - let signed_tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let signed_tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: keypair_address, amount: fee, @@ -933,20 +994,21 @@ async fn app_stateful_check_fails_insufficient_total_balance() { fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&alice); - - // make transfer + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap() + .into_signed(&alice); app.execute_transaction(Arc::new(signed_tx)).await.unwrap(); // build double transfer exceeding balance - let signed_tx_fail = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let signed_tx_fail = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data: data.clone(), @@ -959,10 +1021,16 @@ async fn app_stateful_check_fails_insufficient_total_balance() { fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&keypair); - + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap() + .into_signed(&keypair); // try double, see fails stateful check let res = signed_tx_fail .check_and_execute(Arc::get_mut(&mut app.state).unwrap()) @@ -973,21 +1041,24 @@ async fn app_stateful_check_fails_insufficient_total_balance() { assert!(res.contains("insufficient funds for asset")); // build single transfer to see passes - let signed_tx_pass = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + let signed_tx_pass = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - } - .into_signed(&keypair); + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap() + .into_signed(&keypair); signed_tx_pass .check_and_execute(Arc::get_mut(&mut app.state).unwrap()) @@ -1032,13 +1103,16 @@ async fn app_execute_transaction_bridge_lock_unlock_action_ok() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![action.into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -1056,13 +1130,16 @@ async fn app_execute_transaction_bridge_lock_unlock_action_ok() { rollup_withdrawal_event_id: "id-from-rollup".to_string(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![action.into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&bridge)); app.execute_transaction(signed_tx) @@ -1103,13 +1180,17 @@ async fn app_execute_transaction_action_index_correctly_increments() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![action.clone().into(), action.into()], - }; + + let tx = UnsignedTransaction::builder() + .actions(vec![action.clone().into(), action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); @@ -1139,12 +1220,9 @@ async fn transaction_execution_records_deposit_event() { state_tx .put_bridge_account_ibc_asset(bob_address, nria()) .unwrap(); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + + let tx = UnsignedTransaction::builder() + .actions(vec![ BridgeLockAction { to: bob_address, amount: 1, @@ -1153,9 +1231,15 @@ async fn transaction_execution_records_deposit_event() { destination_chain_address: "test_chain_address".to_string(), } .into(), - ], - }; - + ]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let expected_deposit = Deposit { diff --git a/crates/astria-sequencer/src/benchmark_utils.rs b/crates/astria-sequencer/src/benchmark_utils.rs index 2e5cfe8225..4508e91017 100644 --- a/crates/astria-sequencer/src/benchmark_utils.rs +++ b/crates/astria-sequencer/src/benchmark_utils.rs @@ -94,11 +94,12 @@ fn sequence_actions() -> Vec> { data: vec![2; 1000].into(), fee_asset: Denom::IbcPrefixed(IbcPrefixed::new([3; 32])), }; - let tx = UnsignedTransaction { - actions: vec![Action::Sequence(sequence_action)], - params, - } - .into_signed(signing_key); + let tx = UnsignedTransaction::builder() + .actions(vec![Action::Sequence(sequence_action)]) + .params(params) + .build() + .expect("failed to build transaction from actions") + .into_signed(signing_key); Arc::new(tx) }) .take(SEQUENCE_ACTION_TX_COUNT) @@ -121,13 +122,16 @@ fn transfers() -> Vec> { .nonce(u32::try_from(nonce).unwrap()) .chain_id("test") .build(); - let tx = UnsignedTransaction { - actions: std::iter::repeat(action.clone()) - .take(TRANSFERS_PER_TX) - .collect(), - params, - } - .into_signed(sender); + let tx = UnsignedTransaction::builder() + .actions( + std::iter::repeat(action.clone()) + .take(TRANSFERS_PER_TX) + .collect(), + ) + .params(params) + .build() + .expect("failed to build transaction from actions") + .into_signed(sender); Arc::new(tx) }) .collect() diff --git a/crates/astria-sequencer/src/proposal/commitment.rs b/crates/astria-sequencer/src/proposal/commitment.rs index 1578b47208..f07868e7a1 100644 --- a/crates/astria-sequencer/src/proposal/commitment.rs +++ b/crates/astria-sequencer/src/proposal/commitment.rs @@ -121,13 +121,17 @@ mod test { }; let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - actions: vec![sequence_action.clone().into(), transfer_action.into()], - }; + + let tx = UnsignedTransaction::builder() + .actions(vec![sequence_action.clone().into(), transfer_action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test-chain-1") + .build(), + ) + .build() + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; @@ -137,13 +141,16 @@ mod test { } = generate_rollup_datas_commitment(&txs, HashMap::new()); let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - actions: vec![sequence_action.into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![sequence_action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test-chain-1") + .build(), + ) + .build() + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; @@ -175,13 +182,16 @@ mod test { }; let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - actions: vec![sequence_action.into(), transfer_action.into()], - }; + let tx = UnsignedTransaction::builder() + .actions(vec![sequence_action.clone().into(), transfer_action.into()]) + .params( + TransactionParams::builder() + .nonce(0) + .chain_id("test-chain-1") + .build(), + ) + .build() + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 4fd31287cb..3f7437d980 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -237,20 +237,23 @@ mod test { }; fn make_unsigned_tx() -> UnsignedTransaction { - UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![ + UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data: Bytes::from_static(b"hello world"), fee_asset: crate::test_utils::nria().into(), } .into(), - ], - } + ]) + .params( + TransactionParams::builder() + .chain_id("test") + .nonce(0) + .build(), + ) + .build() + .unwrap() } fn new_prepare_proposal_request() -> request::PrepareProposal { diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index f7f7985c3a..bd4f2dd70e 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -91,7 +91,7 @@ pub(crate) async fn get_fees_for_transaction( .wrap_err("failed to get bridge sudo change fee")?; let mut fees_by_asset = HashMap::new(); - for (i, action) in tx.actions.iter().enumerate() { + for (i, action) in tx.actions().iter().enumerate() { match action { Action::Transfer(act) => { transfer_update_fees(&act.fee_asset, &mut fees_by_asset, transfer_fee); @@ -405,10 +405,11 @@ mod tests { .nonce(0) .chain_id("test-chain-id") .build(); - let tx = UnsignedTransaction { - actions, - params, - }; + let tx = UnsignedTransaction::builder() + .actions(actions) + .params(params) + .build() + .unwrap(); let signed_tx = tx.into_signed(&alice); check_balance_for_total_fees_and_transfers(&signed_tx, &state_tx) @@ -471,10 +472,11 @@ mod tests { .nonce(0) .chain_id("test-chain-id") .build(); - let tx = UnsignedTransaction { - actions, - params, - }; + let tx = UnsignedTransaction::builder() + .actions(actions) + .params(params) + .build() + .unwrap(); let signed_tx = tx.into_signed(&alice); let err = check_balance_for_total_fees_and_transfers(&signed_tx, &state_tx) From ed325e821793b2d6b76737dc0ced6781379e828a Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Wed, 18 Sep 2024 11:13:39 -0400 Subject: [PATCH 02/18] fix comment --- .../src/protocol/transaction/v1alpha1/action_groups.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs index fbf963c297..24ea36af8a 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs @@ -114,9 +114,8 @@ impl BelongsToGroup for Action { Action::BridgeSudoChange(act) => act.belongs_to_group(), Action::FeeChange(act) => act.belongs_to_group(), Action::FeeAssetChange(act) => act.belongs_to_group(), - Action::Ibc(_) => BundlableGeneral.into(), /* Can't use implement on act - * directly since it lives in a externa - * crate */ + Action::Ibc(_) => BundlableGeneral.into(), /* Can't implement on action directly + * since it lives in a external crate */ } } } From 5fbd0b79c39616df4aee381afb191193de13cfb4 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 23 Sep 2024 17:35:14 -0400 Subject: [PATCH 03/18] respond to review --- .../src/bridge_withdrawer/submitter/mod.rs | 18 +- .../astria-cli/src/commands/bridge/submit.rs | 20 +- crates/astria-cli/src/commands/sequencer.rs | 20 +- .../src/executor/bundle_factory/mod.rs | 9 + .../src/executor/bundle_factory/tests.rs | 25 + crates/astria-composer/src/executor/mod.rs | 17 +- crates/astria-core/src/protocol/test_utils.rs | 21 +- .../protocol/transaction/v1alpha1/action.rs | 32 + .../transaction/v1alpha1/action_group.rs | 505 ++++++++++++++++ .../transaction/v1alpha1/action_groups.rs | 228 ------- .../src/protocol/transaction/v1alpha1/mod.rs | 98 +-- .../astria-sequencer-client/src/tests/http.rs | 20 +- crates/astria-sequencer/src/app/mod.rs | 9 +- crates/astria-sequencer/src/app/test_utils.rs | 20 +- .../src/app/tests_app/mempool.rs | 132 ++-- .../astria-sequencer/src/app/tests_app/mod.rs | 148 +++-- .../src/app/tests_block_fees.rs | 110 ++-- .../src/app/tests_breaking_changes.rs | 260 ++++---- .../src/app/tests_execute_transaction.rs | 564 ++++++++---------- .../astria-sequencer/src/benchmark_utils.rs | 23 +- .../src/proposal/commitment.rs | 54 +- .../astria-sequencer/src/service/consensus.rs | 20 +- .../src/transaction/checks.rs | 12 +- 23 files changed, 1248 insertions(+), 1117 deletions(-) create mode 100644 crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs delete mode 100644 crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs index b4789b6535..64b9014bd0 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs @@ -154,16 +154,14 @@ impl Submitter { .wrap_err("failed to get nonce from sequencer")?; debug!(nonce, "fetched latest nonce"); - let unsigned = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(nonce) - .chain_id(sequencer_chain_id) - .build(), - ) - .build() - .wrap_err("failed to build unsigned transaction")?; + let unsigned = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(nonce) + .chain_id(sequencer_chain_id) + .build(), + ) + .wrap_err("failed to build unsigned transaction")?; // sign transaction let signed = unsigned.into_signed(signer.signing_key()); diff --git a/crates/astria-cli/src/commands/bridge/submit.rs b/crates/astria-cli/src/commands/bridge/submit.rs index c3a531894a..f238e8e32a 100644 --- a/crates/astria-cli/src/commands/bridge/submit.rs +++ b/crates/astria-cli/src/commands/bridge/submit.rs @@ -129,17 +129,15 @@ async fn submit_transaction( .await .wrap_err("failed to get nonce")?; - let tx = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(nonce_res.nonce) - .chain_id(chain_id) - .build(), - ) - .build() - .expect("failed to build transaction from actions") - .into_signed(signing_key); + let tx = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(nonce_res.nonce) + .chain_id(chain_id) + .build(), + ) + .wrap_err("failed to build transaction from actions")? + .into_signed(signing_key); let res = client .submit_transaction_sync(tx) .await diff --git a/crates/astria-cli/src/commands/sequencer.rs b/crates/astria-cli/src/commands/sequencer.rs index 75221d66c9..d68eb76d55 100644 --- a/crates/astria-cli/src/commands/sequencer.rs +++ b/crates/astria-cli/src/commands/sequencer.rs @@ -475,17 +475,15 @@ async fn submit_transaction( .await .wrap_err("failed to get nonce")?; - let tx = UnsignedTransaction::builder() - .actions(vec![action]) - .params( - TransactionParams::builder() - .nonce(nonce_res.nonce) - .chain_id(chain_id) - .build(), - ) - .build() - .expect("failed to build transaction from actions") - .into_signed(&sequencer_key); + let tx = UnsignedTransaction::new( + vec![action], + TransactionParams::builder() + .nonce(nonce_res.nonce) + .chain_id(chain_id) + .build(), + ) + .wrap_err("failed to build transaction from actions")? + .into_signed(&sequencer_key); let res = sequencer_client .submit_transaction_sync(tx) .await diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index 58a3e5a9b2..75b197035e 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -13,6 +13,8 @@ use astria_core::{ protocol::transaction::v1alpha1::{ action::SequenceAction, Action, + TransactionParams, + UnsignedTransaction, }, Protobuf as _, }; @@ -74,6 +76,12 @@ impl SizedBundle { } } + #[allow(clippy::allow_panic)] // method is expected to never panic as an invariant of the type + /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. + pub(super) fn to_unsigned_transaction(&self, params: TransactionParams) -> UnsignedTransaction { + UnsignedTransaction::new(self.buffer.clone(), params).unwrap() + } + /// Buffer `seq_action` into the bundle. /// # Errors /// - `seq_action` is beyond the max size allowed for the entire bundle @@ -112,6 +120,7 @@ impl SizedBundle { } /// Consume self and return the underlying buffer of actions. + #[cfg(test)] pub(super) fn into_actions(self) -> Vec { self.buffer } diff --git a/crates/astria-composer/src/executor/bundle_factory/tests.rs b/crates/astria-composer/src/executor/bundle_factory/tests.rs index dd8af22cf7..e70742bbd5 100644 --- a/crates/astria-composer/src/executor/bundle_factory/tests.rs +++ b/crates/astria-composer/src/executor/bundle_factory/tests.rs @@ -83,6 +83,8 @@ mod sized_bundle { #[cfg(test)] mod bundle_factory { + use astria_core::protocol::transaction::v1alpha1::TransactionParams; + use super::*; use crate::{ executor::bundle_factory::{ @@ -333,4 +335,27 @@ mod bundle_factory { assert_eq!(bundle_factory.finished.len(), 0); assert!(!bundle_factory.is_full()); } + + #[test] + fn unsigned_transaction_construction() { + let mut bundle_factory = BundleFactory::new(1000, 10); + + bundle_factory + .try_push(sequence_action_with_n_bytes(50)) + .unwrap(); + bundle_factory + .try_push(sequence_action_with_n_bytes(50)) + .unwrap(); + + let bundle = bundle_factory.pop_now(); + + let transaction_params = TransactionParams::builder() + .chain_id("astria-testnet-1".to_string()) + .build(); + + // construction of multiple sequence actions should not panic + let unsigned_tx = bundle.to_unsigned_transaction(transaction_params); + + assert_eq!(unsigned_tx.actions().len(), 2); + } } diff --git a/crates/astria-composer/src/executor/mod.rs b/crates/astria-composer/src/executor/mod.rs index 19aa778050..ab35740420 100644 --- a/crates/astria-composer/src/executor/mod.rs +++ b/crates/astria-composer/src/executor/mod.rs @@ -18,7 +18,6 @@ use astria_core::{ action::SequenceAction, SignedTransaction, TransactionParams, - UnsignedTransaction, }, }, }; @@ -681,11 +680,9 @@ impl Future for SubmitFut { .nonce(*this.nonce) .chain_id(&*this.chain_id) .build(); - let tx = UnsignedTransaction::builder() - .actions(this.bundle.clone().into_actions()) - .params(params) - .build() - .expect("failed to build transaction from actions") + let tx = this + .bundle + .to_unsigned_transaction(params) .into_signed(this.signing_key); info!( nonce.actual = *this.nonce, @@ -760,11 +757,9 @@ impl Future for SubmitFut { .nonce(*this.nonce) .chain_id(&*this.chain_id) .build(); - let tx = UnsignedTransaction::builder() - .actions(this.bundle.clone().into_actions()) - .params(params) - .build() - .expect("failed to build transaction from actions") + let tx = this + .bundle + .to_unsigned_transaction(params) .into_signed(this.signing_key); info!( nonce.resubmission = *this.nonce, diff --git a/crates/astria-core/src/protocol/test_utils.rs b/crates/astria-core/src/protocol/test_utils.rs index f4cfe2988b..50c1f2f21b 100644 --- a/crates/astria-core/src/protocol/test_utils.rs +++ b/crates/astria-core/src/protocol/test_utils.rs @@ -104,17 +104,16 @@ impl ConfigureSequencerBlock { let txs = if actions.is_empty() { vec![] } else { - let unsigned_transaction = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(1) - .chain_id(chain_id.clone()) - .build(), - ) - .build() - .expect("failed to build unsigned transaction"); - + let unsigned_transaction = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(1) + .chain_id(chain_id.clone()) + .build(), + ) + .expect( + "failed to build unsigned transaction, should be okay since only sequence actions", + ); vec![unsigned_transaction.into_signed(&signing_key)] }; let mut deposits_map: HashMap> = HashMap::new(); diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs index 91c747a2f5..7242282f73 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs @@ -164,6 +164,14 @@ impl Action { }; Some(transfer_action) } + + pub fn is_fee_asset_change(&self) -> bool { + matches!(self, Self::FeeAssetChange(_)) + } + + pub fn is_fee_change(&self) -> bool { + matches!(self, Self::FeeChange(_)) + } } impl From for Action { @@ -252,6 +260,30 @@ impl TryFrom for Action { } } +pub(super) trait ActionName { + fn name(&self) -> &'static str; +} + +impl ActionName for Action { + fn name(&self) -> &'static str { + match self { + Action::Sequence(_) => "Sequence", + Action::Transfer(_) => "Transfer", + Action::ValidatorUpdate(_) => "ValidatorUpdate", + Action::SudoAddressChange(_) => "SudoAddressChange", + Action::Ibc(_) => "Ibc", + Action::Ics20Withdrawal(_) => "Ics20Withdrawal", + Action::IbcRelayerChange(_) => "IbcRelayerChange", + Action::FeeAssetChange(_) => "FeeAssetChange", + Action::InitBridgeAccount(_) => "InitBridgeAccount", + Action::BridgeLock(_) => "BridgeLock", + Action::BridgeUnlock(_) => "BridgeUnlock", + Action::BridgeSudoChange(_) => "BridgeSudoChange", + Action::FeeChange(_) => "FeeChange", + } + } +} + #[allow(clippy::module_name_repetitions)] #[derive(Debug, thiserror::Error)] #[error(transparent)] diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs new file mode 100644 index 0000000000..878901a064 --- /dev/null +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs @@ -0,0 +1,505 @@ +use std::fmt::{ + self, + Debug, +}; + +use penumbra_ibc::IbcRelay; + +use super::{ + action::{ + ActionName, + BridgeLockAction, + BridgeSudoChangeAction, + BridgeUnlockAction, + FeeAssetChangeAction, + FeeChangeAction, + IbcRelayerChangeAction, + Ics20Withdrawal, + InitBridgeAccountAction, + SequenceAction, + SudoAddressChangeAction, + TransferAction, + ValidatorUpdate, + }, + Action, +}; + +trait BelongsToGroup { + const GROUP: ActionGroup; +} + +macro_rules! impl_belong_to_group { + ($(($act:ty, $group:expr)),*$(,)?) => { + $( + impl BelongsToGroup for $act { + const GROUP: ActionGroup = $group; + } + )* + } +} + +impl_belong_to_group!( + ( + SequenceAction, + ActionGroup::BundlableGeneral(BundlableGeneral) + ), + ( + TransferAction, + ActionGroup::BundlableGeneral(BundlableGeneral) + ), + ( + ValidatorUpdate, + ActionGroup::BundlableGeneral(BundlableGeneral) + ), + (SudoAddressChangeAction, ActionGroup::Sudo(Sudo)), + ( + IbcRelayerChangeAction, + ActionGroup::BundlableSudo(BundlableSudo) + ), + ( + Ics20Withdrawal, + ActionGroup::BundlableGeneral(BundlableGeneral) + ), + (InitBridgeAccountAction, ActionGroup::General(General)), + ( + BridgeLockAction, + ActionGroup::BundlableGeneral(BundlableGeneral) + ), + ( + BridgeUnlockAction, + ActionGroup::BundlableGeneral(BundlableGeneral) + ), + (BridgeSudoChangeAction, ActionGroup::General(General)), + (FeeChangeAction, ActionGroup::BundlableSudo(BundlableSudo)), + ( + FeeAssetChangeAction, + ActionGroup::BundlableSudo(BundlableSudo) + ), + (IbcRelay, ActionGroup::BundlableGeneral(BundlableGeneral)), +); + +pub trait Group { + fn group(&self) -> ActionGroup; +} + +impl Group for Action { + fn group(&self) -> ActionGroup { + match self { + Action::Sequence(_) => SequenceAction::GROUP, + Action::Transfer(_) => TransferAction::GROUP, + Action::ValidatorUpdate(_) => ValidatorUpdate::GROUP, + Action::SudoAddressChange(_) => SudoAddressChangeAction::GROUP, + Action::IbcRelayerChange(_) => IbcRelayerChangeAction::GROUP, + Action::Ics20Withdrawal(_) => Ics20Withdrawal::GROUP, + Action::InitBridgeAccount(_) => InitBridgeAccountAction::GROUP, + Action::BridgeLock(_) => BridgeLockAction::GROUP, + Action::BridgeUnlock(_) => BridgeUnlockAction::GROUP, + Action::BridgeSudoChange(_) => BridgeSudoChangeAction::GROUP, + Action::FeeChange(_) => FeeChangeAction::GROUP, + Action::FeeAssetChange(_) => FeeAssetChangeAction::GROUP, + Action::Ibc(_) => IbcRelay::GROUP, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct BundlableGeneral; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct General; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct BundlableSudo; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Sudo; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ActionGroup { + BundlableGeneral(BundlableGeneral), + General(General), + BundlableSudo(BundlableSudo), + Sudo(Sudo), +} + +impl fmt::Display for ActionGroup { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ActionGroup::BundlableGeneral(_) => write!(f, "BundlableGeneral"), + ActionGroup::General(_) => write!(f, "General"), + ActionGroup::BundlableSudo(_) => write!(f, "BundlableSudo"), + ActionGroup::Sudo(_) => write!(f, "Sudo"), + } + } +} + +impl From for ActionGroup { + fn from(val: BundlableGeneral) -> ActionGroup { + ActionGroup::BundlableGeneral(val) + } +} + +impl From for ActionGroup { + fn from(val: General) -> ActionGroup { + ActionGroup::General(val) + } +} + +impl From for ActionGroup { + fn from(val: BundlableSudo) -> ActionGroup { + ActionGroup::BundlableSudo(val) + } +} + +impl From for ActionGroup { + fn from(val: Sudo) -> ActionGroup { + ActionGroup::Sudo(val) + } +} + +#[derive(Debug, thiserror::Error)] +enum ErrorKind { + #[error("input contains mixed action types")] + Mixed, + #[error("attempted to create bundle with non bundleable `ActionGroup` type")] + NotBundleable, +} + +#[derive(Debug)] +pub struct Error { + kind: ErrorKind, + context: Option, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.kind)?; + if let Some(ctx) = &self.context { + write!(f, ": {ctx}")?; + } + Ok(()) + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.kind) + } +} + +impl Error { + fn new(kind: ErrorKind, context: Option) -> Self { + Self { + kind, + context, + } + } + + #[must_use] + pub fn mixed( + original_group: ActionGroup, + additional_group: ActionGroup, + action: &Action, + ) -> Self { + let context = format!( + "Mixed actions of different types. Original group: '{original_group}', Additional \ + group: '{additional_group}', triggering action: '{}'", + action.name() + ); + Self::new(ErrorKind::Mixed, Some(context)) + } + + #[must_use] + pub fn not_bundlable(group: ActionGroup) -> Self { + let context = format!("ActionGroup type '{group}' is not bundleable"); + Self::new(ErrorKind::NotBundleable, Some(context)) + } +} + +/// Invariants: `group` is set if `inner` is not empty. +#[derive(Clone, Debug)] +pub(super) struct Actions { + inner: Vec, +} + +impl Actions { + pub(super) fn actions(&self) -> &[Action] { + &self.inner + } + + #[must_use] + pub(super) fn into_actions(self) -> Vec { + self.inner + } + + pub(super) fn group(&self) -> Option { + self.inner.first().map(Group::group) + } + + pub(super) fn default() -> Self { + Self { + inner: vec![], + } + } + + pub(super) fn from_list_of_actions(actions: Vec) -> Result { + let mut actions_iter = actions.iter(); + let group = match actions_iter.next() { + Some(action) => action.group(), + None => { + // empty `actions`, so invariants met + return Ok(Self::default()); + } + }; + + // assert size constraints on non-bundlable action groups + if matches!(group, ActionGroup::General(_) | ActionGroup::Sudo(_)) && actions.len() > 1 { + return Err(Error::not_bundlable(group)); + } + + // assert the rest of the actions have the same group as the first + for action in actions_iter { + if action.group() != group { + return Err(Error::mixed(group, action.group(), action)); + } + } + + Ok(Self { + inner: actions, + }) + } +} + +#[cfg(test)] +mod test { + use ibc_types::core::client::Height; + + use super::*; + use crate::{ + crypto::VerificationKey, + primitive::v1::{ + asset::Denom, + Address, + RollupId, + }, + protocol::transaction::v1alpha1::action::{ + FeeChange, + TransferAction, + }, + }; + const ASTRIA_ADDRESS_PREFIX: &str = "astria"; + + #[test] + fn from_list_of_actions_bundlable_general() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + let actions = vec![ + Action::Sequence(SequenceAction { + rollup_id: RollupId::from([8; 32]), + data: vec![].into(), + fee_asset: asset.clone(), + }) + .into(), + Action::Transfer(TransferAction { + to: address, + amount: 100, + asset: asset.clone(), + fee_asset: asset.clone(), + }) + .into(), + Action::BridgeLock(BridgeLockAction { + to: address, + amount: 100, + asset: asset.clone(), + fee_asset: asset.clone(), + destination_chain_address: "".to_string(), + }) + .into(), + Action::BridgeUnlock(BridgeUnlockAction { + to: address, + amount: 100, + fee_asset: asset.clone(), + bridge_address: address, + memo: "".to_string(), + rollup_block_number: 0, + rollup_withdrawal_event_id: "".to_string(), + }) + .into(), + Action::ValidatorUpdate(ValidatorUpdate { + power: 100, + verification_key: VerificationKey::try_from([0; 32]).unwrap(), + }) + .into(), + Action::Ics20Withdrawal(Ics20Withdrawal { + denom: asset.clone(), + destination_chain_address: "test".to_string(), + return_address: address, + amount: 1_000_000u128, + memo: "test".to_string(), + fee_asset: asset.clone(), + timeout_height: Height::new(1, 1).unwrap(), + timeout_time: 0, + source_channel: "channel-0".parse().unwrap(), + bridge_address: Some(address), + use_compat_address: false, + }) + .into(), + ]; + + assert!(matches!( + Actions::from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::BundlableGeneral(_)) + )); + } + + #[test] + fn from_list_of_actions_bundlable_sudo() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + let actions = vec![ + Action::FeeChange(FeeChangeAction { + fee_change: FeeChange::TransferBaseFee, + new_value: 100, + }) + .into(), + Action::FeeAssetChange(FeeAssetChangeAction::Addition(asset).into()).into(), + Action::IbcRelayerChange(IbcRelayerChangeAction::Addition(address)).into(), + ]; + + assert!(matches!( + Actions::from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::BundlableSudo(_)) + )); + } + + #[test] + fn from_list_of_actions_sudo() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let actions = vec![ + Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + }) + .into(), + ]; + + assert!(matches!( + Actions::from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::Sudo(_)) + )); + + let actions = vec![ + Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + }) + .into(), + Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + }) + .into(), + ]; + + assert_eq!( + Actions::from_list_of_actions(actions) + .unwrap_err() + .to_string(), + "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ + 'Sudo' is not bundleable" + ); + } + + #[test] + fn from_list_of_actions_general() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + + let init_bridge_account_action = InitBridgeAccountAction { + rollup_id: RollupId::from([8; 32]), + asset: asset.clone(), + fee_asset: asset.clone(), + sudo_address: Some(address), + withdrawer_address: Some(address), + }; + + let sudo_bridge_address_change_action = BridgeSudoChangeAction { + new_sudo_address: Some(address), + bridge_address: address, + new_withdrawer_address: Some(address), + fee_asset: asset.clone(), + }; + + let actions = vec![init_bridge_account_action.clone().into()]; + + assert!(matches!( + Actions::from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::General(_)) + )); + + let actions = vec![sudo_bridge_address_change_action.clone().into()]; + + assert!(matches!( + Actions::from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::General(_)) + )); + + let actions = vec![ + init_bridge_account_action.into(), + sudo_bridge_address_change_action.into(), + ]; + + assert_eq!( + Actions::from_list_of_actions(actions) + .unwrap_err() + .to_string(), + "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ + 'General' is not bundleable" + ); + } + + #[test] + fn from_list_of_actions_mixed() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + let actions = vec![ + Action::Sequence(SequenceAction { + rollup_id: RollupId::from([8; 32]), + data: vec![].into(), + fee_asset: asset.clone(), + }) + .into(), + Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + }) + .into(), + ]; + + assert_eq!( + Actions::from_list_of_actions(actions) + .unwrap_err() + .to_string(), + "input contains mixed action types: Mixed actions of different types. Original group: \ + 'BundlableGeneral', Additional group: 'Sudo', triggering action: 'SudoAddressChange'" + ); + } +} diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs deleted file mode 100644 index 24ea36af8a..0000000000 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_groups.rs +++ /dev/null @@ -1,228 +0,0 @@ -use super::{ - action::{ - BridgeLockAction, - BridgeSudoChangeAction, - BridgeUnlockAction, - FeeAssetChangeAction, - FeeChangeAction, - IbcRelayerChangeAction, - Ics20Withdrawal, - InitBridgeAccountAction, - SequenceAction, - SudoAddressChangeAction, - TransferAction, - ValidatorUpdate, - }, - Action, -}; -trait Sealed {} - -trait BelongsToGroup: Sealed { - fn belongs_to_group(&self) -> ActionGroup; -} - -macro_rules! impl_belong_to_group { - ($($act:ty),*$(,)?) => { - $( - impl Sealed for $act {} - - impl BelongsToGroup for $act { - fn belongs_to_group(&self) -> ActionGroup { - Self::ACTION_GROUP.into() - } - } - )* - } -} - -impl SequenceAction { - const ACTION_GROUP: BundlableGeneral = BundlableGeneral; -} - -impl TransferAction { - const ACTION_GROUP: BundlableGeneral = BundlableGeneral; -} - -impl ValidatorUpdate { - const ACTION_GROUP: BundlableGeneral = BundlableGeneral; -} - -impl SudoAddressChangeAction { - const ACTION_GROUP: Sudo = Sudo; -} - -impl IbcRelayerChangeAction { - const ACTION_GROUP: BundlableSudo = BundlableSudo; -} - -impl Ics20Withdrawal { - const ACTION_GROUP: BundlableGeneral = BundlableGeneral; -} - -impl FeeAssetChangeAction { - const ACTION_GROUP: BundlableSudo = BundlableSudo; -} - -impl InitBridgeAccountAction { - const ACTION_GROUP: General = General; -} - -impl BridgeLockAction { - const ACTION_GROUP: BundlableGeneral = BundlableGeneral; -} - -impl BridgeUnlockAction { - const ACTION_GROUP: BundlableGeneral = BundlableGeneral; -} - -impl BridgeSudoChangeAction { - const ACTION_GROUP: General = General; -} - -impl FeeChangeAction { - const ACTION_GROUP: BundlableSudo = BundlableSudo; -} - -impl_belong_to_group!( - SequenceAction, - TransferAction, - ValidatorUpdate, - SudoAddressChangeAction, - IbcRelayerChangeAction, - Ics20Withdrawal, - InitBridgeAccountAction, - BridgeLockAction, - BridgeUnlockAction, - BridgeSudoChangeAction, - FeeChangeAction, - FeeAssetChangeAction -); - -impl Sealed for Action {} -impl BelongsToGroup for Action { - fn belongs_to_group(&self) -> ActionGroup { - match self { - Action::Sequence(act) => act.belongs_to_group(), - Action::Transfer(act) => act.belongs_to_group(), - Action::ValidatorUpdate(act) => act.belongs_to_group(), - Action::SudoAddressChange(act) => act.belongs_to_group(), - Action::IbcRelayerChange(act) => act.belongs_to_group(), - Action::Ics20Withdrawal(act) => act.belongs_to_group(), - Action::InitBridgeAccount(act) => act.belongs_to_group(), - Action::BridgeLock(act) => act.belongs_to_group(), - Action::BridgeUnlock(act) => act.belongs_to_group(), - Action::BridgeSudoChange(act) => act.belongs_to_group(), - Action::FeeChange(act) => act.belongs_to_group(), - Action::FeeAssetChange(act) => act.belongs_to_group(), - Action::Ibc(_) => BundlableGeneral.into(), /* Can't implement on action directly - * since it lives in a external crate */ - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BundlableGeneral; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct General; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BundlableSudo; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Sudo; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ActionGroup { - BundlableGeneral(BundlableGeneral), - General(General), - BundlableSudo(BundlableSudo), - Sudo(Sudo), -} - -impl From for ActionGroup { - fn from(val: BundlableGeneral) -> ActionGroup { - ActionGroup::BundlableGeneral(val) - } -} - -impl From for ActionGroup { - fn from(val: General) -> ActionGroup { - ActionGroup::General(val) - } -} - -impl From for ActionGroup { - fn from(val: BundlableSudo) -> ActionGroup { - ActionGroup::BundlableSudo(val) - } -} - -impl From for ActionGroup { - fn from(val: Sudo) -> ActionGroup { - ActionGroup::Sudo(val) - } -} - -#[derive(Debug, thiserror::Error)] -enum ActionGroupErrorKind { - #[error("input contains mixed action types")] - Mixed, - #[error("input attempted to bundle non bundleable action type")] - NotBundleable, -} - -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct ActionGroupError(ActionGroupErrorKind); -impl ActionGroupError { - fn mixed() -> Self { - Self(ActionGroupErrorKind::Mixed) - } - - fn not_bundlable() -> Self { - Self(ActionGroupErrorKind::NotBundleable) - } -} - -/// Invariants: `group` is set if `inner` is not empty. -#[derive(Clone, Debug)] -pub(super) struct Actions { - group: Option, - inner: Vec, -} - -impl Actions { - pub(super) fn actions(&self) -> &[Action] { - &self.inner - } - - #[must_use] - pub(super) fn into_actions(self) -> Vec { - self.inner - } - - pub(super) fn group(&self) -> &Option { - &self.group - } - - pub(super) fn from_list_of_actions(actions: Vec) -> Result { - let mut group = None; - for action in &actions { - if group.is_none() { - group = Some(action.belongs_to_group()); - } else if group != Some(action.belongs_to_group()) { - return Err(ActionGroupError::mixed()); - } - } - - // assert size constraints on non-bundlable action groups - if (Some(ActionGroup::General(General)) == group || Some(ActionGroup::Sudo(Sudo)) == group) - && actions.len() > 1 - { - return Err(ActionGroupError::not_bundlable()); - } - Ok(Self { - group, - inner: actions, - }) - } -} diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index f1b569dca9..c031d695ab 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -1,4 +1,4 @@ -use action_groups::{ +use action_group::{ ActionGroup, Actions, }; @@ -25,7 +25,7 @@ use crate::{ }; pub mod action; -pub mod action_groups; +pub mod action_group; pub use action::Action; #[derive(Debug, thiserror::Error)] @@ -207,7 +207,7 @@ impl SignedTransaction { } #[must_use] - pub fn group(&self) -> &Option { + pub fn group(&self) -> Option { self.transaction.actions.group() } @@ -236,74 +236,29 @@ impl SignedTransaction { } } -pub struct UnsignedTransactionBuilder { - actions: Vec, +#[derive(Clone, Debug)] +#[allow(clippy::module_name_repetitions)] +pub struct UnsignedTransaction { + actions: Actions, params: TransactionParams, } -impl UnsignedTransactionBuilder { - fn new() -> Self { - Self { - actions: Vec::new(), - params: TransactionParams::from_raw(raw::TransactionParams { - nonce: 0, - chain_id: String::new(), - }), - } - } -} - -impl UnsignedTransactionBuilder { - #[must_use] - pub fn actions(self, actions: Vec) -> Self { - Self { - actions, - ..self - } - } - - #[must_use] - pub fn params(self, params: TransactionParams) -> Self { - Self { - params, - ..self - } - } - - /// Builds the `UnsignedTransaction` from the builder. +impl UnsignedTransaction { + /// Creates the `UnsignedTransaction` from the actions and params. /// /// # Errors /// /// This function will return an error if the actions list contains actions /// of different `ActionGroup` types or violated an `ActionGroup`'s bundling constraints. - pub fn build(self) -> Result { - let Self { - actions, - params, - } = self; - - let actions = Actions::from_list_of_actions(actions) - .map_err(UnsignedTransactionError::action_group)?; - - Ok(UnsignedTransaction { - actions, + pub fn new( + actions: Vec, + params: TransactionParams, + ) -> Result { + Ok(Self { + actions: Actions::from_list_of_actions(actions)?, params, }) } -} - -#[derive(Clone, Debug)] -#[allow(clippy::module_name_repetitions)] -pub struct UnsignedTransaction { - actions: Actions, - params: TransactionParams, -} - -impl UnsignedTransaction { - #[must_use] - pub fn builder() -> UnsignedTransactionBuilder { - UnsignedTransactionBuilder::new() - } #[must_use] pub fn into_actions(self) -> Vec { @@ -402,10 +357,7 @@ impl UnsignedTransaction { .collect::>() .map_err(UnsignedTransactionError::action)?; - UnsignedTransactionBuilder::new() - .actions(actions) - .params(params) - .build() + UnsignedTransaction::new(actions, params).map_err(UnsignedTransactionError::action_group) } /// Attempt to convert from a protobuf [`pbjson_types::Any`]. @@ -448,7 +400,7 @@ impl UnsignedTransactionError { Self(UnsignedTransactionErrorKind::DecodeAny(inner)) } - fn action_group(inner: action_groups::ActionGroupError) -> Self { + fn action_group(inner: action_group::Error) -> Self { Self(UnsignedTransactionErrorKind::ActionGroup(inner)) } } @@ -470,8 +422,8 @@ enum UnsignedTransactionErrorKind { raw::UnsignedTransaction::type_url() )] DecodeAny(#[source] prost::DecodeError), - #[error("failed to construct valid `Actions` from list of actions")] - ActionGroup(#[source] action_groups::ActionGroupError), + #[error("`actions` field does not make a valid `ActionGroup`")] + ActionGroup(#[source] action_group::Error), } pub struct TransactionParamsBuilder> { @@ -687,11 +639,7 @@ mod test { chain_id: "test-1".to_string(), }); - let unsigned = UnsignedTransaction::builder() - .actions(vec![transfer.into()]) - .params(params) - .build() - .expect("failed to build unsigned transaction"); + let unsigned = UnsignedTransaction::new(vec![transfer.into()], params).unwrap(); let tx = SignedTransaction { signature, @@ -726,11 +674,7 @@ mod test { chain_id: "test-1".to_string(), }); - let unsigned_tx = UnsignedTransaction::builder() - .actions(vec![transfer.into()]) - .params(params) - .build() - .expect("failed to build unsigned transaction"); + let unsigned_tx = UnsignedTransaction::new(vec![transfer.into()], params).unwrap(); let signed_tx = unsigned_tx.into_signed(&signing_key); let raw = signed_tx.to_raw(); diff --git a/crates/astria-sequencer-client/src/tests/http.rs b/crates/astria-sequencer-client/src/tests/http.rs index 7c0752ad22..ffa5490214 100644 --- a/crates/astria-sequencer-client/src/tests/http.rs +++ b/crates/astria-sequencer-client/src/tests/http.rs @@ -157,17 +157,15 @@ fn create_signed_transaction() -> SignedTransaction { } .into(), ]; - UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .chain_id("test") - .nonce(1) - .build(), - ) - .build() - .unwrap() - .into_signed(&alice_key) + UnsignedTransaction::new( + actions, + TransactionParams::builder() + .chain_id("test") + .nonce(1) + .build(), + ) + .unwrap() + .into_signed(&alice_key) } #[tokio::test] diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 2513a9a8f8..99f57694fd 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -25,7 +25,7 @@ use astria_core::{ genesis::v1alpha1::GenesisAppState, transaction::v1alpha1::{ action::ValidatorUpdate, - action_groups::ActionGroup, + action_group::ActionGroup, Action, SignedTransaction, }, @@ -1026,9 +1026,10 @@ impl App { // flag mempool for cleaning if we ran a fee change action self.recost_mempool = self.recost_mempool || matches!(signed_tx.group(), Some(ActionGroup::BundlableSudo(_))) - && signed_tx.actions().iter().any(|action| { - matches!(action, Action::FeeAssetChange(_) | Action::FeeChange(_)) - }); + && signed_tx + .actions() + .iter() + .any(|act| act.is_fee_asset_change() || act.is_fee_change()); Ok(state_tx.apply().1) } diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 678a9090e7..9f7d1c47b9 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -223,23 +223,21 @@ pub(crate) fn mock_tx( signer: &SigningKey, rollup_name: &str, ) -> Arc { - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(rollup_name.as_bytes()), data: Bytes::from_static(&[0x99]), fee_asset: denom_0(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(nonce) - .chain_id("test") - .build(), - ) - .build() - .expect("Failed to build unsigned transaction"); + ], + TransactionParams::builder() + .nonce(nonce) + .chain_id("test") + .build(), + ) + .expect("Failed to build unsigned transaction"); Arc::new(tx.into_signed(signer)) } diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index 807451cd6d..2775efb83d 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -50,23 +50,21 @@ async fn trigger_cleaning() { app.commit(storage.clone()).await; // create tx which will cause mempool cleaning flag to be set - let tx_trigger = UnsignedTransaction::builder() - .actions(vec![ + let tx_trigger = UnsignedTransaction::new( + vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&get_judy_signing_key()); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&get_judy_signing_key()); app.mempool .insert( @@ -151,23 +149,21 @@ async fn do_not_trigger_cleaning() { // create tx which will fail execution and not trigger flag // (wrong sudo signer) - let tx_fail = UnsignedTransaction::builder() - .actions(vec![ + let tx_fail = UnsignedTransaction::new( + vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&get_alice_signing_key()); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&get_alice_signing_key()); app.mempool .insert( @@ -227,8 +223,8 @@ async fn maintenance_recosting_promotes() { // create tx which will not be included in block due to // having insufficient funds (transaction will be recosted to enable) - let tx_fail_recost_funds = UnsignedTransaction::builder() - .actions(vec![ + let tx_fail_recost_funds = UnsignedTransaction::new( + vec![ TransferAction { to: astria_address_from_hex_string(CAROL_ADDRESS), amount: 1u128, @@ -236,16 +232,14 @@ async fn maintenance_recosting_promotes() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&get_bob_signing_key()); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&get_bob_signing_key()); let mut bob_funds = HashMap::new(); bob_funds.insert(nria().into(), 11); @@ -262,23 +256,21 @@ async fn maintenance_recosting_promotes() { .unwrap(); // create tx which will enable recost tx to pass - let tx_recost = UnsignedTransaction::builder() - .actions(vec![ + let tx_recost = UnsignedTransaction::new( + vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, // originally 12 } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&get_judy_signing_key()); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&get_judy_signing_key()); let mut judy_funds = HashMap::new(); judy_funds.insert(nria().into(), 0); @@ -406,8 +398,8 @@ async fn maintenance_funds_added_promotes() { // create tx that will not be included in block due to // having no funds (will be sent transfer to then enable) - let tx_fail_transfer_funds = UnsignedTransaction::builder() - .actions(vec![ + let tx_fail_transfer_funds = UnsignedTransaction::new( + vec![ TransferAction { to: astria_address_from_hex_string(BOB_ADDRESS), amount: 10u128, @@ -415,16 +407,14 @@ async fn maintenance_funds_added_promotes() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&get_carol_signing_key()); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&get_carol_signing_key()); let mut carol_funds = HashMap::new(); carol_funds.insert(nria().into(), 0); @@ -441,8 +431,8 @@ async fn maintenance_funds_added_promotes() { .unwrap(); // create tx which will enable no funds to pass - let tx_fund = UnsignedTransaction::builder() - .actions(vec![ + let tx_fund = UnsignedTransaction::new( + vec![ TransferAction { to: astria_address_from_hex_string(CAROL_ADDRESS), amount: 22u128, @@ -450,16 +440,14 @@ async fn maintenance_funds_added_promotes() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&get_alice_signing_key()); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&get_alice_signing_key()); let mut alice_funds = HashMap::new(); alice_funds.insert(nria().into(), 100); diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 3bae8d1acc..43ce190fe0 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -231,8 +231,8 @@ async fn app_transfer_block_fees_to_sudo() { // transfer funds from Alice to Bob; use native token for fee payment let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let amount = 333_333; - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ TransferAction { to: bob_address, amount, @@ -240,15 +240,13 @@ async fn app_transfer_block_fees_to_sudo() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction"); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -325,16 +323,14 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { fee_asset: nria().into(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![lock_action.into(), sequence_action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction"); + let tx = UnsignedTransaction::new( + vec![lock_action.into(), sequence_action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -422,16 +418,14 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { fee_asset: nria().into(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![lock_action.into(), sequence_action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction"); + let tx = UnsignedTransaction::new( + vec![lock_action.into(), sequence_action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -562,43 +556,39 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { // create txs which will cause cometBFT overflow let alice = get_alice_signing_key(); - let tx_pass = UnsignedTransaction::builder() - .actions(vec![ + let tx_pass = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&alice); - - let tx_overflow = UnsignedTransaction::builder() - .actions(vec![ + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&alice); + + let tx_overflow = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&alice); + ], + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&alice); app.mempool .insert( @@ -661,43 +651,39 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { // create txs which will cause sequencer overflow (max is currently 256_000 bytes) let alice = get_alice_signing_key(); - let tx_pass = UnsignedTransaction::builder() - .actions(vec![ + let tx_pass = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 200_000]), fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&alice); - - let tx_overflow = UnsignedTransaction::builder() - .actions(vec![ + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&alice); + + let tx_overflow = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .build() - .expect("failed to build unsigned transaction") - .into_signed(&alice); + ], + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction") + .into_signed(&alice); app.mempool .insert( diff --git a/crates/astria-sequencer/src/app/tests_block_fees.rs b/crates/astria-sequencer/src/app/tests_block_fees.rs index eb38fe8a71..cf40c0e7df 100644 --- a/crates/astria-sequencer/src/app/tests_block_fees.rs +++ b/crates/astria-sequencer/src/app/tests_block_fees.rs @@ -53,8 +53,8 @@ async fn transaction_execution_records_fee_event() { let alice = get_alice_signing_key(); let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let value = 333_333; - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ TransferAction { to: bob_address, amount: value, @@ -62,15 +62,13 @@ async fn transaction_execution_records_fee_event() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let events = app.execute_transaction(signed_tx).await.unwrap(); @@ -116,16 +114,14 @@ async fn ensure_correct_block_fees_transfer() { .into(), ]; - let tx = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -160,16 +156,14 @@ async fn ensure_correct_block_fees_sequence() { .into(), ]; - let tx = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -206,16 +200,14 @@ async fn ensure_correct_block_fees_init_bridge_acct() { .into(), ]; - let tx = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -263,16 +255,14 @@ async fn ensure_correct_block_fees_bridge_lock() { .into(), ]; - let tx = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .expect("failed to build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); @@ -328,16 +318,14 @@ async fn ensure_correct_block_fees_bridge_sudo_change() { .into(), ]; - let tx = UnsignedTransaction::builder() - .actions(actions) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + actions, + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 586496b35b..a38d9d42cc 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -116,16 +116,14 @@ async fn app_finalize_block_snapshot() { fee_asset: nria().into(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![lock_action.into(), sequence_action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![lock_action.into(), sequence_action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -209,8 +207,8 @@ async fn app_execute_transaction_with_every_action_snapshot() { let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); - let tx_bundlable_general = UnsignedTransaction::builder() - .actions(vec![ + let tx_bundlable_general = UnsignedTransaction::new( + vec![ TransferAction { to: bob_address, amount: 333_333, @@ -225,50 +223,43 @@ async fn app_execute_transaction_with_every_action_snapshot() { } .into(), Action::ValidatorUpdate(update.clone()), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); - let tx_bundlable_sudo = UnsignedTransaction::builder() - .actions(vec![ + let tx_bundlable_sudo = UnsignedTransaction::new( + vec![ IbcRelayerChangeAction::Addition(bob_address).into(), IbcRelayerChangeAction::Addition(carol_address).into(), IbcRelayerChangeAction::Removal(bob_address).into(), - // TODO: should fee assets be stored in state? FeeAssetChangeAction::Addition("test-0".parse().unwrap()).into(), FeeAssetChangeAction::Addition("test-1".parse().unwrap()).into(), FeeAssetChangeAction::Removal("test-0".parse().unwrap()).into(), - ]) - .params( - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .unwrap(); - let tx_sudo = UnsignedTransaction::builder() - .actions(vec![ + let tx_sudo = UnsignedTransaction::new( + vec![ SudoAddressChangeAction { new_address: bob_address, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx_general_bundlable = Arc::new(tx_bundlable_general.into_signed(&alice)); app.execute_transaction(signed_tx_general_bundlable) @@ -283,8 +274,8 @@ async fn app_execute_transaction_with_every_action_snapshot() { let signed_tx_sudo = Arc::new(tx_sudo.into_signed(&alice)); app.execute_transaction(signed_tx_sudo).await.unwrap(); - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ InitBridgeAccountAction { rollup_id, asset: nria().into(), @@ -293,20 +284,18 @@ async fn app_execute_transaction_with_every_action_snapshot() { withdrawer_address: None, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); - let tx_bridge_bundlable = UnsignedTransaction::builder() - .actions(vec![ + let tx_bridge_bundlable = UnsignedTransaction::new( + vec![ BridgeLockAction { to: bridge_address, amount: 100, @@ -325,21 +314,19 @@ async fn app_execute_transaction_with_every_action_snapshot() { rollup_withdrawal_event_id: "a-rollup-defined-hash".to_string(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx_bridge_bundlable.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); - let tx_bridge = UnsignedTransaction::builder() - .actions(vec![ + let tx_bridge = UnsignedTransaction::new( + vec![ BridgeSudoChangeAction { bridge_address, new_sudo_address: Some(bob_address), @@ -347,15 +334,13 @@ async fn app_execute_transaction_with_every_action_snapshot() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx_bridge.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); @@ -390,8 +375,8 @@ async fn app_transaction_bundle_categories() { let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); assert!( - UnsignedTransaction::builder() - .actions(vec![ + UnsignedTransaction::new( + vec![ TransferAction { to: bob_address, amount: 333_333, @@ -441,21 +426,19 @@ async fn app_transaction_bundle_categories() { use_compat_address: false, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .is_ok(), + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .is_ok(), "should be able to construct general bundle" ); assert!( - UnsignedTransaction::builder() - .actions(vec![ + UnsignedTransaction::new( + vec![ IbcRelayerChangeAction::Addition(bob_address).into(), FeeAssetChangeAction::Removal("test-0".parse().unwrap()).into(), FeeChangeAction { @@ -463,21 +446,19 @@ async fn app_transaction_bundle_categories() { new_value: 10, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .build() - .is_ok(), + ], + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .is_ok(), "should be able to construct sudo bundle" ); - assert!( - UnsignedTransaction::builder() - .actions(vec![ + assert_eq!( + UnsignedTransaction::new( + vec![ SudoAddressChangeAction { new_address: bob_address, } @@ -486,22 +467,21 @@ async fn app_transaction_bundle_categories() { new_address: bob_address, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .build() - .unwrap_err() - .to_string() - .contains("failed to construct valid `Actions`") + ], + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .unwrap_err() + .to_string(), + "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ + 'Sudo' is not bundleable" ); - assert!( - UnsignedTransaction::builder() - .actions(vec![ + assert_eq!( + UnsignedTransaction::new( + vec![ InitBridgeAccountAction { rollup_id, asset: nria().into(), @@ -518,22 +498,21 @@ async fn app_transaction_bundle_categories() { withdrawer_address: None, } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .build() - .unwrap_err() - .to_string() - .contains("failed to construct valid `Actions`") + ], + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .unwrap_err() + .to_string(), + "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ + 'General' is not bundleable" ); - assert!( - UnsignedTransaction::builder() - .actions(vec![ + assert_eq!( + UnsignedTransaction::new( + vec![ BridgeSudoChangeAction { bridge_address, new_sudo_address: Some(bob_address), @@ -548,16 +527,15 @@ async fn app_transaction_bundle_categories() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .build() - .unwrap_err() - .to_string() - .contains("failed to construct valid `Actions`") + ], + TransactionParams::builder() + .nonce(2) + .chain_id("test") + .build(), + ) + .unwrap_err() + .to_string(), + "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ + 'General' is not bundleable" ); } diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index f6498eecaf..f159fde978 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -103,8 +103,8 @@ async fn app_execute_transaction_transfer() { let alice_address = astria_address(&alice.address_bytes()); let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let value = 333_333; - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ TransferAction { to: bob_address, amount: value, @@ -112,15 +112,13 @@ async fn app_execute_transaction_transfer() { fee_asset: crate::test_utils::nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -163,8 +161,8 @@ async fn app_execute_transaction_transfer_not_native_token() { // transfer funds from Alice to Bob; use native token for fee payment let bob_address = astria_address_from_hex_string(BOB_ADDRESS); - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ TransferAction { to: bob_address, amount: value, @@ -172,15 +170,13 @@ async fn app_execute_transaction_transfer_not_native_token() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -231,8 +227,8 @@ async fn app_execute_transaction_transfer_balance_too_low_for_fee() { let bob = astria_address_from_hex_string(BOB_ADDRESS); // 0-value transfer; only fee is deducted from sender - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ TransferAction { to: bob, amount: 0, @@ -240,15 +236,13 @@ async fn app_execute_transaction_transfer_balance_too_low_for_fee() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&keypair)); let res = app @@ -275,23 +269,21 @@ async fn app_execute_transaction_sequence() { let data = Bytes::from_static(b"hello world"); let fee = calculate_fee_from_state(&data, &app.state).await.unwrap(); - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -313,23 +305,21 @@ async fn app_execute_transaction_invalid_fee_asset() { let alice = get_alice_signing_key(); let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: test_asset(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -347,16 +337,14 @@ async fn app_execute_transaction_validator_update() { verification_key: crate::test_utils::verification_key(1), }; - let tx = UnsignedTransaction::builder() - .actions(vec![Action::ValidatorUpdate(update.clone())]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![Action::ValidatorUpdate(update.clone())], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -377,18 +365,16 @@ async fn app_execute_transaction_ibc_relayer_change_addition() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction::builder() - .actions(vec![Action::IbcRelayerChange( - IbcRelayerChangeAction::Addition(alice_address), - )]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![Action::IbcRelayerChange(IbcRelayerChangeAction::Addition( + alice_address, + ))], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -410,16 +396,14 @@ async fn app_execute_transaction_ibc_relayer_change_deletion() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction::builder() - .actions(vec![IbcRelayerChangeAction::Removal(alice_address).into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![IbcRelayerChangeAction::Removal(alice_address).into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); assert_eq!(app.state.get_account_nonce(alice_address).await.unwrap(), 1); @@ -442,16 +426,14 @@ async fn app_execute_transaction_ibc_relayer_change_invalid() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction::builder() - .actions(vec![IbcRelayerChangeAction::Removal(alice_address).into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![IbcRelayerChangeAction::Removal(alice_address).into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -465,19 +447,16 @@ async fn app_execute_transaction_sudo_address_change() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; let new_address = astria_address_from_hex_string(BOB_ADDRESS); - - let tx = UnsignedTransaction::builder() - .actions(vec![Action::SudoAddressChange(SudoAddressChangeAction { + let tx = UnsignedTransaction::new( + vec![Action::SudoAddressChange(SudoAddressChangeAction { new_address, - })]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + })], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -506,19 +485,16 @@ async fn app_execute_transaction_sudo_address_change_error() { .try_into() .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - - let tx = UnsignedTransaction::builder() - .actions(vec![Action::SudoAddressChange(SudoAddressChangeAction { + let tx = UnsignedTransaction::new( + vec![Action::SudoAddressChange(SudoAddressChangeAction { new_address: alice_address, - })]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + })], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let res = app @@ -539,18 +515,16 @@ async fn app_execute_transaction_fee_asset_change_addition() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction::builder() - .actions(vec![Action::FeeAssetChange( - FeeAssetChangeAction::Addition(test_asset()), - )]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![Action::FeeAssetChange(FeeAssetChangeAction::Addition( + test_asset(), + ))], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -575,18 +549,16 @@ async fn app_execute_transaction_fee_asset_change_removal() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction::builder() - .actions(vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( + let tx = UnsignedTransaction::new( + vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( test_asset(), - ))]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ))], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -603,18 +575,16 @@ async fn app_execute_transaction_fee_asset_change_invalid() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction::builder() - .actions(vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( + let tx = UnsignedTransaction::new( + vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( nria().into(), - ))]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ))], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let res = app @@ -648,16 +618,14 @@ async fn app_execute_transaction_init_bridge_account_ok() { withdrawer_address: None, }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -707,16 +675,14 @@ async fn app_execute_transaction_init_bridge_account_account_already_registered( sudo_address: None, withdrawer_address: None, }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -729,16 +695,14 @@ async fn app_execute_transaction_init_bridge_account_account_already_registered( withdrawer_address: None, }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -769,16 +733,14 @@ async fn app_execute_transaction_bridge_lock_action_ok() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -851,16 +813,14 @@ async fn app_execute_transaction_bridge_lock_action_invalid_for_eoa() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -876,23 +836,21 @@ async fn app_execute_transaction_invalid_nonce() { // create tx with invalid nonce 1 let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(1) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let response = app.execute_transaction(signed_tx).await; @@ -926,23 +884,21 @@ async fn app_execute_transaction_invalid_chain_id() { // create tx with invalid nonce 1 let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction::builder() - .actions(vec![ + let tx = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("wrong-chain") - .build(), - ) - .build() - .unwrap(); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("wrong-chain") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let response = app.execute_transaction(signed_tx).await; @@ -985,8 +941,8 @@ async fn app_stateful_check_fails_insufficient_total_balance() { .unwrap(); // transfer just enough to cover single sequence fee with data - let signed_tx = UnsignedTransaction::builder() - .actions(vec![ + let signed_tx = UnsignedTransaction::new( + vec![ TransferAction { to: keypair_address, amount: fee, @@ -994,21 +950,19 @@ async fn app_stateful_check_fails_insufficient_total_balance() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap() - .into_signed(&alice); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap() + .into_signed(&alice); app.execute_transaction(Arc::new(signed_tx)).await.unwrap(); // build double transfer exceeding balance - let signed_tx_fail = UnsignedTransaction::builder() - .actions(vec![ + let signed_tx_fail = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data: data.clone(), @@ -1021,16 +975,14 @@ async fn app_stateful_check_fails_insufficient_total_balance() { fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap() - .into_signed(&keypair); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap() + .into_signed(&keypair); // try double, see fails stateful check let res = signed_tx_fail .check_and_execute(Arc::get_mut(&mut app.state).unwrap()) @@ -1041,24 +993,22 @@ async fn app_stateful_check_fails_insufficient_total_balance() { assert!(res.contains("insufficient funds for asset")); // build single transfer to see passes - let signed_tx_pass = UnsignedTransaction::builder() - .actions(vec![ + let signed_tx_pass = UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap() - .into_signed(&keypair); + ], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap() + .into_signed(&keypair); signed_tx_pass .check_and_execute(Arc::get_mut(&mut app.state).unwrap()) @@ -1103,16 +1053,14 @@ async fn app_execute_transaction_bridge_lock_unlock_action_ok() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -1130,16 +1078,14 @@ async fn app_execute_transaction_bridge_lock_unlock_action_ok() { rollup_withdrawal_event_id: "id-from-rollup".to_string(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&bridge)); app.execute_transaction(signed_tx) @@ -1181,16 +1127,14 @@ async fn app_execute_transaction_action_index_correctly_increments() { destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::builder() - .actions(vec![action.clone().into(), action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![action.clone().into(), action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); @@ -1221,25 +1165,21 @@ async fn transaction_execution_records_deposit_event() { .put_bridge_account_ibc_asset(bob_address, nria()) .unwrap(); - let tx = UnsignedTransaction::builder() - .actions(vec![ - BridgeLockAction { - to: bob_address, - amount: 1, - asset: nria().into(), - fee_asset: nria().into(), - destination_chain_address: "test_chain_address".to_string(), - } - .into(), - ]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .build() - .unwrap(); + let action = BridgeLockAction { + to: bob_address, + amount: 1, + asset: nria().into(), + fee_asset: nria().into(), + destination_chain_address: "test_chain_address".to_string(), + }; + let tx = UnsignedTransaction::new( + vec![action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test") + .build(), + ) + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let expected_deposit = Deposit { diff --git a/crates/astria-sequencer/src/benchmark_utils.rs b/crates/astria-sequencer/src/benchmark_utils.rs index 4508e91017..46ba8f1211 100644 --- a/crates/astria-sequencer/src/benchmark_utils.rs +++ b/crates/astria-sequencer/src/benchmark_utils.rs @@ -94,10 +94,7 @@ fn sequence_actions() -> Vec> { data: vec![2; 1000].into(), fee_asset: Denom::IbcPrefixed(IbcPrefixed::new([3; 32])), }; - let tx = UnsignedTransaction::builder() - .actions(vec![Action::Sequence(sequence_action)]) - .params(params) - .build() + let tx = UnsignedTransaction::new(vec![Action::Sequence(sequence_action)], params) .expect("failed to build transaction from actions") .into_signed(signing_key); Arc::new(tx) @@ -122,16 +119,14 @@ fn transfers() -> Vec> { .nonce(u32::try_from(nonce).unwrap()) .chain_id("test") .build(); - let tx = UnsignedTransaction::builder() - .actions( - std::iter::repeat(action.clone()) - .take(TRANSFERS_PER_TX) - .collect(), - ) - .params(params) - .build() - .expect("failed to build transaction from actions") - .into_signed(sender); + let tx = UnsignedTransaction::new( + std::iter::repeat(action.clone()) + .take(TRANSFERS_PER_TX) + .collect(), + params, + ) + .expect("failed to build unsigned transaction") + .into_signed(sender); Arc::new(tx) }) .collect() diff --git a/crates/astria-sequencer/src/proposal/commitment.rs b/crates/astria-sequencer/src/proposal/commitment.rs index f07868e7a1..86cffd7fe4 100644 --- a/crates/astria-sequencer/src/proposal/commitment.rs +++ b/crates/astria-sequencer/src/proposal/commitment.rs @@ -122,16 +122,14 @@ mod test { let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction::builder() - .actions(vec![sequence_action.clone().into(), transfer_action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![sequence_action.clone().into(), transfer_action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test-chain-1") + .build(), + ) + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; @@ -141,16 +139,14 @@ mod test { } = generate_rollup_datas_commitment(&txs, HashMap::new()); let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction::builder() - .actions(vec![sequence_action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![sequence_action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test-chain-1") + .build(), + ) + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; @@ -182,16 +178,14 @@ mod test { }; let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction::builder() - .actions(vec![sequence_action.clone().into(), transfer_action.into()]) - .params( - TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - ) - .build() - .unwrap(); + let tx = UnsignedTransaction::new( + vec![sequence_action.clone().into(), transfer_action.into()], + TransactionParams::builder() + .nonce(0) + .chain_id("test-chain-1") + .build(), + ) + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 3f7437d980..cc3209e213 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -237,23 +237,21 @@ mod test { }; fn make_unsigned_tx() -> UnsignedTransaction { - UnsignedTransaction::builder() - .actions(vec![ + UnsignedTransaction::new( + vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data: Bytes::from_static(b"hello world"), fee_asset: crate::test_utils::nria().into(), } .into(), - ]) - .params( - TransactionParams::builder() - .chain_id("test") - .nonce(0) - .build(), - ) - .build() - .unwrap() + ], + TransactionParams::builder() + .chain_id("test") + .nonce(0) + .build(), + ) + .unwrap() } fn new_prepare_proposal_request() -> request::PrepareProposal { diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index bd4f2dd70e..5c597699ca 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -405,11 +405,7 @@ mod tests { .nonce(0) .chain_id("test-chain-id") .build(); - let tx = UnsignedTransaction::builder() - .actions(actions) - .params(params) - .build() - .unwrap(); + let tx = UnsignedTransaction::new(actions, params).unwrap(); let signed_tx = tx.into_signed(&alice); check_balance_for_total_fees_and_transfers(&signed_tx, &state_tx) @@ -472,11 +468,7 @@ mod tests { .nonce(0) .chain_id("test-chain-id") .build(); - let tx = UnsignedTransaction::builder() - .actions(actions) - .params(params) - .build() - .unwrap(); + let tx = UnsignedTransaction::new(actions, params).unwrap(); let signed_tx = tx.into_signed(&alice); let err = check_balance_for_total_fees_and_transfers(&signed_tx, &state_tx) From de9b74410bd2799a267202a8b2afefc8394bfa88 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 23 Sep 2024 17:43:51 -0400 Subject: [PATCH 04/18] fix clippy errors --- .../src/executor/bundle_factory/mod.rs | 2 +- .../transaction/v1alpha1/action_group.rs | 56 +++++++------------ 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index 75b197035e..a9bf17b9a3 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -76,7 +76,7 @@ impl SizedBundle { } } - #[allow(clippy::allow_panic)] // method is expected to never panic as an invariant of the type + #[allow(clippy::panic)] // method is expected to never panic as an invariant of the type /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. pub(super) fn to_unsigned_transaction(&self, params: TransactionParams) -> UnsignedTransaction { UnsignedTransaction::new(self.buffer.clone(), params).unwrap() diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs index 878901a064..fcf66613c0 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs @@ -300,52 +300,46 @@ mod test { rollup_id: RollupId::from([8; 32]), data: vec![].into(), fee_asset: asset.clone(), - }) - .into(), + }), Action::Transfer(TransferAction { to: address, amount: 100, asset: asset.clone(), fee_asset: asset.clone(), - }) - .into(), + }), Action::BridgeLock(BridgeLockAction { to: address, amount: 100, asset: asset.clone(), fee_asset: asset.clone(), - destination_chain_address: "".to_string(), - }) - .into(), + destination_chain_address: String::new(), + }), Action::BridgeUnlock(BridgeUnlockAction { to: address, amount: 100, fee_asset: asset.clone(), bridge_address: address, - memo: "".to_string(), + memo: String::new(), rollup_block_number: 0, - rollup_withdrawal_event_id: "".to_string(), - }) - .into(), + rollup_withdrawal_event_id: String::new(), + }), Action::ValidatorUpdate(ValidatorUpdate { power: 100, verification_key: VerificationKey::try_from([0; 32]).unwrap(), - }) - .into(), + }), Action::Ics20Withdrawal(Ics20Withdrawal { denom: asset.clone(), - destination_chain_address: "test".to_string(), + destination_chain_address: String::new(), return_address: address, amount: 1_000_000u128, - memo: "test".to_string(), + memo: String::new(), fee_asset: asset.clone(), timeout_height: Height::new(1, 1).unwrap(), timeout_time: 0, source_channel: "channel-0".parse().unwrap(), bridge_address: Some(address), use_compat_address: false, - }) - .into(), + }), ]; assert!(matches!( @@ -367,10 +361,9 @@ mod test { Action::FeeChange(FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 100, - }) - .into(), - Action::FeeAssetChange(FeeAssetChangeAction::Addition(asset).into()).into(), - Action::IbcRelayerChange(IbcRelayerChangeAction::Addition(address)).into(), + }), + Action::FeeAssetChange(FeeAssetChangeAction::Addition(asset)), + Action::IbcRelayerChange(IbcRelayerChangeAction::Addition(address)), ]; assert!(matches!( @@ -387,12 +380,9 @@ mod test { .try_build() .unwrap(); - let actions = vec![ - Action::SudoAddressChange(SudoAddressChangeAction { - new_address: address, - }) - .into(), - ]; + let actions = vec![Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + })]; assert!(matches!( Actions::from_list_of_actions(actions).unwrap().group(), @@ -402,12 +392,10 @@ mod test { let actions = vec![ Action::SudoAddressChange(SudoAddressChangeAction { new_address: address, - }) - .into(), + }), Action::SudoAddressChange(SudoAddressChangeAction { new_address: address, - }) - .into(), + }), ]; assert_eq!( @@ -486,12 +474,10 @@ mod test { rollup_id: RollupId::from([8; 32]), data: vec![].into(), fee_asset: asset.clone(), - }) - .into(), + }), Action::SudoAddressChange(SudoAddressChangeAction { new_address: address, - }) - .into(), + }), ]; assert_eq!( From b275612d321d25bcfd0d701ead1a983e39d50aa3 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Wed, 25 Sep 2024 09:04:13 -0400 Subject: [PATCH 05/18] address comments from @noot --- .../src/executor/bundle_factory/mod.rs | 3 +- crates/astria-core/src/protocol/test_utils.rs | 3 +- .../transaction/v1alpha1/action_group.rs | 147 ++++++------------ .../src/protocol/transaction/v1alpha1/mod.rs | 2 +- crates/astria-sequencer/src/app/mod.rs | 2 +- crates/astria-sequencer/src/app/test_utils.rs | 2 +- .../src/app/tests_app/mempool.rs | 12 +- .../astria-sequencer/src/app/tests_app/mod.rs | 14 +- .../src/app/tests_block_fees.rs | 8 +- .../src/app/tests_breaking_changes.rs | 26 ++-- .../astria-sequencer/src/benchmark_utils.rs | 2 +- 11 files changed, 85 insertions(+), 136 deletions(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index a9bf17b9a3..8eda56aa6b 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -76,7 +76,8 @@ impl SizedBundle { } } - #[allow(clippy::panic)] // method is expected to never panic as an invariant of the type + #[allow(clippy::panic)] // method is expected to never panic because only `SequenceActions` are + /// added to the bundle, which should produce a valid variant of the `ActionGroup` type /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. pub(super) fn to_unsigned_transaction(&self, params: TransactionParams) -> UnsignedTransaction { UnsignedTransaction::new(self.buffer.clone(), params).unwrap() diff --git a/crates/astria-core/src/protocol/test_utils.rs b/crates/astria-core/src/protocol/test_utils.rs index 50c1f2f21b..12a7cbea31 100644 --- a/crates/astria-core/src/protocol/test_utils.rs +++ b/crates/astria-core/src/protocol/test_utils.rs @@ -112,7 +112,8 @@ impl ConfigureSequencerBlock { .build(), ) .expect( - "failed to build unsigned transaction, should be okay since only sequence actions", + "should be able to build unsigned transaction since only sequence actions are \ + contained", ); vec![unsigned_transaction.into_signed(&signing_key)] }; diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs index fcf66613c0..c03b27f2af 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs @@ -39,43 +39,19 @@ macro_rules! impl_belong_to_group { } impl_belong_to_group!( - ( - SequenceAction, - ActionGroup::BundlableGeneral(BundlableGeneral) - ), - ( - TransferAction, - ActionGroup::BundlableGeneral(BundlableGeneral) - ), - ( - ValidatorUpdate, - ActionGroup::BundlableGeneral(BundlableGeneral) - ), - (SudoAddressChangeAction, ActionGroup::Sudo(Sudo)), - ( - IbcRelayerChangeAction, - ActionGroup::BundlableSudo(BundlableSudo) - ), - ( - Ics20Withdrawal, - ActionGroup::BundlableGeneral(BundlableGeneral) - ), - (InitBridgeAccountAction, ActionGroup::General(General)), - ( - BridgeLockAction, - ActionGroup::BundlableGeneral(BundlableGeneral) - ), - ( - BridgeUnlockAction, - ActionGroup::BundlableGeneral(BundlableGeneral) - ), - (BridgeSudoChangeAction, ActionGroup::General(General)), - (FeeChangeAction, ActionGroup::BundlableSudo(BundlableSudo)), - ( - FeeAssetChangeAction, - ActionGroup::BundlableSudo(BundlableSudo) - ), - (IbcRelay, ActionGroup::BundlableGeneral(BundlableGeneral)), + (SequenceAction, ActionGroup::General), + (TransferAction, ActionGroup::General), + (ValidatorUpdate, ActionGroup::General), + (SudoAddressChangeAction, ActionGroup::UnbundleableSudo), + (IbcRelayerChangeAction, ActionGroup::Sudo), + (Ics20Withdrawal, ActionGroup::General), + (InitBridgeAccountAction, ActionGroup::UnbundleableGeneral), + (BridgeLockAction, ActionGroup::General), + (BridgeUnlockAction, ActionGroup::General), + (BridgeSudoChangeAction, ActionGroup::UnbundleableGeneral), + (FeeChangeAction, ActionGroup::Sudo), + (FeeAssetChangeAction, ActionGroup::Sudo), + (IbcRelay, ActionGroup::General), ); pub trait Group { @@ -102,58 +78,25 @@ impl Group for Action { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct BundlableGeneral; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct General; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct BundlableSudo; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Sudo; - #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ActionGroup { - BundlableGeneral(BundlableGeneral), - General(General), - BundlableSudo(BundlableSudo), - Sudo(Sudo), + General, + UnbundleableGeneral, + Sudo, + UnbundleableSudo, } impl fmt::Display for ActionGroup { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ActionGroup::BundlableGeneral(_) => write!(f, "BundlableGeneral"), - ActionGroup::General(_) => write!(f, "General"), - ActionGroup::BundlableSudo(_) => write!(f, "BundlableSudo"), - ActionGroup::Sudo(_) => write!(f, "Sudo"), + ActionGroup::General => write!(f, "General"), + ActionGroup::UnbundleableGeneral => write!(f, "UnbundleableGeneral"), + ActionGroup::Sudo => write!(f, "Sudo"), + ActionGroup::UnbundleableSudo => write!(f, "UnbundleableSudo"), } } } -impl From for ActionGroup { - fn from(val: BundlableGeneral) -> ActionGroup { - ActionGroup::BundlableGeneral(val) - } -} - -impl From for ActionGroup { - fn from(val: General) -> ActionGroup { - ActionGroup::General(val) - } -} - -impl From for ActionGroup { - fn from(val: BundlableSudo) -> ActionGroup { - ActionGroup::BundlableSudo(val) - } -} - -impl From for ActionGroup { - fn from(val: Sudo) -> ActionGroup { - ActionGroup::Sudo(val) - } -} - #[derive(Debug, thiserror::Error)] enum ErrorKind { #[error("input contains mixed action types")] @@ -207,7 +150,7 @@ impl Error { } #[must_use] - pub fn not_bundlable(group: ActionGroup) -> Self { + pub fn not_bundleable(group: ActionGroup) -> Self { let context = format!("ActionGroup type '{group}' is not bundleable"); Self::new(ErrorKind::NotBundleable, Some(context)) } @@ -239,7 +182,7 @@ impl Actions { } } - pub(super) fn from_list_of_actions(actions: Vec) -> Result { + pub(super) fn try_from_list_of_actions(actions: Vec) -> Result { let mut actions_iter = actions.iter(); let group = match actions_iter.next() { Some(action) => action.group(), @@ -249,9 +192,13 @@ impl Actions { } }; - // assert size constraints on non-bundlable action groups - if matches!(group, ActionGroup::General(_) | ActionGroup::Sudo(_)) && actions.len() > 1 { - return Err(Error::not_bundlable(group)); + // assert size constraints on non-bundleable action groups + if matches!( + group, + ActionGroup::UnbundleableGeneral | ActionGroup::UnbundleableSudo + ) && actions.len() > 1 + { + return Err(Error::not_bundleable(group)); } // assert the rest of the actions have the same group as the first @@ -287,7 +234,7 @@ mod test { const ASTRIA_ADDRESS_PREFIX: &str = "astria"; #[test] - fn from_list_of_actions_bundlable_general() { + fn try_from_list_of_actions_bundleable_general() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -343,13 +290,13 @@ mod test { ]; assert!(matches!( - Actions::from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::BundlableGeneral(_)) + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::General) )); } #[test] - fn from_list_of_actions_bundlable_sudo() { + fn from_list_of_actions_bundleable_sudo() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -367,8 +314,8 @@ mod test { ]; assert!(matches!( - Actions::from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::BundlableSudo(_)) + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::Sudo) )); } @@ -385,8 +332,8 @@ mod test { })]; assert!(matches!( - Actions::from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::Sudo(_)) + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::UnbundleableSudo) )); let actions = vec![ @@ -399,7 +346,7 @@ mod test { ]; assert_eq!( - Actions::from_list_of_actions(actions) + Actions::try_from_list_of_actions(actions) .unwrap_err() .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ @@ -435,15 +382,15 @@ mod test { let actions = vec![init_bridge_account_action.clone().into()]; assert!(matches!( - Actions::from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::General(_)) + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::UnbundleableGeneral) )); let actions = vec![sudo_bridge_address_change_action.clone().into()]; assert!(matches!( - Actions::from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::General(_)) + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::UnbundleableGeneral) )); let actions = vec![ @@ -452,11 +399,11 @@ mod test { ]; assert_eq!( - Actions::from_list_of_actions(actions) + Actions::try_from_list_of_actions(actions) .unwrap_err() .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'General' is not bundleable" + 'UnbundleableGeneral' is not bundleable" ); } @@ -481,11 +428,11 @@ mod test { ]; assert_eq!( - Actions::from_list_of_actions(actions) + Actions::try_from_list_of_actions(actions) .unwrap_err() .to_string(), "input contains mixed action types: Mixed actions of different types. Original group: \ - 'BundlableGeneral', Additional group: 'Sudo', triggering action: 'SudoAddressChange'" + 'General', Additional group: 'Sudo', triggering action: 'SudoAddressChange'" ); } } diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index c031d695ab..7bffbd3e6e 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -255,7 +255,7 @@ impl UnsignedTransaction { params: TransactionParams, ) -> Result { Ok(Self { - actions: Actions::from_list_of_actions(actions)?, + actions: Actions::try_from_list_of_actions(actions)?, params, }) } diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 99f57694fd..999db0407c 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -1025,7 +1025,7 @@ impl App { // flag mempool for cleaning if we ran a fee change action self.recost_mempool = self.recost_mempool - || matches!(signed_tx.group(), Some(ActionGroup::BundlableSudo(_))) + || matches!(signed_tx.group(), Some(ActionGroup::Sudo)) && signed_tx .actions() .iter() diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 9f7d1c47b9..20ef3d5c5b 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -237,7 +237,7 @@ pub(crate) fn mock_tx( .chain_id("test") .build(), ) - .expect("Failed to build unsigned transaction"); + .expect("can build unsigned transaction"); Arc::new(tx.into_signed(signer)) } diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index 2775efb83d..8a9d011172 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -63,7 +63,7 @@ async fn trigger_cleaning() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&get_judy_signing_key()); app.mempool @@ -162,7 +162,7 @@ async fn do_not_trigger_cleaning() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&get_alice_signing_key()); app.mempool @@ -238,7 +238,7 @@ async fn maintenance_recosting_promotes() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&get_bob_signing_key()); let mut bob_funds = HashMap::new(); @@ -269,7 +269,7 @@ async fn maintenance_recosting_promotes() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&get_judy_signing_key()); let mut judy_funds = HashMap::new(); @@ -413,7 +413,7 @@ async fn maintenance_funds_added_promotes() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&get_carol_signing_key()); let mut carol_funds = HashMap::new(); @@ -446,7 +446,7 @@ async fn maintenance_funds_added_promotes() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&get_alice_signing_key()); let mut alice_funds = HashMap::new(); diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 43ce190fe0..1f3dc03f8e 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -246,7 +246,7 @@ async fn app_transfer_block_fees_to_sudo() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction"); + .expect("can build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -330,7 +330,7 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction"); + .expect("can build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -425,7 +425,7 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction"); + .expect("can build unsigned transaction"); let signed_tx = tx.into_signed(&alice); @@ -570,7 +570,7 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&alice); let tx_overflow = UnsignedTransaction::new( @@ -587,7 +587,7 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&alice); app.mempool @@ -665,7 +665,7 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&alice); let tx_overflow = UnsignedTransaction::new( @@ -682,7 +682,7 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(&alice); app.mempool diff --git a/crates/astria-sequencer/src/app/tests_block_fees.rs b/crates/astria-sequencer/src/app/tests_block_fees.rs index cf40c0e7df..415c782b9a 100644 --- a/crates/astria-sequencer/src/app/tests_block_fees.rs +++ b/crates/astria-sequencer/src/app/tests_block_fees.rs @@ -121,7 +121,7 @@ async fn ensure_correct_block_fees_transfer() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction"); + .expect("can build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -163,7 +163,7 @@ async fn ensure_correct_block_fees_sequence() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction"); + .expect("can build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -207,7 +207,7 @@ async fn ensure_correct_block_fees_init_bridge_acct() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction"); + .expect("can build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -262,7 +262,7 @@ async fn ensure_correct_block_fees_bridge_lock() { .chain_id("test") .build(), ) - .expect("failed to build unsigned transaction"); + .expect("can build unsigned transaction"); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index a38d9d42cc..5b7feeacde 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -207,7 +207,7 @@ async fn app_execute_transaction_with_every_action_snapshot() { let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); - let tx_bundlable_general = UnsignedTransaction::new( + let tx_bundleable_general = UnsignedTransaction::new( vec![ TransferAction { to: bob_address, @@ -231,7 +231,7 @@ async fn app_execute_transaction_with_every_action_snapshot() { ) .unwrap(); - let tx_bundlable_sudo = UnsignedTransaction::new( + let tx_bundleable_sudo = UnsignedTransaction::new( vec![ IbcRelayerChangeAction::Addition(bob_address).into(), IbcRelayerChangeAction::Addition(carol_address).into(), @@ -261,13 +261,13 @@ async fn app_execute_transaction_with_every_action_snapshot() { ) .unwrap(); - let signed_tx_general_bundlable = Arc::new(tx_bundlable_general.into_signed(&alice)); - app.execute_transaction(signed_tx_general_bundlable) + let signed_tx_general_bundleable = Arc::new(tx_bundleable_general.into_signed(&alice)); + app.execute_transaction(signed_tx_general_bundleable) .await .unwrap(); - let signed_tx_sudo_bundlable = Arc::new(tx_bundlable_sudo.into_signed(&alice)); - app.execute_transaction(signed_tx_sudo_bundlable) + let signed_tx_sudo_bundleable = Arc::new(tx_bundleable_sudo.into_signed(&alice)); + app.execute_transaction(signed_tx_sudo_bundleable) .await .unwrap(); @@ -294,7 +294,7 @@ async fn app_execute_transaction_with_every_action_snapshot() { let signed_tx = Arc::new(tx.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); - let tx_bridge_bundlable = UnsignedTransaction::new( + let tx_bridge_bundleable = UnsignedTransaction::new( vec![ BridgeLockAction { to: bridge_address, @@ -322,7 +322,7 @@ async fn app_execute_transaction_with_every_action_snapshot() { ) .unwrap(); - let signed_tx = Arc::new(tx_bridge_bundlable.into_signed(&bridge)); + let signed_tx = Arc::new(tx_bridge_bundleable.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); let tx_bridge = UnsignedTransaction::new( @@ -355,10 +355,10 @@ async fn app_execute_transaction_with_every_action_snapshot() { } // Note: this test ensures that all actions are in their expected -// bundlable vs non-bundlable category. Tests all actions except +// bundleable vs non-bundleable category. Tests all actions except // IbcRelay. // -// If any PR changes the bundlable status of an action, the PR must +// If any PR changes the bundleable status of an action, the PR must // be marked as breaking. #[allow(clippy::too_many_lines)] #[tokio::test] @@ -476,7 +476,7 @@ async fn app_transaction_bundle_categories() { .unwrap_err() .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'Sudo' is not bundleable" + 'UnbundleableSudo' is not bundleable" ); assert_eq!( @@ -507,7 +507,7 @@ async fn app_transaction_bundle_categories() { .unwrap_err() .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'General' is not bundleable" + 'UnbundleableGeneral' is not bundleable" ); assert_eq!( @@ -536,6 +536,6 @@ async fn app_transaction_bundle_categories() { .unwrap_err() .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'General' is not bundleable" + 'UnbundleableGeneral' is not bundleable" ); } diff --git a/crates/astria-sequencer/src/benchmark_utils.rs b/crates/astria-sequencer/src/benchmark_utils.rs index 46ba8f1211..02430dd7d3 100644 --- a/crates/astria-sequencer/src/benchmark_utils.rs +++ b/crates/astria-sequencer/src/benchmark_utils.rs @@ -125,7 +125,7 @@ fn transfers() -> Vec> { .collect(), params, ) - .expect("failed to build unsigned transaction") + .expect("can build unsigned transaction") .into_signed(sender); Arc::new(tx) }) From 8162f9ded2300f2a84263dbd39b2ea660ed5d4a7 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Wed, 25 Sep 2024 13:49:19 -0400 Subject: [PATCH 06/18] respond to all comments by @SuperFluffy except builder pattern --- .../transaction/v1alpha1/action_group.rs | 438 ------------------ .../transaction/v1alpha1/action_group/mod.rs | 204 ++++++++ .../v1alpha1/action_group/tests.rs | 234 ++++++++++ .../src/protocol/transaction/v1alpha1/mod.rs | 13 +- crates/astria-sequencer/src/app/mod.rs | 3 +- crates/astria-sequencer/src/app/test_utils.rs | 2 +- .../src/app/tests_app/mempool.rs | 12 +- .../astria-sequencer/src/app/tests_app/mod.rs | 14 +- .../src/app/tests_block_fees.rs | 8 +- .../src/app/tests_breaking_changes.rs | 9 +- .../astria-sequencer/src/benchmark_utils.rs | 2 +- 11 files changed, 468 insertions(+), 471 deletions(-) delete mode 100644 crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs create mode 100644 crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs create mode 100644 crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs deleted file mode 100644 index c03b27f2af..0000000000 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group.rs +++ /dev/null @@ -1,438 +0,0 @@ -use std::fmt::{ - self, - Debug, -}; - -use penumbra_ibc::IbcRelay; - -use super::{ - action::{ - ActionName, - BridgeLockAction, - BridgeSudoChangeAction, - BridgeUnlockAction, - FeeAssetChangeAction, - FeeChangeAction, - IbcRelayerChangeAction, - Ics20Withdrawal, - InitBridgeAccountAction, - SequenceAction, - SudoAddressChangeAction, - TransferAction, - ValidatorUpdate, - }, - Action, -}; - -trait BelongsToGroup { - const GROUP: ActionGroup; -} - -macro_rules! impl_belong_to_group { - ($(($act:ty, $group:expr)),*$(,)?) => { - $( - impl BelongsToGroup for $act { - const GROUP: ActionGroup = $group; - } - )* - } -} - -impl_belong_to_group!( - (SequenceAction, ActionGroup::General), - (TransferAction, ActionGroup::General), - (ValidatorUpdate, ActionGroup::General), - (SudoAddressChangeAction, ActionGroup::UnbundleableSudo), - (IbcRelayerChangeAction, ActionGroup::Sudo), - (Ics20Withdrawal, ActionGroup::General), - (InitBridgeAccountAction, ActionGroup::UnbundleableGeneral), - (BridgeLockAction, ActionGroup::General), - (BridgeUnlockAction, ActionGroup::General), - (BridgeSudoChangeAction, ActionGroup::UnbundleableGeneral), - (FeeChangeAction, ActionGroup::Sudo), - (FeeAssetChangeAction, ActionGroup::Sudo), - (IbcRelay, ActionGroup::General), -); - -pub trait Group { - fn group(&self) -> ActionGroup; -} - -impl Group for Action { - fn group(&self) -> ActionGroup { - match self { - Action::Sequence(_) => SequenceAction::GROUP, - Action::Transfer(_) => TransferAction::GROUP, - Action::ValidatorUpdate(_) => ValidatorUpdate::GROUP, - Action::SudoAddressChange(_) => SudoAddressChangeAction::GROUP, - Action::IbcRelayerChange(_) => IbcRelayerChangeAction::GROUP, - Action::Ics20Withdrawal(_) => Ics20Withdrawal::GROUP, - Action::InitBridgeAccount(_) => InitBridgeAccountAction::GROUP, - Action::BridgeLock(_) => BridgeLockAction::GROUP, - Action::BridgeUnlock(_) => BridgeUnlockAction::GROUP, - Action::BridgeSudoChange(_) => BridgeSudoChangeAction::GROUP, - Action::FeeChange(_) => FeeChangeAction::GROUP, - Action::FeeAssetChange(_) => FeeAssetChangeAction::GROUP, - Action::Ibc(_) => IbcRelay::GROUP, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ActionGroup { - General, - UnbundleableGeneral, - Sudo, - UnbundleableSudo, -} - -impl fmt::Display for ActionGroup { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ActionGroup::General => write!(f, "General"), - ActionGroup::UnbundleableGeneral => write!(f, "UnbundleableGeneral"), - ActionGroup::Sudo => write!(f, "Sudo"), - ActionGroup::UnbundleableSudo => write!(f, "UnbundleableSudo"), - } - } -} - -#[derive(Debug, thiserror::Error)] -enum ErrorKind { - #[error("input contains mixed action types")] - Mixed, - #[error("attempted to create bundle with non bundleable `ActionGroup` type")] - NotBundleable, -} - -#[derive(Debug)] -pub struct Error { - kind: ErrorKind, - context: Option, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.kind)?; - if let Some(ctx) = &self.context { - write!(f, ": {ctx}")?; - } - Ok(()) - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.kind) - } -} - -impl Error { - fn new(kind: ErrorKind, context: Option) -> Self { - Self { - kind, - context, - } - } - - #[must_use] - pub fn mixed( - original_group: ActionGroup, - additional_group: ActionGroup, - action: &Action, - ) -> Self { - let context = format!( - "Mixed actions of different types. Original group: '{original_group}', Additional \ - group: '{additional_group}', triggering action: '{}'", - action.name() - ); - Self::new(ErrorKind::Mixed, Some(context)) - } - - #[must_use] - pub fn not_bundleable(group: ActionGroup) -> Self { - let context = format!("ActionGroup type '{group}' is not bundleable"); - Self::new(ErrorKind::NotBundleable, Some(context)) - } -} - -/// Invariants: `group` is set if `inner` is not empty. -#[derive(Clone, Debug)] -pub(super) struct Actions { - inner: Vec, -} - -impl Actions { - pub(super) fn actions(&self) -> &[Action] { - &self.inner - } - - #[must_use] - pub(super) fn into_actions(self) -> Vec { - self.inner - } - - pub(super) fn group(&self) -> Option { - self.inner.first().map(Group::group) - } - - pub(super) fn default() -> Self { - Self { - inner: vec![], - } - } - - pub(super) fn try_from_list_of_actions(actions: Vec) -> Result { - let mut actions_iter = actions.iter(); - let group = match actions_iter.next() { - Some(action) => action.group(), - None => { - // empty `actions`, so invariants met - return Ok(Self::default()); - } - }; - - // assert size constraints on non-bundleable action groups - if matches!( - group, - ActionGroup::UnbundleableGeneral | ActionGroup::UnbundleableSudo - ) && actions.len() > 1 - { - return Err(Error::not_bundleable(group)); - } - - // assert the rest of the actions have the same group as the first - for action in actions_iter { - if action.group() != group { - return Err(Error::mixed(group, action.group(), action)); - } - } - - Ok(Self { - inner: actions, - }) - } -} - -#[cfg(test)] -mod test { - use ibc_types::core::client::Height; - - use super::*; - use crate::{ - crypto::VerificationKey, - primitive::v1::{ - asset::Denom, - Address, - RollupId, - }, - protocol::transaction::v1alpha1::action::{ - FeeChange, - TransferAction, - }, - }; - const ASTRIA_ADDRESS_PREFIX: &str = "astria"; - - #[test] - fn try_from_list_of_actions_bundleable_general() { - let address: Address<_> = Address::builder() - .array([0; 20]) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .unwrap(); - - let asset: Denom = "nria".parse().unwrap(); - let actions = vec![ - Action::Sequence(SequenceAction { - rollup_id: RollupId::from([8; 32]), - data: vec![].into(), - fee_asset: asset.clone(), - }), - Action::Transfer(TransferAction { - to: address, - amount: 100, - asset: asset.clone(), - fee_asset: asset.clone(), - }), - Action::BridgeLock(BridgeLockAction { - to: address, - amount: 100, - asset: asset.clone(), - fee_asset: asset.clone(), - destination_chain_address: String::new(), - }), - Action::BridgeUnlock(BridgeUnlockAction { - to: address, - amount: 100, - fee_asset: asset.clone(), - bridge_address: address, - memo: String::new(), - rollup_block_number: 0, - rollup_withdrawal_event_id: String::new(), - }), - Action::ValidatorUpdate(ValidatorUpdate { - power: 100, - verification_key: VerificationKey::try_from([0; 32]).unwrap(), - }), - Action::Ics20Withdrawal(Ics20Withdrawal { - denom: asset.clone(), - destination_chain_address: String::new(), - return_address: address, - amount: 1_000_000u128, - memo: String::new(), - fee_asset: asset.clone(), - timeout_height: Height::new(1, 1).unwrap(), - timeout_time: 0, - source_channel: "channel-0".parse().unwrap(), - bridge_address: Some(address), - use_compat_address: false, - }), - ]; - - assert!(matches!( - Actions::try_from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::General) - )); - } - - #[test] - fn from_list_of_actions_bundleable_sudo() { - let address: Address<_> = Address::builder() - .array([0; 20]) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .unwrap(); - - let asset: Denom = "nria".parse().unwrap(); - let actions = vec![ - Action::FeeChange(FeeChangeAction { - fee_change: FeeChange::TransferBaseFee, - new_value: 100, - }), - Action::FeeAssetChange(FeeAssetChangeAction::Addition(asset)), - Action::IbcRelayerChange(IbcRelayerChangeAction::Addition(address)), - ]; - - assert!(matches!( - Actions::try_from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::Sudo) - )); - } - - #[test] - fn from_list_of_actions_sudo() { - let address: Address<_> = Address::builder() - .array([0; 20]) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .unwrap(); - - let actions = vec![Action::SudoAddressChange(SudoAddressChangeAction { - new_address: address, - })]; - - assert!(matches!( - Actions::try_from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::UnbundleableSudo) - )); - - let actions = vec![ - Action::SudoAddressChange(SudoAddressChangeAction { - new_address: address, - }), - Action::SudoAddressChange(SudoAddressChangeAction { - new_address: address, - }), - ]; - - assert_eq!( - Actions::try_from_list_of_actions(actions) - .unwrap_err() - .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'Sudo' is not bundleable" - ); - } - - #[test] - fn from_list_of_actions_general() { - let address: Address<_> = Address::builder() - .array([0; 20]) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .unwrap(); - - let asset: Denom = "nria".parse().unwrap(); - - let init_bridge_account_action = InitBridgeAccountAction { - rollup_id: RollupId::from([8; 32]), - asset: asset.clone(), - fee_asset: asset.clone(), - sudo_address: Some(address), - withdrawer_address: Some(address), - }; - - let sudo_bridge_address_change_action = BridgeSudoChangeAction { - new_sudo_address: Some(address), - bridge_address: address, - new_withdrawer_address: Some(address), - fee_asset: asset.clone(), - }; - - let actions = vec![init_bridge_account_action.clone().into()]; - - assert!(matches!( - Actions::try_from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::UnbundleableGeneral) - )); - - let actions = vec![sudo_bridge_address_change_action.clone().into()]; - - assert!(matches!( - Actions::try_from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::UnbundleableGeneral) - )); - - let actions = vec![ - init_bridge_account_action.into(), - sudo_bridge_address_change_action.into(), - ]; - - assert_eq!( - Actions::try_from_list_of_actions(actions) - .unwrap_err() - .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'UnbundleableGeneral' is not bundleable" - ); - } - - #[test] - fn from_list_of_actions_mixed() { - let address: Address<_> = Address::builder() - .array([0; 20]) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .unwrap(); - - let asset: Denom = "nria".parse().unwrap(); - let actions = vec![ - Action::Sequence(SequenceAction { - rollup_id: RollupId::from([8; 32]), - data: vec![].into(), - fee_asset: asset.clone(), - }), - Action::SudoAddressChange(SudoAddressChangeAction { - new_address: address, - }), - ]; - - assert_eq!( - Actions::try_from_list_of_actions(actions) - .unwrap_err() - .to_string(), - "input contains mixed action types: Mixed actions of different types. Original group: \ - 'General', Additional group: 'Sudo', triggering action: 'SudoAddressChange'" - ); - } -} diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs new file mode 100644 index 0000000000..ec8525634d --- /dev/null +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs @@ -0,0 +1,204 @@ +#[cfg(test)] +mod tests; + +use std::fmt::{ + self, + Debug, +}; + +use penumbra_ibc::IbcRelay; + +use super::{ + action::{ + ActionName, + BridgeLockAction, + BridgeSudoChangeAction, + BridgeUnlockAction, + FeeAssetChangeAction, + FeeChangeAction, + IbcRelayerChangeAction, + Ics20Withdrawal, + InitBridgeAccountAction, + SequenceAction, + SudoAddressChangeAction, + TransferAction, + ValidatorUpdate, + }, + Action, +}; + +trait BelongsToGroup { + const GROUP: ActionGroup; +} + +macro_rules! impl_belong_to_group { + ($(($act:ty, $group:expr)),*$(,)?) => { + $( + impl BelongsToGroup for $act { + const GROUP: ActionGroup = $group; + } + )* + } +} + +impl_belong_to_group!( + (SequenceAction, ActionGroup::General), + (TransferAction, ActionGroup::General), + (ValidatorUpdate, ActionGroup::General), + (SudoAddressChangeAction, ActionGroup::UnbundleableSudo), + (IbcRelayerChangeAction, ActionGroup::Sudo), + (Ics20Withdrawal, ActionGroup::General), + (InitBridgeAccountAction, ActionGroup::UnbundleableGeneral), + (BridgeLockAction, ActionGroup::General), + (BridgeUnlockAction, ActionGroup::General), + (BridgeSudoChangeAction, ActionGroup::UnbundleableGeneral), + (FeeChangeAction, ActionGroup::Sudo), + (FeeAssetChangeAction, ActionGroup::Sudo), + (IbcRelay, ActionGroup::General), +); + +trait Group { + fn group(&self) -> ActionGroup; +} + +impl Group for Action { + fn group(&self) -> ActionGroup { + match self { + Action::Sequence(_) => SequenceAction::GROUP, + Action::Transfer(_) => TransferAction::GROUP, + Action::ValidatorUpdate(_) => ValidatorUpdate::GROUP, + Action::SudoAddressChange(_) => SudoAddressChangeAction::GROUP, + Action::IbcRelayerChange(_) => IbcRelayerChangeAction::GROUP, + Action::Ics20Withdrawal(_) => Ics20Withdrawal::GROUP, + Action::InitBridgeAccount(_) => InitBridgeAccountAction::GROUP, + Action::BridgeLock(_) => BridgeLockAction::GROUP, + Action::BridgeUnlock(_) => BridgeUnlockAction::GROUP, + Action::BridgeSudoChange(_) => BridgeSudoChangeAction::GROUP, + Action::FeeChange(_) => FeeChangeAction::GROUP, + Action::FeeAssetChange(_) => FeeAssetChangeAction::GROUP, + Action::Ibc(_) => IbcRelay::GROUP, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(super) enum ActionGroup { + General, + UnbundleableGeneral, + Sudo, + UnbundleableSudo, +} + +impl ActionGroup { + pub(super) fn is_bundleable(self) -> bool { + matches!(self, ActionGroup::General | ActionGroup::Sudo) + } + + pub(super) fn is_sudo(self) -> bool { + matches!(self, ActionGroup::Sudo) + } +} + +impl fmt::Display for ActionGroup { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ActionGroup::General => write!(f, "general"), + ActionGroup::UnbundleableGeneral => write!(f, "unbundleable general"), + ActionGroup::Sudo => write!(f, "sudo"), + ActionGroup::UnbundleableSudo => write!(f, "unbundleable sudo"), + } + } +} + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct Error(ErrorKind); + +impl Error { + fn mixed(original_group: ActionGroup, additional_group: ActionGroup, action: String) -> Self { + Self(ErrorKind::Mixed { + original_group, + additional_group, + action, + }) + } + + fn not_bundleable(group: ActionGroup) -> Self { + Self(ErrorKind::NotBundleable { + group, + }) + } +} + +#[derive(Debug, thiserror::Error)] +enum ErrorKind { + #[error( + "input contains mixed `ActionGroup` types. original group: {original_group}, additional \ + group: {additional_group}, triggering action: {action}" + )] + Mixed { + original_group: ActionGroup, + additional_group: ActionGroup, + action: String, + }, + #[error("attempted to create bundle with non bundleable `ActionGroup` type: {group}")] + NotBundleable { group: ActionGroup }, +} + +/// Invariants: `group` is set if `inner` is not empty. +#[derive(Clone, Debug)] +pub(super) struct Actions { + inner: Vec, +} + +impl Actions { + pub(super) fn actions(&self) -> &[Action] { + &self.inner + } + + #[must_use] + pub(super) fn into_actions(self) -> Vec { + self.inner + } + + pub(super) fn group(&self) -> Option { + self.inner.first().map(Group::group) + } + + fn default() -> Self { + Self { + inner: vec![], + } + } + + pub(super) fn try_from_list_of_actions(actions: Vec) -> Result { + let mut actions_iter = actions.iter(); + let group = match actions_iter.next() { + Some(action) => action.group(), + None => { + // empty `actions`, so invariants met + return Ok(Self::default()); + } + }; + + // assert size constraints on non-bundleable action groups + if actions.len() > 1 && !group.is_bundleable() { + return Err(Error::not_bundleable(group)); + } + + // assert the rest of the actions have the same group as the first + for action in actions_iter { + if action.group() != group { + return Err(Error::mixed( + group, + action.group(), + action.name().to_string(), + )); + } + } + + Ok(Self { + inner: actions, + }) + } +} diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs new file mode 100644 index 0000000000..ff963f5220 --- /dev/null +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs @@ -0,0 +1,234 @@ +use ibc_types::core::client::Height; + +use crate::{ + crypto::VerificationKey, + primitive::v1::{ + asset::Denom, + Address, + RollupId, + }, + protocol::transaction::v1alpha1::{ + action::{ + Action, + BridgeLockAction, + BridgeSudoChangeAction, + BridgeUnlockAction, + FeeAssetChangeAction, + FeeChange, + FeeChangeAction, + IbcRelayerChangeAction, + Ics20Withdrawal, + InitBridgeAccountAction, + SequenceAction, + SudoAddressChangeAction, + TransferAction, + ValidatorUpdate, + }, + action_group::{ + ActionGroup, + Actions, + }, + }, +}; +const ASTRIA_ADDRESS_PREFIX: &str = "astria"; + +#[test] +fn try_from_list_of_actions_bundleable_general() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + let actions = vec![ + Action::Sequence(SequenceAction { + rollup_id: RollupId::from([8; 32]), + data: vec![].into(), + fee_asset: asset.clone(), + }), + Action::Transfer(TransferAction { + to: address, + amount: 100, + asset: asset.clone(), + fee_asset: asset.clone(), + }), + Action::BridgeLock(BridgeLockAction { + to: address, + amount: 100, + asset: asset.clone(), + fee_asset: asset.clone(), + destination_chain_address: String::new(), + }), + Action::BridgeUnlock(BridgeUnlockAction { + to: address, + amount: 100, + fee_asset: asset.clone(), + bridge_address: address, + memo: String::new(), + rollup_block_number: 0, + rollup_withdrawal_event_id: String::new(), + }), + Action::ValidatorUpdate(ValidatorUpdate { + power: 100, + verification_key: VerificationKey::try_from([0; 32]).unwrap(), + }), + Action::Ics20Withdrawal(Ics20Withdrawal { + denom: asset.clone(), + destination_chain_address: String::new(), + return_address: address, + amount: 1_000_000u128, + memo: String::new(), + fee_asset: asset.clone(), + timeout_height: Height::new(1, 1).unwrap(), + timeout_time: 0, + source_channel: "channel-0".parse().unwrap(), + bridge_address: Some(address), + use_compat_address: false, + }), + ]; + + assert!(matches!( + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::General) + )); +} + +#[test] +fn from_list_of_actions_bundleable_sudo() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + let actions = vec![ + Action::FeeChange(FeeChangeAction { + fee_change: FeeChange::TransferBaseFee, + new_value: 100, + }), + Action::FeeAssetChange(FeeAssetChangeAction::Addition(asset)), + Action::IbcRelayerChange(IbcRelayerChangeAction::Addition(address)), + ]; + + assert!(matches!( + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::Sudo) + )); +} + +#[test] +fn from_list_of_actions_sudo() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let actions = vec![Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + })]; + + assert!(matches!( + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::UnbundleableSudo) + )); + + let actions = vec![ + Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + }), + Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + }), + ]; + + assert_eq!( + Actions::try_from_list_of_actions(actions) + .unwrap_err() + .to_string(), + "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable sudo" + ); +} + +#[test] +fn from_list_of_actions_general() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + + let init_bridge_account_action = InitBridgeAccountAction { + rollup_id: RollupId::from([8; 32]), + asset: asset.clone(), + fee_asset: asset.clone(), + sudo_address: Some(address), + withdrawer_address: Some(address), + }; + + let sudo_bridge_address_change_action = BridgeSudoChangeAction { + new_sudo_address: Some(address), + bridge_address: address, + new_withdrawer_address: Some(address), + fee_asset: asset.clone(), + }; + + let actions = vec![init_bridge_account_action.clone().into()]; + + assert!(matches!( + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::UnbundleableGeneral) + )); + + let actions = vec![sudo_bridge_address_change_action.clone().into()]; + + assert!(matches!( + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::UnbundleableGeneral) + )); + + let actions = vec![ + init_bridge_account_action.into(), + sudo_bridge_address_change_action.into(), + ]; + + assert_eq!( + Actions::try_from_list_of_actions(actions) + .unwrap_err() + .to_string(), + "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" + ); +} + +#[test] +fn from_list_of_actions_mixed() { + let address: Address<_> = Address::builder() + .array([0; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + + let asset: Denom = "nria".parse().unwrap(); + let actions = vec![ + Action::Sequence(SequenceAction { + rollup_id: RollupId::from([8; 32]), + data: vec![].into(), + fee_asset: asset.clone(), + }), + Action::SudoAddressChange(SudoAddressChangeAction { + new_address: address, + }), + ]; + + assert_eq!( + Actions::try_from_list_of_actions(actions) + .unwrap_err() + .to_string(), + "input contains mixed `ActionGroup` types. original group: general, additional group: \ + unbundleable sudo, triggering action: SudoAddressChange" + ); +} diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index 7bffbd3e6e..898f5cd23d 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -1,7 +1,4 @@ -use action_group::{ - ActionGroup, - Actions, -}; +use action_group::Actions; use bytes::Bytes; use prost::{ Message as _, @@ -207,8 +204,12 @@ impl SignedTransaction { } #[must_use] - pub fn group(&self) -> Option { - self.transaction.actions.group() + pub fn is_sudo_action_group(&self) -> bool { + if let Some(group) = self.transaction.actions.group() { + group.is_sudo() + } else { + false + } } #[must_use] diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 999db0407c..842f8f04cb 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -25,7 +25,6 @@ use astria_core::{ genesis::v1alpha1::GenesisAppState, transaction::v1alpha1::{ action::ValidatorUpdate, - action_group::ActionGroup, Action, SignedTransaction, }, @@ -1025,7 +1024,7 @@ impl App { // flag mempool for cleaning if we ran a fee change action self.recost_mempool = self.recost_mempool - || matches!(signed_tx.group(), Some(ActionGroup::Sudo)) + || signed_tx.is_sudo_action_group() && signed_tx .actions() .iter() diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 20ef3d5c5b..778b6d3e52 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -237,7 +237,7 @@ pub(crate) fn mock_tx( .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); Arc::new(tx.into_signed(signer)) } diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index 8a9d011172..059529aa0d 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -63,7 +63,7 @@ async fn trigger_cleaning() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&get_judy_signing_key()); app.mempool @@ -162,7 +162,7 @@ async fn do_not_trigger_cleaning() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&get_alice_signing_key()); app.mempool @@ -238,7 +238,7 @@ async fn maintenance_recosting_promotes() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&get_bob_signing_key()); let mut bob_funds = HashMap::new(); @@ -269,7 +269,7 @@ async fn maintenance_recosting_promotes() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&get_judy_signing_key()); let mut judy_funds = HashMap::new(); @@ -413,7 +413,7 @@ async fn maintenance_funds_added_promotes() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&get_carol_signing_key()); let mut carol_funds = HashMap::new(); @@ -446,7 +446,7 @@ async fn maintenance_funds_added_promotes() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&get_alice_signing_key()); let mut alice_funds = HashMap::new(); diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 1f3dc03f8e..83fe85c1eb 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -246,7 +246,7 @@ async fn app_transfer_block_fees_to_sudo() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -330,7 +330,7 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -425,7 +425,7 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -570,7 +570,7 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&alice); let tx_overflow = UnsignedTransaction::new( @@ -587,7 +587,7 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&alice); app.mempool @@ -665,7 +665,7 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&alice); let tx_overflow = UnsignedTransaction::new( @@ -682,7 +682,7 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(&alice); app.mempool diff --git a/crates/astria-sequencer/src/app/tests_block_fees.rs b/crates/astria-sequencer/src/app/tests_block_fees.rs index 415c782b9a..2463da510d 100644 --- a/crates/astria-sequencer/src/app/tests_block_fees.rs +++ b/crates/astria-sequencer/src/app/tests_block_fees.rs @@ -121,7 +121,7 @@ async fn ensure_correct_block_fees_transfer() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -163,7 +163,7 @@ async fn ensure_correct_block_fees_sequence() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -207,7 +207,7 @@ async fn ensure_correct_block_fees_init_bridge_acct() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -262,7 +262,7 @@ async fn ensure_correct_block_fees_bridge_lock() { .chain_id("test") .build(), ) - .expect("can build unsigned transaction"); + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 5b7feeacde..adc3088b2f 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -475,8 +475,7 @@ async fn app_transaction_bundle_categories() { ) .unwrap_err() .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'UnbundleableSudo' is not bundleable" + "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable sudo" ); assert_eq!( @@ -506,8 +505,7 @@ async fn app_transaction_bundle_categories() { ) .unwrap_err() .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'UnbundleableGeneral' is not bundleable" + "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" ); assert_eq!( @@ -535,7 +533,6 @@ async fn app_transaction_bundle_categories() { ) .unwrap_err() .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: ActionGroup type \ - 'UnbundleableGeneral' is not bundleable" + "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" ); } diff --git a/crates/astria-sequencer/src/benchmark_utils.rs b/crates/astria-sequencer/src/benchmark_utils.rs index 02430dd7d3..6ccd241bd0 100644 --- a/crates/astria-sequencer/src/benchmark_utils.rs +++ b/crates/astria-sequencer/src/benchmark_utils.rs @@ -125,7 +125,7 @@ fn transfers() -> Vec> { .collect(), params, ) - .expect("can build unsigned transaction") + .unwrap() .into_signed(sender); Arc::new(tx) }) From a6f91a9329145ab86ac13a96b5ea117ce4ac53af Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Thu, 26 Sep 2024 07:37:26 -0400 Subject: [PATCH 07/18] switch to combined builder for UnsignedTransaction and TransactionParams --- .../src/bridge_withdrawer/submitter/mod.rs | 15 +- .../astria-cli/src/commands/bridge/submit.rs | 17 +- crates/astria-cli/src/commands/sequencer.rs | 17 +- .../src/executor/bundle_factory/mod.rs | 16 +- .../src/executor/bundle_factory/tests.rs | 7 +- crates/astria-composer/src/executor/mod.rs | 13 +- crates/astria-core/src/protocol/test_utils.rs | 27 +- .../src/protocol/transaction/v1alpha1/mod.rs | 113 ++--- .../astria-sequencer-client/src/tests/http.rs | 17 +- crates/astria-sequencer/src/app/test_utils.rs | 17 +- .../src/app/tests_app/mempool.rs | 103 ++--- .../astria-sequencer/src/app/tests_app/mod.rs | 115 ++--- .../src/app/tests_block_fees.rs | 81 ++-- .../src/app/tests_breaking_changes.rs | 193 ++++----- .../src/app/tests_execute_transaction.rs | 408 +++++++----------- .../astria-sequencer/src/benchmark_utils.rs | 31 +- .../src/proposal/commitment.rs | 40 +- .../astria-sequencer/src/service/consensus.rs | 16 +- .../src/transaction/checks.rs | 25 +- 19 files changed, 531 insertions(+), 740 deletions(-) diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs index 64b9014bd0..a768873c5e 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs @@ -13,7 +13,6 @@ use astria_core::{ }, protocol::transaction::v1alpha1::{ Action, - TransactionParams, UnsignedTransaction, }, }; @@ -154,14 +153,12 @@ impl Submitter { .wrap_err("failed to get nonce from sequencer")?; debug!(nonce, "fetched latest nonce"); - let unsigned = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(nonce) - .chain_id(sequencer_chain_id) - .build(), - ) - .wrap_err("failed to build unsigned transaction")?; + let unsigned = UnsignedTransaction::builder() + .actions(actions) + .nonce(nonce) + .chain_id(sequencer_chain_id) + .try_build() + .wrap_err("failed to build unsigned transaction")?; // sign transaction let signed = unsigned.into_signed(signer.signing_key()); diff --git a/crates/astria-cli/src/commands/bridge/submit.rs b/crates/astria-cli/src/commands/bridge/submit.rs index f238e8e32a..cd0085b54c 100644 --- a/crates/astria-cli/src/commands/bridge/submit.rs +++ b/crates/astria-cli/src/commands/bridge/submit.rs @@ -7,7 +7,6 @@ use astria_core::{ crypto::SigningKey, protocol::transaction::v1alpha1::{ Action, - TransactionParams, UnsignedTransaction, }, }; @@ -129,15 +128,13 @@ async fn submit_transaction( .await .wrap_err("failed to get nonce")?; - let tx = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(nonce_res.nonce) - .chain_id(chain_id) - .build(), - ) - .wrap_err("failed to build transaction from actions")? - .into_signed(signing_key); + let tx = UnsignedTransaction::builder() + .actions(actions) + .nonce(nonce_res.nonce) + .chain_id(chain_id) + .try_build() + .wrap_err("failed to build transaction from actions")? + .into_signed(signing_key); let res = client .submit_transaction_sync(tx) .await diff --git a/crates/astria-cli/src/commands/sequencer.rs b/crates/astria-cli/src/commands/sequencer.rs index d68eb76d55..c4849282fc 100644 --- a/crates/astria-cli/src/commands/sequencer.rs +++ b/crates/astria-cli/src/commands/sequencer.rs @@ -16,7 +16,6 @@ use astria_core::{ TransferAction, ValidatorUpdate, }, - TransactionParams, UnsignedTransaction, }, }; @@ -475,15 +474,13 @@ async fn submit_transaction( .await .wrap_err("failed to get nonce")?; - let tx = UnsignedTransaction::new( - vec![action], - TransactionParams::builder() - .nonce(nonce_res.nonce) - .chain_id(chain_id) - .build(), - ) - .wrap_err("failed to build transaction from actions")? - .into_signed(&sequencer_key); + let tx = UnsignedTransaction::builder() + .actions(vec![action]) + .nonce(nonce_res.nonce) + .chain_id(chain_id) + .try_build() + .wrap_err("failed to build transaction from actions")? + .into_signed(&sequencer_key); let res = sequencer_client .submit_transaction_sync(tx) .await diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index 8eda56aa6b..0fc824d7be 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -13,7 +13,6 @@ use astria_core::{ protocol::transaction::v1alpha1::{ action::SequenceAction, Action, - TransactionParams, UnsignedTransaction, }, Protobuf as _, @@ -77,10 +76,19 @@ impl SizedBundle { } #[allow(clippy::panic)] // method is expected to never panic because only `SequenceActions` are - /// added to the bundle, which should produce a valid variant of the `ActionGroup` type + /// added to the bundle, which should produce a valid variant of the `ActionGroup` type. /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. - pub(super) fn to_unsigned_transaction(&self, params: TransactionParams) -> UnsignedTransaction { - UnsignedTransaction::new(self.buffer.clone(), params).unwrap() + pub(super) fn to_unsigned_transaction( + &self, + nonce: u32, + chain_id: &str, + ) -> UnsignedTransaction { + UnsignedTransaction::builder() + .actions(self.buffer.clone()) + .chain_id(chain_id) + .nonce(nonce) + .try_build() + .unwrap() } /// Buffer `seq_action` into the bundle. diff --git a/crates/astria-composer/src/executor/bundle_factory/tests.rs b/crates/astria-composer/src/executor/bundle_factory/tests.rs index e70742bbd5..fc6f543b82 100644 --- a/crates/astria-composer/src/executor/bundle_factory/tests.rs +++ b/crates/astria-composer/src/executor/bundle_factory/tests.rs @@ -83,7 +83,6 @@ mod sized_bundle { #[cfg(test)] mod bundle_factory { - use astria_core::protocol::transaction::v1alpha1::TransactionParams; use super::*; use crate::{ @@ -349,12 +348,8 @@ mod bundle_factory { let bundle = bundle_factory.pop_now(); - let transaction_params = TransactionParams::builder() - .chain_id("astria-testnet-1".to_string()) - .build(); - // construction of multiple sequence actions should not panic - let unsigned_tx = bundle.to_unsigned_transaction(transaction_params); + let unsigned_tx = bundle.to_unsigned_transaction(0, "astria-testnet-1"); assert_eq!(unsigned_tx.actions().len(), 2); } diff --git a/crates/astria-composer/src/executor/mod.rs b/crates/astria-composer/src/executor/mod.rs index ab35740420..41cd8ed2a1 100644 --- a/crates/astria-composer/src/executor/mod.rs +++ b/crates/astria-composer/src/executor/mod.rs @@ -17,7 +17,6 @@ use astria_core::{ transaction::v1alpha1::{ action::SequenceAction, SignedTransaction, - TransactionParams, }, }, }; @@ -676,13 +675,9 @@ impl Future for SubmitFut { let new_state = match this.state.project() { SubmitStateProj::NotStarted => { - let params = TransactionParams::builder() - .nonce(*this.nonce) - .chain_id(&*this.chain_id) - .build(); let tx = this .bundle - .to_unsigned_transaction(params) + .to_unsigned_transaction(*this.nonce, &*this.chain_id) .into_signed(this.signing_key); info!( nonce.actual = *this.nonce, @@ -753,13 +748,9 @@ impl Future for SubmitFut { } => match ready!(fut.poll(cx)) { Ok(nonce) => { *this.nonce = nonce; - let params = TransactionParams::builder() - .nonce(*this.nonce) - .chain_id(&*this.chain_id) - .build(); let tx = this .bundle - .to_unsigned_transaction(params) + .to_unsigned_transaction(*this.nonce, &*this.chain_id) .into_signed(this.signing_key); info!( nonce.resubmission = *this.nonce, diff --git a/crates/astria-core/src/protocol/test_utils.rs b/crates/astria-core/src/protocol/test_utils.rs index 12a7cbea31..c7a0fc06b6 100644 --- a/crates/astria-core/src/protocol/test_utils.rs +++ b/crates/astria-core/src/protocol/test_utils.rs @@ -7,11 +7,7 @@ use prost::Message as _; use super::{ group_sequence_actions_in_signed_transaction_transactions_by_rollup_id, - transaction::v1alpha1::{ - action::SequenceAction, - TransactionParams, - UnsignedTransaction, - }, + transaction::v1alpha1::action::SequenceAction, }; use crate::{ crypto::SigningKey, @@ -19,6 +15,7 @@ use crate::{ derive_merkle_tree_from_rollup_txs, RollupId, }, + protocol::transaction::v1alpha1::UnsignedTransaction, sequencerblock::v1alpha1::{ block::Deposit, SequencerBlock, @@ -104,17 +101,15 @@ impl ConfigureSequencerBlock { let txs = if actions.is_empty() { vec![] } else { - let unsigned_transaction = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(1) - .chain_id(chain_id.clone()) - .build(), - ) - .expect( - "should be able to build unsigned transaction since only sequence actions are \ - contained", - ); + let unsigned_transaction = UnsignedTransaction::builder() + .actions(actions) + .chain_id(chain_id.clone()) + .nonce(1) + .try_build() + .expect( + "should be able to build unsigned transaction since only sequence actions are \ + contained", + ); vec![unsigned_transaction.into_signed(&signing_key)] }; let mut deposits_map: HashMap> = HashMap::new(); diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index 898f5cd23d..d16b080817 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -245,20 +245,9 @@ pub struct UnsignedTransaction { } impl UnsignedTransaction { - /// Creates the `UnsignedTransaction` from the actions and params. - /// - /// # Errors - /// - /// This function will return an error if the actions list contains actions - /// of different `ActionGroup` types or violated an `ActionGroup`'s bundling constraints. - pub fn new( - actions: Vec, - params: TransactionParams, - ) -> Result { - Ok(Self { - actions: Actions::try_from_list_of_actions(actions)?, - params, - }) + #[must_use] + pub fn builder() -> UnsignedTransactionBuilder { + UnsignedTransactionBuilder::new() } #[must_use] @@ -358,7 +347,12 @@ impl UnsignedTransaction { .collect::>() .map_err(UnsignedTransactionError::action)?; - UnsignedTransaction::new(actions, params).map_err(UnsignedTransactionError::action_group) + UnsignedTransaction::builder() + .actions(actions) + .chain_id(params.chain_id) + .nonce(params.nonce) + .try_build() + .map_err(UnsignedTransactionError::action_group) } /// Attempt to convert from a protobuf [`pbjson_types::Any`]. @@ -427,33 +421,46 @@ enum UnsignedTransactionErrorKind { ActionGroup(#[source] action_group::Error), } -pub struct TransactionParamsBuilder> { +pub struct UnsignedTransactionBuilder> { nonce: u32, chain_id: TChainId, + actions: Vec, } -impl TransactionParamsBuilder { - fn new() -> Self { +impl UnsignedTransactionBuilder { + #[must_use] + #[allow(clippy::new_without_default)] + pub fn new() -> Self { Self { nonce: 0, chain_id: "".into(), + actions: vec![], } } } -impl TransactionParamsBuilder { - #[must_use = "the transaction params builder must be built to be useful"] +impl UnsignedTransactionBuilder { + #[must_use] + pub fn actions(self, actions: Vec) -> Self { + Self { + actions, + ..self + } + } + + #[must_use] pub fn chain_id<'a, T: Into>>( self, chain_id: T, - ) -> TransactionParamsBuilder> { - TransactionParamsBuilder { + ) -> UnsignedTransactionBuilder> { + UnsignedTransactionBuilder { chain_id: chain_id.into(), nonce: self.nonce, + actions: self.actions, } } - #[must_use = "the transaction params builder must be built to be useful"] + #[must_use] pub fn nonce(self, nonce: u32) -> Self { Self { nonce, @@ -462,22 +469,28 @@ impl TransactionParamsBuilder { } } -impl<'a> TransactionParamsBuilder> { - /// Constructs a [`TransactionParams`] from the configured builder. +impl<'a> UnsignedTransactionBuilder> { + /// Constructs a [`UnsignedTransaction`] from the configured builder. /// /// # Errors + /// Returns an error if the actions do not make a valid `ActionGroup`. + /// /// Returns an error if the set chain ID does not contain a chain name that can be turned into /// a bech32 human readable prefix (everything before the first dash i.e. `-`). - #[must_use] - pub fn build(self) -> TransactionParams { + pub fn try_build(self) -> Result { let Self { nonce, chain_id, + actions, } = self; - TransactionParams { - nonce, - chain_id: chain_id.into(), - } + let actions = Actions::try_from_list_of_actions(actions)?; + Ok(UnsignedTransaction { + actions, + params: TransactionParams { + nonce, + chain_id: chain_id.into(), + }, + }) } } @@ -488,11 +501,6 @@ pub struct TransactionParams { } impl TransactionParams { - #[must_use = "the transaction params builder must be built to be useful"] - pub fn builder() -> TransactionParamsBuilder { - TransactionParamsBuilder::new() - } - #[must_use] pub fn into_raw(self) -> raw::TransactionParams { let Self { @@ -507,16 +515,17 @@ impl TransactionParams { } /// Convert from a raw protobuf [`raw::UnsignedTransaction`]. - /// - /// # Errors - /// See [`TransactionParamsBuilder::try_build`] for errors returned by this method. #[must_use] pub fn from_raw(proto: raw::TransactionParams) -> Self { let raw::TransactionParams { nonce, chain_id, } = proto; - Self::builder().nonce(nonce).chain_id(chain_id).build() + + Self { + nonce, + chain_id, + } } } @@ -635,12 +644,12 @@ mod test { fee_asset: asset(), }; - let params = TransactionParams::from_raw(raw::TransactionParams { - nonce: 1, - chain_id: "test-1".to_string(), - }); - - let unsigned = UnsignedTransaction::new(vec![transfer.into()], params).unwrap(); + let unsigned = UnsignedTransaction::builder() + .actions(vec![transfer.into()]) + .chain_id("test-1".to_string()) + .nonce(1) + .try_build() + .unwrap(); let tx = SignedTransaction { signature, @@ -670,12 +679,12 @@ mod test { fee_asset: asset(), }; - let params = TransactionParams::from_raw(raw::TransactionParams { - nonce: 1, - chain_id: "test-1".to_string(), - }); - - let unsigned_tx = UnsignedTransaction::new(vec![transfer.into()], params).unwrap(); + let unsigned_tx = UnsignedTransaction::builder() + .actions(vec![transfer.into()]) + .chain_id("test-1".to_string()) + .nonce(1) + .try_build() + .unwrap(); let signed_tx = unsigned_tx.into_signed(&signing_key); let raw = signed_tx.to_raw(); diff --git a/crates/astria-sequencer-client/src/tests/http.rs b/crates/astria-sequencer-client/src/tests/http.rs index ffa5490214..c23f14b1d1 100644 --- a/crates/astria-sequencer-client/src/tests/http.rs +++ b/crates/astria-sequencer-client/src/tests/http.rs @@ -7,7 +7,6 @@ use astria_core::{ protocol::transaction::v1alpha1::{ action::TransferAction, SignedTransaction, - TransactionParams, UnsignedTransaction, }, }; @@ -157,15 +156,13 @@ fn create_signed_transaction() -> SignedTransaction { } .into(), ]; - UnsignedTransaction::new( - actions, - TransactionParams::builder() - .chain_id("test") - .nonce(1) - .build(), - ) - .unwrap() - .into_signed(&alice_key) + UnsignedTransaction::builder() + .actions(actions) + .chain_id("test") + .nonce(1) + .try_build() + .unwrap() + .into_signed(&alice_key) } #[tokio::test] diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 778b6d3e52..fcef1cb703 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -24,7 +24,6 @@ use astria_core::{ ValidatorUpdate, }, SignedTransaction, - TransactionParams, UnsignedTransaction, }, }, @@ -223,21 +222,19 @@ pub(crate) fn mock_tx( signer: &SigningKey, rollup_name: &str, ) -> Arc { - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(rollup_name.as_bytes()), data: Bytes::from_static(&[0x99]), fee_asset: denom_0(), } .into(), - ], - TransactionParams::builder() - .nonce(nonce) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .nonce(nonce) + .try_build() + .unwrap(); Arc::new(tx.into_signed(signer)) } diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index 059529aa0d..f0ba4820d4 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -9,7 +9,6 @@ use astria_core::{ FeeChangeAction, TransferAction, }, - TransactionParams, UnsignedTransaction, }, }, @@ -50,21 +49,18 @@ async fn trigger_cleaning() { app.commit(storage.clone()).await; // create tx which will cause mempool cleaning flag to be set - let tx_trigger = UnsignedTransaction::new( - vec![ + let tx_trigger = UnsignedTransaction::builder() + .actions(vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&get_judy_signing_key()); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&get_judy_signing_key()); app.mempool .insert( @@ -149,21 +145,18 @@ async fn do_not_trigger_cleaning() { // create tx which will fail execution and not trigger flag // (wrong sudo signer) - let tx_fail = UnsignedTransaction::new( - vec![ + let tx_fail = UnsignedTransaction::builder() + .actions(vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&get_alice_signing_key()); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&get_alice_signing_key()); app.mempool .insert( @@ -223,8 +216,8 @@ async fn maintenance_recosting_promotes() { // create tx which will not be included in block due to // having insufficient funds (transaction will be recosted to enable) - let tx_fail_recost_funds = UnsignedTransaction::new( - vec![ + let tx_fail_recost_funds = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: astria_address_from_hex_string(CAROL_ADDRESS), amount: 1u128, @@ -232,14 +225,11 @@ async fn maintenance_recosting_promotes() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&get_bob_signing_key()); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&get_bob_signing_key()); let mut bob_funds = HashMap::new(); bob_funds.insert(nria().into(), 11); @@ -256,21 +246,18 @@ async fn maintenance_recosting_promotes() { .unwrap(); // create tx which will enable recost tx to pass - let tx_recost = UnsignedTransaction::new( - vec![ + let tx_recost = UnsignedTransaction::builder() + .actions(vec![ FeeChangeAction { fee_change: FeeChange::TransferBaseFee, new_value: 10, // originally 12 } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&get_judy_signing_key()); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&get_judy_signing_key()); let mut judy_funds = HashMap::new(); judy_funds.insert(nria().into(), 0); @@ -398,8 +385,8 @@ async fn maintenance_funds_added_promotes() { // create tx that will not be included in block due to // having no funds (will be sent transfer to then enable) - let tx_fail_transfer_funds = UnsignedTransaction::new( - vec![ + let tx_fail_transfer_funds = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: astria_address_from_hex_string(BOB_ADDRESS), amount: 10u128, @@ -407,14 +394,11 @@ async fn maintenance_funds_added_promotes() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&get_carol_signing_key()); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&get_carol_signing_key()); let mut carol_funds = HashMap::new(); carol_funds.insert(nria().into(), 0); @@ -431,8 +415,8 @@ async fn maintenance_funds_added_promotes() { .unwrap(); // create tx which will enable no funds to pass - let tx_fund = UnsignedTransaction::new( - vec![ + let tx_fund = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: astria_address_from_hex_string(CAROL_ADDRESS), amount: 22u128, @@ -440,14 +424,11 @@ async fn maintenance_funds_added_promotes() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&get_alice_signing_key()); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&get_alice_signing_key()); let mut alice_funds = HashMap::new(); alice_funds.insert(nria().into(), 100); diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 83fe85c1eb..1f6523c156 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -15,7 +15,6 @@ use astria_core::{ SequenceAction, TransferAction, }, - TransactionParams, UnsignedTransaction, }, }, @@ -231,8 +230,8 @@ async fn app_transfer_block_fees_to_sudo() { // transfer funds from Alice to Bob; use native token for fee payment let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let amount = 333_333; - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount, @@ -240,13 +239,10 @@ async fn app_transfer_block_fees_to_sudo() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -323,14 +319,11 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { fee_asset: nria().into(), }; - let tx = UnsignedTransaction::new( - vec![lock_action.into(), sequence_action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![lock_action.into(), sequence_action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -418,14 +411,11 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { fee_asset: nria().into(), }; - let tx = UnsignedTransaction::new( - vec![lock_action.into(), sequence_action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![lock_action.into(), sequence_action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -556,39 +546,34 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { // create txs which will cause cometBFT overflow let alice = get_alice_signing_key(); - let tx_pass = UnsignedTransaction::new( - vec![ + let tx_pass = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&alice); - - let tx_overflow = UnsignedTransaction::new( - vec![ + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&alice); + + let tx_overflow = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&alice); + ]) + .chain_id("test") + .nonce(1) + .try_build() + .unwrap() + .into_signed(&alice); app.mempool .insert( @@ -651,39 +636,33 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { // create txs which will cause sequencer overflow (max is currently 256_000 bytes) let alice = get_alice_signing_key(); - let tx_pass = UnsignedTransaction::new( - vec![ + let tx_pass = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 200_000]), fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&alice); - - let tx_overflow = UnsignedTransaction::new( - vec![ + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&alice); + let tx_overflow = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from([1u8; 32]), data: Bytes::copy_from_slice(&[1u8; 100_000]), fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&alice); + ]) + .nonce(1) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&alice); app.mempool .insert( diff --git a/crates/astria-sequencer/src/app/tests_block_fees.rs b/crates/astria-sequencer/src/app/tests_block_fees.rs index 2463da510d..b6108b925a 100644 --- a/crates/astria-sequencer/src/app/tests_block_fees.rs +++ b/crates/astria-sequencer/src/app/tests_block_fees.rs @@ -10,7 +10,6 @@ use astria_core::{ SequenceAction, TransferAction, }, - TransactionParams, UnsignedTransaction, }, sequencerblock::v1alpha1::block::Deposit, @@ -53,8 +52,8 @@ async fn transaction_execution_records_fee_event() { let alice = get_alice_signing_key(); let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let value = 333_333; - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: value, @@ -62,13 +61,10 @@ async fn transaction_execution_records_fee_event() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let events = app.execute_transaction(signed_tx).await.unwrap(); @@ -114,14 +110,11 @@ async fn ensure_correct_block_fees_transfer() { .into(), ]; - let tx = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(actions) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -156,14 +149,11 @@ async fn ensure_correct_block_fees_sequence() { .into(), ]; - let tx = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(actions) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -200,14 +190,11 @@ async fn ensure_correct_block_fees_init_bridge_acct() { .into(), ]; - let tx = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(actions) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -255,14 +242,11 @@ async fn ensure_correct_block_fees_bridge_lock() { .into(), ]; - let tx = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(actions) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); @@ -318,14 +302,11 @@ async fn ensure_correct_block_fees_bridge_sudo_change() { .into(), ]; - let tx = UnsignedTransaction::new( - actions, - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(actions) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index adc3088b2f..d0996c47c0 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -32,7 +32,6 @@ use astria_core::{ ValidatorUpdate, }, Action, - TransactionParams, UnsignedTransaction, }, }, @@ -116,14 +115,11 @@ async fn app_finalize_block_snapshot() { fee_asset: nria().into(), }; - let tx = UnsignedTransaction::new( - vec![lock_action.into(), sequence_action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![lock_action.into(), sequence_action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&alice); @@ -207,8 +203,8 @@ async fn app_execute_transaction_with_every_action_snapshot() { let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); - let tx_bundleable_general = UnsignedTransaction::new( - vec![ + let tx_bundleable_general = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: 333_333, @@ -223,43 +219,36 @@ async fn app_execute_transaction_with_every_action_snapshot() { } .into(), Action::ValidatorUpdate(update.clone()), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); - let tx_bundleable_sudo = UnsignedTransaction::new( - vec![ + let tx_bundleable_sudo = UnsignedTransaction::builder() + .actions(vec![ IbcRelayerChangeAction::Addition(bob_address).into(), IbcRelayerChangeAction::Addition(carol_address).into(), IbcRelayerChangeAction::Removal(bob_address).into(), FeeAssetChangeAction::Addition("test-0".parse().unwrap()).into(), FeeAssetChangeAction::Addition("test-1".parse().unwrap()).into(), FeeAssetChangeAction::Removal("test-0".parse().unwrap()).into(), - ], - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .nonce(1) + .chain_id("test") + .try_build() + .unwrap(); - let tx_sudo = UnsignedTransaction::new( - vec![ + let tx_sudo = UnsignedTransaction::builder() + .actions(vec![ SudoAddressChangeAction { new_address: bob_address, } .into(), - ], - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx_general_bundleable = Arc::new(tx_bundleable_general.into_signed(&alice)); app.execute_transaction(signed_tx_general_bundleable) @@ -274,8 +263,8 @@ async fn app_execute_transaction_with_every_action_snapshot() { let signed_tx_sudo = Arc::new(tx_sudo.into_signed(&alice)); app.execute_transaction(signed_tx_sudo).await.unwrap(); - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ InitBridgeAccountAction { rollup_id, asset: nria().into(), @@ -284,18 +273,15 @@ async fn app_execute_transaction_with_every_action_snapshot() { withdrawer_address: None, } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); - let tx_bridge_bundleable = UnsignedTransaction::new( - vec![ + let tx_bridge_bundleable = UnsignedTransaction::builder() + .actions(vec![ BridgeLockAction { to: bridge_address, amount: 100, @@ -314,19 +300,17 @@ async fn app_execute_transaction_with_every_action_snapshot() { rollup_withdrawal_event_id: "a-rollup-defined-hash".to_string(), } .into(), - ], - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .nonce(1) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx_bridge_bundleable.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); - let tx_bridge = UnsignedTransaction::new( - vec![ + let tx_bridge = UnsignedTransaction::builder() + .actions(vec![ BridgeSudoChangeAction { bridge_address, new_sudo_address: Some(bob_address), @@ -334,13 +318,11 @@ async fn app_execute_transaction_with_every_action_snapshot() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx_bridge.into_signed(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); @@ -375,8 +357,8 @@ async fn app_transaction_bundle_categories() { let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); assert!( - UnsignedTransaction::new( - vec![ + UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: 333_333, @@ -426,19 +408,16 @@ async fn app_transaction_bundle_categories() { use_compat_address: false, } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .is_ok(), + ]) + .chain_id("test") + .try_build() + .is_ok(), "should be able to construct general bundle" ); assert!( - UnsignedTransaction::new( - vec![ + UnsignedTransaction::builder() + .actions(vec![ IbcRelayerChangeAction::Addition(bob_address).into(), FeeAssetChangeAction::Removal("test-0".parse().unwrap()).into(), FeeChangeAction { @@ -446,19 +425,17 @@ async fn app_transaction_bundle_categories() { new_value: 10, } .into(), - ], - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .is_ok(), + ]) + .nonce(1) + .chain_id("test") + .try_build() + .is_ok(), "should be able to construct sudo bundle" ); assert_eq!( - UnsignedTransaction::new( - vec![ + UnsignedTransaction::builder() + .actions(vec![ SudoAddressChangeAction { new_address: bob_address, } @@ -467,20 +444,18 @@ async fn app_transaction_bundle_categories() { new_address: bob_address, } .into(), - ], - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .unwrap_err() - .to_string(), + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap_err() + .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable sudo" ); assert_eq!( - UnsignedTransaction::new( - vec![ + UnsignedTransaction::builder() + .actions(vec![ InitBridgeAccountAction { rollup_id, asset: nria().into(), @@ -497,20 +472,18 @@ async fn app_transaction_bundle_categories() { withdrawer_address: None, } .into(), - ], - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .unwrap_err() - .to_string(), + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap_err() + .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" ); assert_eq!( - UnsignedTransaction::new( - vec![ + UnsignedTransaction::builder() + .actions(vec![ BridgeSudoChangeAction { bridge_address, new_sudo_address: Some(bob_address), @@ -525,14 +498,12 @@ async fn app_transaction_bundle_categories() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(2) - .chain_id("test") - .build(), - ) - .unwrap_err() - .to_string(), + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap_err() + .to_string(), "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" ); } diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index f159fde978..0e6b418445 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -19,7 +19,6 @@ use astria_core::{ ValidatorUpdate, }, Action, - TransactionParams, UnsignedTransaction, }, }, @@ -103,8 +102,8 @@ async fn app_execute_transaction_transfer() { let alice_address = astria_address(&alice.address_bytes()); let bob_address = astria_address_from_hex_string(BOB_ADDRESS); let value = 333_333; - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: value, @@ -112,13 +111,10 @@ async fn app_execute_transaction_transfer() { fee_asset: crate::test_utils::nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -161,8 +157,8 @@ async fn app_execute_transaction_transfer_not_native_token() { // transfer funds from Alice to Bob; use native token for fee payment let bob_address = astria_address_from_hex_string(BOB_ADDRESS); - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob_address, amount: value, @@ -170,13 +166,10 @@ async fn app_execute_transaction_transfer_not_native_token() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -227,8 +220,8 @@ async fn app_execute_transaction_transfer_balance_too_low_for_fee() { let bob = astria_address_from_hex_string(BOB_ADDRESS); // 0-value transfer; only fee is deducted from sender - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: bob, amount: 0, @@ -236,13 +229,10 @@ async fn app_execute_transaction_transfer_balance_too_low_for_fee() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&keypair)); let res = app @@ -269,21 +259,18 @@ async fn app_execute_transaction_sequence() { let data = Bytes::from_static(b"hello world"); let fee = calculate_fee_from_state(&data, &app.state).await.unwrap(); - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -305,21 +292,18 @@ async fn app_execute_transaction_invalid_fee_asset() { let alice = get_alice_signing_key(); let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: test_asset(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -337,14 +321,11 @@ async fn app_execute_transaction_validator_update() { verification_key: crate::test_utils::verification_key(1), }; - let tx = UnsignedTransaction::new( - vec![Action::ValidatorUpdate(update.clone())], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![Action::ValidatorUpdate(update.clone())]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -365,16 +346,13 @@ async fn app_execute_transaction_ibc_relayer_change_addition() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction::new( - vec![Action::IbcRelayerChange(IbcRelayerChangeAction::Addition( - alice_address, - ))], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![Action::IbcRelayerChange( + IbcRelayerChangeAction::Addition(alice_address), + )]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -396,14 +374,11 @@ async fn app_execute_transaction_ibc_relayer_change_deletion() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction::new( - vec![IbcRelayerChangeAction::Removal(alice_address).into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![IbcRelayerChangeAction::Removal(alice_address).into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); assert_eq!(app.state.get_account_nonce(alice_address).await.unwrap(), 1); @@ -426,14 +401,11 @@ async fn app_execute_transaction_ibc_relayer_change_invalid() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction::new( - vec![IbcRelayerChangeAction::Removal(alice_address).into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![IbcRelayerChangeAction::Removal(alice_address).into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -447,16 +419,13 @@ async fn app_execute_transaction_sudo_address_change() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; let new_address = astria_address_from_hex_string(BOB_ADDRESS); - let tx = UnsignedTransaction::new( - vec![Action::SudoAddressChange(SudoAddressChangeAction { + let tx = UnsignedTransaction::builder() + .actions(vec![Action::SudoAddressChange(SudoAddressChangeAction { new_address, - })], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + })]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -485,16 +454,13 @@ async fn app_execute_transaction_sudo_address_change_error() { .try_into() .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction::new( - vec![Action::SudoAddressChange(SudoAddressChangeAction { + let tx = UnsignedTransaction::builder() + .actions(vec![Action::SudoAddressChange(SudoAddressChangeAction { new_address: alice_address, - })], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + })]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let res = app @@ -515,16 +481,13 @@ async fn app_execute_transaction_fee_asset_change_addition() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction::new( - vec![Action::FeeAssetChange(FeeAssetChangeAction::Addition( - test_asset(), - ))], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![Action::FeeAssetChange( + FeeAssetChangeAction::Addition(test_asset()), + )]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -549,16 +512,13 @@ async fn app_execute_transaction_fee_asset_change_removal() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction::new( - vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( + let tx = UnsignedTransaction::builder() + .actions(vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( test_asset(), - ))], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ))]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -575,16 +535,13 @@ async fn app_execute_transaction_fee_asset_change_invalid() { let mut app = initialize_app(Some(genesis_state()), vec![]).await; - let tx = UnsignedTransaction::new( - vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( + let tx = UnsignedTransaction::builder() + .actions(vec![Action::FeeAssetChange(FeeAssetChangeAction::Removal( nria().into(), - ))], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + ))]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let res = app @@ -618,14 +575,11 @@ async fn app_execute_transaction_init_bridge_account_ok() { withdrawer_address: None, }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -675,14 +629,11 @@ async fn app_execute_transaction_init_bridge_account_account_already_registered( sudo_address: None, withdrawer_address: None, }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -695,14 +646,11 @@ async fn app_execute_transaction_init_bridge_account_account_already_registered( withdrawer_address: None, }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -733,14 +681,11 @@ async fn app_execute_transaction_bridge_lock_action_ok() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -813,14 +758,11 @@ async fn app_execute_transaction_bridge_lock_action_invalid_for_eoa() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); assert!(app.execute_transaction(signed_tx).await.is_err()); @@ -836,21 +778,19 @@ async fn app_execute_transaction_invalid_nonce() { // create tx with invalid nonce 1 let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(1) - .chain_id("test") - .build(), - ) - .unwrap(); + ]) + .nonce(1) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let response = app.execute_transaction(signed_tx).await; @@ -884,21 +824,18 @@ async fn app_execute_transaction_invalid_chain_id() { // create tx with invalid nonce 1 let data = Bytes::from_static(b"hello world"); - let tx = UnsignedTransaction::new( - vec![ + let tx = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("wrong-chain") - .build(), - ) - .unwrap(); + ]) + .chain_id("wrong-chain") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let response = app.execute_transaction(signed_tx).await; @@ -941,8 +878,8 @@ async fn app_stateful_check_fails_insufficient_total_balance() { .unwrap(); // transfer just enough to cover single sequence fee with data - let signed_tx = UnsignedTransaction::new( - vec![ + let signed_tx = UnsignedTransaction::builder() + .actions(vec![ TransferAction { to: keypair_address, amount: fee, @@ -950,19 +887,16 @@ async fn app_stateful_check_fails_insufficient_total_balance() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&alice); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&alice); app.execute_transaction(Arc::new(signed_tx)).await.unwrap(); // build double transfer exceeding balance - let signed_tx_fail = UnsignedTransaction::new( - vec![ + let signed_tx_fail = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data: data.clone(), @@ -975,14 +909,11 @@ async fn app_stateful_check_fails_insufficient_total_balance() { fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&keypair); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&keypair); // try double, see fails stateful check let res = signed_tx_fail .check_and_execute(Arc::get_mut(&mut app.state).unwrap()) @@ -993,22 +924,19 @@ async fn app_stateful_check_fails_insufficient_total_balance() { assert!(res.contains("insufficient funds for asset")); // build single transfer to see passes - let signed_tx_pass = UnsignedTransaction::new( - vec![ + let signed_tx_pass = UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data, fee_asset: nria().into(), } .into(), - ], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap() - .into_signed(&keypair); + ]) + .chain_id("test") + .try_build() + .unwrap() + .into_signed(&keypair); signed_tx_pass .check_and_execute(Arc::get_mut(&mut app.state).unwrap()) @@ -1053,14 +981,11 @@ async fn app_execute_transaction_bridge_lock_unlock_action_ok() { fee_asset: nria().into(), destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); @@ -1078,14 +1003,11 @@ async fn app_execute_transaction_bridge_lock_unlock_action_ok() { rollup_withdrawal_event_id: "id-from-rollup".to_string(), }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&bridge)); app.execute_transaction(signed_tx) @@ -1127,14 +1049,11 @@ async fn app_execute_transaction_action_index_correctly_increments() { destination_chain_address: "nootwashere".to_string(), }; - let tx = UnsignedTransaction::new( - vec![action.clone().into(), action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.clone().into(), action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx.clone()).await.unwrap(); @@ -1172,14 +1091,11 @@ async fn transaction_execution_records_deposit_event() { fee_asset: nria().into(), destination_chain_address: "test_chain_address".to_string(), }; - let tx = UnsignedTransaction::new( - vec![action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![action.into()]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let expected_deposit = Deposit { diff --git a/crates/astria-sequencer/src/benchmark_utils.rs b/crates/astria-sequencer/src/benchmark_utils.rs index 6ccd241bd0..795af951ce 100644 --- a/crates/astria-sequencer/src/benchmark_utils.rs +++ b/crates/astria-sequencer/src/benchmark_utils.rs @@ -22,7 +22,6 @@ use astria_core::{ TransferAction, }, SignedTransaction, - TransactionParams, UnsignedTransaction, }, }; @@ -84,17 +83,16 @@ fn sequence_actions() -> Vec> { let (nonce, chain_id) = nonces_and_chain_ids .entry(verification_key) .or_insert_with(|| (0_u32, format!("chain-{}", signing_key.verification_key()))); - let params = TransactionParams::builder() - .nonce(*nonce) - .chain_id(chain_id.as_str()) - .build(); - *nonce = (*nonce).wrapping_add(1); let sequence_action = SequenceAction { rollup_id: RollupId::new([1; 32]), data: vec![2; 1000].into(), fee_asset: Denom::IbcPrefixed(IbcPrefixed::new([3; 32])), }; - let tx = UnsignedTransaction::new(vec![Action::Sequence(sequence_action)], params) + let tx = UnsignedTransaction::builder() + .actions(vec![Action::Sequence(sequence_action)]) + .nonce(*nonce) + .chain_id(chain_id.as_str()) + .try_build() .expect("failed to build transaction from actions") .into_signed(signing_key); Arc::new(tx) @@ -115,18 +113,17 @@ fn transfers() -> Vec> { }); (0..TRANSFERS_TX_COUNT) .map(|nonce| { - let params = TransactionParams::builder() + let tx = UnsignedTransaction::builder() + .actions( + std::iter::repeat(action.clone()) + .take(TRANSFERS_PER_TX) + .collect(), + ) .nonce(u32::try_from(nonce).unwrap()) .chain_id("test") - .build(); - let tx = UnsignedTransaction::new( - std::iter::repeat(action.clone()) - .take(TRANSFERS_PER_TX) - .collect(), - params, - ) - .unwrap() - .into_signed(sender); + .try_build() + .expect("failed to build transaction from actions") + .into_signed(sender); Arc::new(tx) }) .collect() diff --git a/crates/astria-sequencer/src/proposal/commitment.rs b/crates/astria-sequencer/src/proposal/commitment.rs index 86cffd7fe4..7b4d80153f 100644 --- a/crates/astria-sequencer/src/proposal/commitment.rs +++ b/crates/astria-sequencer/src/proposal/commitment.rs @@ -98,7 +98,6 @@ mod test { SequenceAction, TransferAction, }, - TransactionParams, UnsignedTransaction, }, }; @@ -122,14 +121,11 @@ mod test { let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction::new( - vec![sequence_action.clone().into(), transfer_action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![sequence_action.clone().into(), transfer_action.into()]) + .chain_id("test-chain-1") + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; @@ -139,14 +135,11 @@ mod test { } = generate_rollup_datas_commitment(&txs, HashMap::new()); let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction::new( - vec![sequence_action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![sequence_action.into()]) + .chain_id("test-chain-1") + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; @@ -178,14 +171,11 @@ mod test { }; let signing_key = SigningKey::new(OsRng); - let tx = UnsignedTransaction::new( - vec![sequence_action.clone().into(), transfer_action.into()], - TransactionParams::builder() - .nonce(0) - .chain_id("test-chain-1") - .build(), - ) - .unwrap(); + let tx = UnsignedTransaction::builder() + .actions(vec![sequence_action.clone().into(), transfer_action.into()]) + .chain_id("test-chain-1") + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&signing_key); let txs = vec![signed_tx]; diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index cc3209e213..a5edb78c08 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -211,7 +211,6 @@ mod test { primitive::v1::RollupId, protocol::transaction::v1alpha1::{ action::SequenceAction, - TransactionParams, UnsignedTransaction, }, }; @@ -237,21 +236,18 @@ mod test { }; fn make_unsigned_tx() -> UnsignedTransaction { - UnsignedTransaction::new( - vec![ + UnsignedTransaction::builder() + .actions(vec![ SequenceAction { rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), data: Bytes::from_static(b"hello world"), fee_asset: crate::test_utils::nria().into(), } .into(), - ], - TransactionParams::builder() - .chain_id("test") - .nonce(0) - .build(), - ) - .unwrap() + ]) + .chain_id("test") + .try_build() + .unwrap() } fn new_prepare_proposal_request() -> request::PrepareProposal { diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index 5c597699ca..752bbb8c53 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -313,12 +313,9 @@ mod tests { RollupId, ADDRESS_LEN, }, - protocol::transaction::v1alpha1::{ - action::{ - SequenceAction, - TransferAction, - }, - TransactionParams, + protocol::transaction::v1alpha1::action::{ + SequenceAction, + TransferAction, }, }; use bytes::Bytes; @@ -401,11 +398,11 @@ mod tests { }), ]; - let params = TransactionParams::builder() - .nonce(0) + let tx = UnsignedTransaction::builder() + .actions(actions) .chain_id("test-chain-id") - .build(); - let tx = UnsignedTransaction::new(actions, params).unwrap(); + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&alice); check_balance_for_total_fees_and_transfers(&signed_tx, &state_tx) @@ -464,11 +461,11 @@ mod tests { }), ]; - let params = TransactionParams::builder() - .nonce(0) + let tx = UnsignedTransaction::builder() + .actions(actions) .chain_id("test-chain-id") - .build(); - let tx = UnsignedTransaction::new(actions, params).unwrap(); + .try_build() + .unwrap(); let signed_tx = tx.into_signed(&alice); let err = check_balance_for_total_fees_and_transfers(&signed_tx, &state_tx) From 3694ee6fc8b35d93f9caf5c6820a22bab08ae96f Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Thu, 26 Sep 2024 07:58:48 -0400 Subject: [PATCH 08/18] add IbcSudo action pieces --- .../protocol/transaction/v1alpha1/action.rs | 1 + .../transaction/v1alpha1/action_group/mod.rs | 3 + ...ransaction_with_every_action_snapshot.snap | 60 +++++++++---------- .../src/app/tests_breaking_changes.rs | 17 +++++- .../src/app/tests_execute_transaction.rs | 28 ++++----- 5 files changed, 60 insertions(+), 49 deletions(-) diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs index 24c7c7ed85..2f44dd1289 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs @@ -283,6 +283,7 @@ impl ActionName for Action { Action::ValidatorUpdate(_) => "ValidatorUpdate", Action::SudoAddressChange(_) => "SudoAddressChange", Action::Ibc(_) => "Ibc", + Action::IbcSudoChange(_) => "IbcSudoChange", Action::Ics20Withdrawal(_) => "Ics20Withdrawal", Action::IbcRelayerChange(_) => "IbcRelayerChange", Action::FeeAssetChange(_) => "FeeAssetChange", diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs index ec8525634d..16b71002e5 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs @@ -17,6 +17,7 @@ use super::{ FeeAssetChangeAction, FeeChangeAction, IbcRelayerChangeAction, + IbcSudoChangeAction, Ics20Withdrawal, InitBridgeAccountAction, SequenceAction, @@ -55,6 +56,7 @@ impl_belong_to_group!( (FeeChangeAction, ActionGroup::Sudo), (FeeAssetChangeAction, ActionGroup::Sudo), (IbcRelay, ActionGroup::General), + (IbcSudoChangeAction, ActionGroup::Sudo), ); trait Group { @@ -77,6 +79,7 @@ impl Group for Action { Action::FeeChange(_) => FeeChangeAction::GROUP, Action::FeeAssetChange(_) => FeeAssetChangeAction::GROUP, Action::Ibc(_) => IbcRelay::GROUP, + Action::IbcSudoChange(_) => IbcSudoChangeAction::GROUP, } } } diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index 1d2458f1b5..2e1d34cb43 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 237, - 228, - 62, - 229, - 45, - 77, - 247, - 239, - 251, - 224, - 244, - 97, - 68, - 46, - 184, - 181, - 205, - 86, - 212, - 153, - 66, - 146, - 179, - 120, - 206, - 95, + 255, + 50, 76, - 11, - 0, + 145, + 103, + 50, + 190, + 144, + 101, + 107, + 120, + 151, + 12, + 5, + 165, + 138, 184, - 137, - 173 + 50, + 123, + 183, + 216, + 21, + 236, + 8, + 128, + 144, + 164, + 172, + 141, + 31, + 41, + 213 ] diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 642197bfc4..ec0b1ed0a9 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -26,8 +26,8 @@ use astria_core::{ FeeChange, FeeChangeAction, IbcRelayerChangeAction, - Ics20Withdrawal, IbcSudoChangeAction, + Ics20Withdrawal, SequenceAction, TransferAction, ValidatorUpdate, @@ -239,18 +239,26 @@ async fn app_execute_transaction_with_every_action_snapshot() { .try_build() .unwrap(); - let tx_sudo = UnsignedTransaction::builder() + let tx_sudo_ibc = UnsignedTransaction::builder() .actions(vec![ IbcSudoChangeAction { new_address: bob_address, } .into(), + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap(); + + let tx_sudo = UnsignedTransaction::builder() + .actions(vec![ SudoAddressChangeAction { new_address: bob_address, } .into(), ]) - .nonce(2) + .nonce(3) .chain_id("test") .try_build() .unwrap(); @@ -265,6 +273,9 @@ async fn app_execute_transaction_with_every_action_snapshot() { .await .unwrap(); + let signed_tx_sudo_ibc = Arc::new(tx_sudo_ibc.into_signed(&alice)); + app.execute_transaction(signed_tx_sudo_ibc).await.unwrap(); + let signed_tx_sudo = Arc::new(tx_sudo.into_signed(&alice)); app.execute_transaction(signed_tx_sudo).await.unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 5341ad7bf0..e3d9382317 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -1127,15 +1127,13 @@ async fn app_execute_transaction_ibc_sudo_change() { let new_address = astria_address_from_hex_string(BOB_ADDRESS); - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::IbcSudoChange(IbcSudoChangeAction { + let tx = UnsignedTransaction::builder() + .actions(vec![Action::IbcSudoChange(IbcSudoChangeAction { new_address, - })], - }; + })]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); app.execute_transaction(signed_tx).await.unwrap(); @@ -1164,15 +1162,13 @@ async fn app_execute_transaction_ibc_sudo_change_error() { .unwrap(); let mut app = initialize_app(Some(genesis_state), vec![]).await; - let tx = UnsignedTransaction { - params: TransactionParams::builder() - .nonce(0) - .chain_id("test") - .build(), - actions: vec![Action::IbcSudoChange(IbcSudoChangeAction { + let tx = UnsignedTransaction::builder() + .actions(vec![Action::IbcSudoChange(IbcSudoChangeAction { new_address: alice_address, - })], - }; + })]) + .chain_id("test") + .try_build() + .unwrap(); let signed_tx = Arc::new(tx.into_signed(&alice)); let res = app From 940cadeb0520e08ead3cb18c681403b740129e14 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Thu, 26 Sep 2024 08:29:27 -0400 Subject: [PATCH 09/18] cleanup --- .../src/executor/bundle_factory/tests.rs | 1 - .../protocol/transaction/v1alpha1/action.rs | 2 ++ .../transaction/v1alpha1/action_group/mod.rs | 5 ++--- .../transaction/v1alpha1/action_group/tests.rs | 18 ++++++++++++++---- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/tests.rs b/crates/astria-composer/src/executor/bundle_factory/tests.rs index fc6f543b82..22d1878090 100644 --- a/crates/astria-composer/src/executor/bundle_factory/tests.rs +++ b/crates/astria-composer/src/executor/bundle_factory/tests.rs @@ -83,7 +83,6 @@ mod sized_bundle { #[cfg(test)] mod bundle_factory { - use super::*; use crate::{ executor::bundle_factory::{ diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs index 2f44dd1289..ef0c95c19d 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs @@ -271,6 +271,8 @@ impl TryFrom for Action { } } +// TODO: replace this trait with a Protobuf:FullName implementation. +// Issue tracked in #1567 pub(super) trait ActionName { fn name(&self) -> &'static str; } diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs index 16b71002e5..69ca53f210 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs @@ -56,7 +56,7 @@ impl_belong_to_group!( (FeeChangeAction, ActionGroup::Sudo), (FeeAssetChangeAction, ActionGroup::Sudo), (IbcRelay, ActionGroup::General), - (IbcSudoChangeAction, ActionGroup::Sudo), + (IbcSudoChangeAction, ActionGroup::UnbundleableSudo), ); trait Group { @@ -148,7 +148,6 @@ enum ErrorKind { NotBundleable { group: ActionGroup }, } -/// Invariants: `group` is set if `inner` is not empty. #[derive(Clone, Debug)] pub(super) struct Actions { inner: Vec, @@ -179,7 +178,7 @@ impl Actions { let group = match actions_iter.next() { Some(action) => action.group(), None => { - // empty `actions`, so invariants met + // empty `actions` return Ok(Self::default()); } }; diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs index ff963f5220..d3ae280891 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs @@ -17,6 +17,7 @@ use crate::{ FeeChange, FeeChangeAction, IbcRelayerChangeAction, + IbcSudoChangeAction, Ics20Withdrawal, InitBridgeAccountAction, SequenceAction, @@ -33,7 +34,7 @@ use crate::{ const ASTRIA_ADDRESS_PREFIX: &str = "astria"; #[test] -fn try_from_list_of_actions_bundleable_general() { +fn try_from_list_of_actions_general() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -95,7 +96,7 @@ fn try_from_list_of_actions_bundleable_general() { } #[test] -fn from_list_of_actions_bundleable_sudo() { +fn from_list_of_actions_sudo() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -119,7 +120,7 @@ fn from_list_of_actions_bundleable_sudo() { } #[test] -fn from_list_of_actions_sudo() { +fn from_list_of_actions_unbundelable_sudo() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -135,6 +136,15 @@ fn from_list_of_actions_sudo() { Some(ActionGroup::UnbundleableSudo) )); + let actions = vec![Action::IbcSudoChange(IbcSudoChangeAction { + new_address: address, + })]; + + assert!(matches!( + Actions::try_from_list_of_actions(actions).unwrap().group(), + Some(ActionGroup::UnbundleableSudo) + )); + let actions = vec![ Action::SudoAddressChange(SudoAddressChangeAction { new_address: address, @@ -153,7 +163,7 @@ fn from_list_of_actions_sudo() { } #[test] -fn from_list_of_actions_general() { +fn from_list_of_actions_unbundleable_general() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) From 50eda1e741edfc0fc1ee8571cf3d8dbe96fc2f71 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Thu, 26 Sep 2024 08:48:55 -0400 Subject: [PATCH 10/18] update breaking test --- ...ransaction_with_every_action_snapshot.snap | 61 +++++++++---------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index 52c47f2f6a..4cc4e40008 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -1,39 +1,38 @@ --- source: crates/astria-sequencer/src/app/tests_breaking_changes.rs -assertion_line: 308 expression: app.app_hash.as_bytes() --- [ - 67, - 124, - 63, - 240, - 228, - 207, - 78, - 64, - 191, - 89, - 84, - 121, - 150, - 21, - 207, - 248, - 173, - 132, - 77, + 96, 126, - 148, - 252, - 239, - 104, - 130, - 55, - 201, - 32, - 57, - 167, + 64, + 175, + 139, + 186, + 101, + 145, + 115, + 19, + 45, + 255, + 124, + 232, + 128, + 181, + 193, + 25, + 192, + 26, + 185, + 10, + 113, + 193, + 151, + 38, + 1, 215, - 228 + 173, + 183, + 175, + 217 ] From e41f6c7d9fe7fe919f9d2731aae9cef94a5dca13 Mon Sep 17 00:00:00 2001 From: Lily Johnson <35852084+Lilyjjo@users.noreply.github.com> Date: Thu, 26 Sep 2024 08:59:34 -0400 Subject: [PATCH 11/18] Update crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap fix lint --- ...ges__app_execute_transaction_with_every_action_snapshot.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index 4cc4e40008..e02ff29153 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,7 +3,7 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 96, + 96, 126, 64, 175, From 3e79a1c7794fe1fd78dcc25ae72de8b8f8b399c7 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Sat, 28 Sep 2024 13:37:59 -0400 Subject: [PATCH 12/18] respond to comments by @Fraser999 --- .../src/executor/bundle_factory/mod.rs | 5 +- .../src/executor/bundle_factory/tests.rs | 5 +- .../transaction/v1alpha1/action_group/mod.rs | 41 +++-- .../v1alpha1/action_group/tests.rs | 31 ++-- .../src/protocol/transaction/v1alpha1/mod.rs | 23 +-- .../src/app/tests_breaking_changes.rs | 140 +++++++++--------- 6 files changed, 117 insertions(+), 128 deletions(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index 0fc824d7be..dbac5f48c9 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -75,9 +75,10 @@ impl SizedBundle { } } - #[allow(clippy::panic)] // method is expected to never panic because only `SequenceActions` are - /// added to the bundle, which should produce a valid variant of the `ActionGroup` type. /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. + // Method is expected to never panic because only `SequenceActions` are added to the bundle, + // which should produce a valid variant of the `ActionGroup` type. + #[allow(clippy::panic)] pub(super) fn to_unsigned_transaction( &self, nonce: u32, diff --git a/crates/astria-composer/src/executor/bundle_factory/tests.rs b/crates/astria-composer/src/executor/bundle_factory/tests.rs index 22d1878090..7930a13bc1 100644 --- a/crates/astria-composer/src/executor/bundle_factory/tests.rs +++ b/crates/astria-composer/src/executor/bundle_factory/tests.rs @@ -344,12 +344,15 @@ mod bundle_factory { bundle_factory .try_push(sequence_action_with_n_bytes(50)) .unwrap(); + bundle_factory + .try_push(sequence_action_with_n_bytes(50)) + .unwrap(); let bundle = bundle_factory.pop_now(); // construction of multiple sequence actions should not panic let unsigned_tx = bundle.to_unsigned_transaction(0, "astria-testnet-1"); - assert_eq!(unsigned_tx.actions().len(), 2); + assert_eq!(unsigned_tx.actions().len(), 3); } } diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs index 69ca53f210..87b39a67ab 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs @@ -59,12 +59,8 @@ impl_belong_to_group!( (IbcSudoChangeAction, ActionGroup::UnbundleableSudo), ); -trait Group { - fn group(&self) -> ActionGroup; -} - -impl Group for Action { - fn group(&self) -> ActionGroup { +impl Action { + const fn group(&self) -> ActionGroup { match self { Action::Sequence(_) => SequenceAction::GROUP, Action::Transfer(_) => TransferAction::GROUP, @@ -85,7 +81,7 @@ impl Group for Action { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(super) enum ActionGroup { +pub enum ActionGroup { General, UnbundleableGeneral, Sudo, @@ -118,7 +114,16 @@ impl fmt::Display for ActionGroup { pub struct Error(ErrorKind); impl Error { - fn mixed(original_group: ActionGroup, additional_group: ActionGroup, action: String) -> Self { + #[must_use] + pub fn kind(&self) -> &ErrorKind { + &self.0 + } + + fn mixed( + original_group: ActionGroup, + additional_group: ActionGroup, + action: &'static str, + ) -> Self { Self(ErrorKind::Mixed { original_group, additional_group, @@ -134,7 +139,7 @@ impl Error { } #[derive(Debug, thiserror::Error)] -enum ErrorKind { +pub enum ErrorKind { #[error( "input contains mixed `ActionGroup` types. original group: {original_group}, additional \ group: {additional_group}, triggering action: {action}" @@ -142,13 +147,13 @@ enum ErrorKind { Mixed { original_group: ActionGroup, additional_group: ActionGroup, - action: String, + action: &'static str, }, #[error("attempted to create bundle with non bundleable `ActionGroup` type: {group}")] NotBundleable { group: ActionGroup }, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub(super) struct Actions { inner: Vec, } @@ -164,13 +169,7 @@ impl Actions { } pub(super) fn group(&self) -> Option { - self.inner.first().map(Group::group) - } - - fn default() -> Self { - Self { - inner: vec![], - } + self.inner.first().map(super::action::Action::group) } pub(super) fn try_from_list_of_actions(actions: Vec) -> Result { @@ -191,11 +190,7 @@ impl Actions { // assert the rest of the actions have the same group as the first for action in actions_iter { if action.group() != group { - return Err(Error::mixed( - group, - action.group(), - action.name().to_string(), - )); + return Err(Error::mixed(group, action.group(), action.name())); } } diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs index d3ae280891..c88dc60d71 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs @@ -28,6 +28,7 @@ use crate::{ action_group::{ ActionGroup, Actions, + ErrorKind, }, }, }; @@ -120,7 +121,7 @@ fn from_list_of_actions_sudo() { } #[test] -fn from_list_of_actions_unbundelable_sudo() { +fn from_list_of_actions_unbundleable_sudo() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -154,11 +155,10 @@ fn from_list_of_actions_unbundelable_sudo() { }), ]; - assert_eq!( - Actions::try_from_list_of_actions(actions) - .unwrap_err() - .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable sudo" + let error_kind = Actions::try_from_list_of_actions(actions).unwrap_err().0; + assert!( + matches!(error_kind, ErrorKind::NotBundleable { .. }), + "expected ErrorKind::NotBundleable, got {error_kind:?}" ); } @@ -206,11 +206,10 @@ fn from_list_of_actions_unbundleable_general() { sudo_bridge_address_change_action.into(), ]; - assert_eq!( - Actions::try_from_list_of_actions(actions) - .unwrap_err() - .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" + let error_kind = Actions::try_from_list_of_actions(actions).unwrap_err().0; + assert!( + matches!(error_kind, ErrorKind::NotBundleable { .. }), + "expected ErrorKind::NotBundleable, got {error_kind:?}" ); } @@ -234,11 +233,9 @@ fn from_list_of_actions_mixed() { }), ]; - assert_eq!( - Actions::try_from_list_of_actions(actions) - .unwrap_err() - .to_string(), - "input contains mixed `ActionGroup` types. original group: general, additional group: \ - unbundleable sudo, triggering action: SudoAddressChange" + let error_kind = Actions::try_from_list_of_actions(actions).unwrap_err().0; + assert!( + matches!(error_kind, ErrorKind::Mixed { .. }), + "expected ErrorKind::Mixed, got {error_kind:?}" ); } diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index d16b080817..7c94d516ea 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -421,25 +421,19 @@ enum UnsignedTransactionErrorKind { ActionGroup(#[source] action_group::Error), } -pub struct UnsignedTransactionBuilder> { +#[derive(Default)] +pub struct UnsignedTransactionBuilder { nonce: u32, - chain_id: TChainId, + chain_id: String, actions: Vec, } impl UnsignedTransactionBuilder { #[must_use] - #[allow(clippy::new_without_default)] pub fn new() -> Self { - Self { - nonce: 0, - chain_id: "".into(), - actions: vec![], - } + Self::default() } -} -impl UnsignedTransactionBuilder { #[must_use] pub fn actions(self, actions: Vec) -> Self { Self { @@ -449,10 +443,7 @@ impl UnsignedTransactionBuilder { } #[must_use] - pub fn chain_id<'a, T: Into>>( - self, - chain_id: T, - ) -> UnsignedTransactionBuilder> { + pub fn chain_id>(self, chain_id: T) -> UnsignedTransactionBuilder { UnsignedTransactionBuilder { chain_id: chain_id.into(), nonce: self.nonce, @@ -467,9 +458,7 @@ impl UnsignedTransactionBuilder { ..self } } -} -impl<'a> UnsignedTransactionBuilder> { /// Constructs a [`UnsignedTransaction`] from the configured builder. /// /// # Errors @@ -488,7 +477,7 @@ impl<'a> UnsignedTransactionBuilder> { actions, params: TransactionParams { nonce, - chain_id: chain_id.into(), + chain_id, }, }) } diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index ec0b1ed0a9..35e72c7d88 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -32,6 +32,7 @@ use astria_core::{ TransferAction, ValidatorUpdate, }, + action_group, Action, UnsignedTransaction, }, @@ -449,77 +450,80 @@ async fn app_transaction_bundle_categories() { "should be able to construct sudo bundle" ); - assert_eq!( - UnsignedTransaction::builder() - .actions(vec![ - SudoAddressChangeAction { - new_address: bob_address, - } - .into(), - SudoAddressChangeAction { - new_address: bob_address, - } - .into(), - ]) - .nonce(2) - .chain_id("test") - .try_build() - .unwrap_err() - .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable sudo" + let error = UnsignedTransaction::builder() + .actions(vec![ + SudoAddressChangeAction { + new_address: bob_address, + } + .into(), + SudoAddressChangeAction { + new_address: bob_address, + } + .into(), + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap_err(); + assert!( + matches!(error.kind(), action_group::ErrorKind::NotBundleable { .. }), + "expected ErrorKind::NotBundleable, got {:?}", + error.kind() ); - assert_eq!( - UnsignedTransaction::builder() - .actions(vec![ - InitBridgeAccountAction { - rollup_id, - asset: nria().into(), - fee_asset: nria().into(), - sudo_address: None, - withdrawer_address: None, - } - .into(), - InitBridgeAccountAction { - rollup_id, - asset: nria().into(), - fee_asset: nria().into(), - sudo_address: None, - withdrawer_address: None, - } - .into(), - ]) - .nonce(2) - .chain_id("test") - .try_build() - .unwrap_err() - .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" + let error = UnsignedTransaction::builder() + .actions(vec![ + InitBridgeAccountAction { + rollup_id, + asset: nria().into(), + fee_asset: nria().into(), + sudo_address: None, + withdrawer_address: None, + } + .into(), + InitBridgeAccountAction { + rollup_id, + asset: nria().into(), + fee_asset: nria().into(), + sudo_address: None, + withdrawer_address: None, + } + .into(), + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap_err(); + assert!( + matches!(error.kind(), action_group::ErrorKind::NotBundleable { .. }), + "expected ErrorKind::NotBundleable, got {:?}", + error.kind() ); - assert_eq!( - UnsignedTransaction::builder() - .actions(vec![ - BridgeSudoChangeAction { - bridge_address, - new_sudo_address: Some(bob_address), - new_withdrawer_address: Some(bob_address), - fee_asset: nria().into(), - } - .into(), - BridgeSudoChangeAction { - bridge_address, - new_sudo_address: Some(bob_address), - new_withdrawer_address: Some(bob_address), - fee_asset: nria().into(), - } - .into(), - ]) - .nonce(2) - .chain_id("test") - .try_build() - .unwrap_err() - .to_string(), - "attempted to create bundle with non bundleable `ActionGroup` type: unbundleable general" + let error = UnsignedTransaction::builder() + .actions(vec![ + BridgeSudoChangeAction { + bridge_address, + new_sudo_address: Some(bob_address), + new_withdrawer_address: Some(bob_address), + fee_asset: nria().into(), + } + .into(), + BridgeSudoChangeAction { + bridge_address, + new_sudo_address: Some(bob_address), + new_withdrawer_address: Some(bob_address), + fee_asset: nria().into(), + } + .into(), + ]) + .nonce(2) + .chain_id("test") + .try_build() + .unwrap_err(); + assert!( + matches!(error.kind(), action_group::ErrorKind::NotBundleable { .. }), + "expected ErrorKind::NotBundleable, got {:?}", + error.kind() ); } From 613993fd16b71feab8706e8ad3001179020b0f9d Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 30 Sep 2024 10:42:19 -0400 Subject: [PATCH 13/18] respond to comment by @SuperFluffy --- .../src/executor/bundle_factory/mod.rs | 6 - .../src/executor/bundle_factory/tests.rs | 14 +- .../protocol/transaction/v1alpha1/action.rs | 1 + .../transaction/v1alpha1/action_group/mod.rs | 9 +- .../src/protocol/transaction/v1alpha1/mod.rs | 2 +- .../src/app/tests_breaking_changes.rs | 180 ------------------ 6 files changed, 11 insertions(+), 201 deletions(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index dbac5f48c9..de3e9ee5f2 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -129,12 +129,6 @@ impl SizedBundle { self.curr_size } - /// Consume self and return the underlying buffer of actions. - #[cfg(test)] - pub(super) fn into_actions(self) -> Vec { - self.buffer - } - /// Returns the number of sequence actions in the bundle. pub(super) fn actions_count(&self) -> usize { self.buffer.len() diff --git a/crates/astria-composer/src/executor/bundle_factory/tests.rs b/crates/astria-composer/src/executor/bundle_factory/tests.rs index 7930a13bc1..91b65a7fd7 100644 --- a/crates/astria-composer/src/executor/bundle_factory/tests.rs +++ b/crates/astria-composer/src/executor/bundle_factory/tests.rs @@ -73,7 +73,7 @@ mod sized_bundle { assert!(bundle.buffer.is_empty()); // assert that the flushed bundle has just the sequence action pushed earlier - let actions = flushed_bundle.into_actions(); + let actions = flushed_bundle.buffer; assert_eq!(actions.len(), 1); let actual_seq_action = actions[0].as_sequence().unwrap(); assert_eq!(actual_seq_action.rollup_id, seq_action.rollup_id); @@ -137,7 +137,7 @@ mod bundle_factory { assert_eq!(bundle_factory.finished.len(), 1); // assert `pop_finished()` will return `seq_action0` let next_actions = bundle_factory.next_finished(); - let actions = next_actions.unwrap().pop().into_actions(); + let actions = next_actions.unwrap().pop().buffer; let actual_seq_action = actions[0].as_sequence().unwrap(); assert_eq!(actual_seq_action.rollup_id, seq_action0.rollup_id); assert_eq!(actual_seq_action.data, seq_action0.data); @@ -236,7 +236,7 @@ mod bundle_factory { // assert that the finished queue is empty (curr wasn't flushed) assert_eq!(bundle_factory.finished.len(), 0); // assert `pop_now()` returns `seq_action` - let actions = bundle_factory.pop_now().into_actions(); + let actions = bundle_factory.pop_now().buffer; let actual_seq_action = actions[0].as_sequence().unwrap(); assert_eq!(actual_seq_action.rollup_id, seq_action.rollup_id); assert_eq!(actual_seq_action.data, seq_action.data); @@ -261,7 +261,7 @@ mod bundle_factory { // assert that the bundle factory has one bundle in the finished queue assert_eq!(bundle_factory.finished.len(), 1); // assert `pop_now()` will return `seq_action0` - let actions = bundle_factory.pop_now().into_actions(); + let actions = bundle_factory.pop_now().buffer; let actual_seq_action = actions[0].as_sequence().unwrap(); assert_eq!(actual_seq_action.rollup_id, seq_action0.rollup_id); assert_eq!(actual_seq_action.data, seq_action0.data); @@ -296,7 +296,7 @@ mod bundle_factory { assert_eq!(bundle_factory.finished.len(), 1); // assert `pop_now()` will return `seq_action0` on the first call - let actions_finished = bundle_factory.pop_now().into_actions(); + let actions_finished = bundle_factory.pop_now().buffer; assert_eq!(actions_finished.len(), 1); let actual_seq_action = actions_finished[0].as_sequence().unwrap(); assert_eq!(actual_seq_action.rollup_id, seq_action0.rollup_id); @@ -306,7 +306,7 @@ mod bundle_factory { assert_eq!(bundle_factory.finished.len(), 0); // assert `pop_now()` will return `seq_action1` on the second call (i.e. from curr) - let actions_curr = bundle_factory.pop_now().into_actions(); + let actions_curr = bundle_factory.pop_now().buffer; assert_eq!(actions_curr.len(), 1); let actual_seq_action = actions_curr[0].as_sequence().unwrap(); assert_eq!(actual_seq_action.rollup_id, seq_action1.rollup_id); @@ -335,7 +335,7 @@ mod bundle_factory { } #[test] - fn unsigned_transaction_construction() { + fn transaction_construction_does_not_panic() { let mut bundle_factory = BundleFactory::new(1000, 10); bundle_factory diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs index ef0c95c19d..5ef8e94934 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs @@ -153,6 +153,7 @@ impl Protobuf for Action { } } +// TODO: add unit tests for these methods (https://github.com/astriaorg/astria/issues/1593) impl Action { #[must_use] pub fn as_sequence(&self) -> Option<&SequenceAction> { diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs index 87b39a67ab..1499b0409d 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs @@ -81,7 +81,7 @@ impl Action { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ActionGroup { +pub(super) enum ActionGroup { General, UnbundleableGeneral, Sudo, @@ -114,11 +114,6 @@ impl fmt::Display for ActionGroup { pub struct Error(ErrorKind); impl Error { - #[must_use] - pub fn kind(&self) -> &ErrorKind { - &self.0 - } - fn mixed( original_group: ActionGroup, additional_group: ActionGroup, @@ -139,7 +134,7 @@ impl Error { } #[derive(Debug, thiserror::Error)] -pub enum ErrorKind { +enum ErrorKind { #[error( "input contains mixed `ActionGroup` types. original group: {original_group}, additional \ group: {additional_group}, triggering action: {action}" diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index 7c94d516ea..82832fe09d 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -417,7 +417,7 @@ enum UnsignedTransactionErrorKind { raw::UnsignedTransaction::type_url() )] DecodeAny(#[source] prost::DecodeError), - #[error("`actions` field does not make a valid `ActionGroup`")] + #[error("`actions` field does not form a valid group of actions")] ActionGroup(#[source] action_group::Error), } diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 35e72c7d88..c6a8653d83 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -23,16 +23,12 @@ use astria_core::{ BridgeLockAction, BridgeSudoChangeAction, BridgeUnlockAction, - FeeChange, - FeeChangeAction, IbcRelayerChangeAction, IbcSudoChangeAction, - Ics20Withdrawal, SequenceAction, TransferAction, ValidatorUpdate, }, - action_group, Action, UnsignedTransaction, }, @@ -41,7 +37,6 @@ use astria_core::{ Protobuf, }; use cnidarium::StateDelta; -use ibc_types::core::client::Height; use prost::{ bytes::Bytes, Message as _, @@ -352,178 +347,3 @@ async fn app_execute_transaction_with_every_action_snapshot() { insta::assert_json_snapshot!(app.app_hash.as_bytes()); } - -// Note: this test ensures that all actions are in their expected -// bundleable vs non-bundleable category. Tests all actions except -// IbcRelay. -// -// If any PR changes the bundleable status of an action, the PR must -// be marked as breaking. -#[allow(clippy::too_many_lines)] -#[tokio::test] -async fn app_transaction_bundle_categories() { - use astria_core::protocol::transaction::v1alpha1::action::{ - FeeAssetChangeAction, - InitBridgeAccountAction, - SudoAddressChangeAction, - }; - - let bridge = get_bridge_signing_key(); - let bridge_address = astria_address(&bridge.address_bytes()); - let bob_address = astria_address_from_hex_string(BOB_ADDRESS); - let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); - - assert!( - UnsignedTransaction::builder() - .actions(vec![ - TransferAction { - to: bob_address, - amount: 333_333, - asset: nria().into(), - fee_asset: nria().into(), - } - .into(), - SequenceAction { - rollup_id: RollupId::from_unhashed_bytes(b"testchainid"), - data: Bytes::from_static(b"hello world"), - fee_asset: nria().into(), - } - .into(), - Action::ValidatorUpdate(ValidatorUpdate { - power: 100, - verification_key: crate::test_utils::verification_key(1), - }), - BridgeLockAction { - to: bridge_address, - amount: 100, - asset: nria().into(), - fee_asset: nria().into(), - destination_chain_address: "nootwashere".to_string(), - } - .into(), - BridgeUnlockAction { - to: bob_address, - amount: 10, - fee_asset: nria().into(), - memo: String::new(), - bridge_address: astria_address(&bridge.address_bytes()), - rollup_block_number: 1, - rollup_withdrawal_event_id: "a-rollup-defined-hash".to_string(), - } - .into(), - Ics20Withdrawal { - amount: 1, - denom: nria().into(), - bridge_address: None, - destination_chain_address: "test".to_string(), - return_address: bob_address, - timeout_height: Height::new(1, 1).unwrap(), - timeout_time: 1, - source_channel: "channel-0".to_string().parse().unwrap(), - fee_asset: nria().into(), - memo: String::new(), - use_compat_address: false, - } - .into(), - ]) - .chain_id("test") - .try_build() - .is_ok(), - "should be able to construct general bundle" - ); - - assert!( - UnsignedTransaction::builder() - .actions(vec![ - IbcRelayerChangeAction::Addition(bob_address).into(), - FeeAssetChangeAction::Removal("test-0".parse().unwrap()).into(), - FeeChangeAction { - fee_change: FeeChange::TransferBaseFee, - new_value: 10, - } - .into(), - ]) - .nonce(1) - .chain_id("test") - .try_build() - .is_ok(), - "should be able to construct sudo bundle" - ); - - let error = UnsignedTransaction::builder() - .actions(vec![ - SudoAddressChangeAction { - new_address: bob_address, - } - .into(), - SudoAddressChangeAction { - new_address: bob_address, - } - .into(), - ]) - .nonce(2) - .chain_id("test") - .try_build() - .unwrap_err(); - assert!( - matches!(error.kind(), action_group::ErrorKind::NotBundleable { .. }), - "expected ErrorKind::NotBundleable, got {:?}", - error.kind() - ); - - let error = UnsignedTransaction::builder() - .actions(vec![ - InitBridgeAccountAction { - rollup_id, - asset: nria().into(), - fee_asset: nria().into(), - sudo_address: None, - withdrawer_address: None, - } - .into(), - InitBridgeAccountAction { - rollup_id, - asset: nria().into(), - fee_asset: nria().into(), - sudo_address: None, - withdrawer_address: None, - } - .into(), - ]) - .nonce(2) - .chain_id("test") - .try_build() - .unwrap_err(); - assert!( - matches!(error.kind(), action_group::ErrorKind::NotBundleable { .. }), - "expected ErrorKind::NotBundleable, got {:?}", - error.kind() - ); - - let error = UnsignedTransaction::builder() - .actions(vec![ - BridgeSudoChangeAction { - bridge_address, - new_sudo_address: Some(bob_address), - new_withdrawer_address: Some(bob_address), - fee_asset: nria().into(), - } - .into(), - BridgeSudoChangeAction { - bridge_address, - new_sudo_address: Some(bob_address), - new_withdrawer_address: Some(bob_address), - fee_asset: nria().into(), - } - .into(), - ]) - .nonce(2) - .chain_id("test") - .try_build() - .unwrap_err(); - assert!( - matches!(error.kind(), action_group::ErrorKind::NotBundleable { .. }), - "expected ErrorKind::NotBundleable, got {:?}", - error.kind() - ); -} From 052dbfdd8b655bed0808fa9024a754cae2f8d5a0 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 30 Sep 2024 12:35:40 -0400 Subject: [PATCH 14/18] respond to comments by @SuperFluffy --- .../src/executor/bundle_factory/mod.rs | 7 +++- crates/astria-composer/src/executor/mod.rs | 1 - .../transaction/v1alpha1/action_group/mod.rs | 37 ++++++++++--------- .../v1alpha1/action_group/tests.rs | 8 ++-- .../src/protocol/transaction/v1alpha1/mod.rs | 4 +- crates/astria-sequencer/src/app/mod.rs | 2 +- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index de3e9ee5f2..3a0dbdae77 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -78,7 +78,12 @@ impl SizedBundle { /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. // Method is expected to never panic because only `SequenceActions` are added to the bundle, // which should produce a valid variant of the `ActionGroup` type. - #[allow(clippy::panic)] + #[allow( + clippy::panic, + reason = "method is expected to never panic because only `SequenceActions` are added to \ + the bundle, which should produce a valid variant of the `ActionGroup` type; \ + this is checked by `tests::transaction_construction_should_not_panic" + )] pub(super) fn to_unsigned_transaction( &self, nonce: u32, diff --git a/crates/astria-composer/src/executor/mod.rs b/crates/astria-composer/src/executor/mod.rs index 6d9652b505..26260cbfaa 100644 --- a/crates/astria-composer/src/executor/mod.rs +++ b/crates/astria-composer/src/executor/mod.rs @@ -668,7 +668,6 @@ impl Future for SubmitFut { type Output = eyre::Result; // FIXME (https://github.com/astriaorg/astria/issues/1572): This function is too long and should be refactored. - #[expect(clippy::too_many_lines, reason = "this may warrant a refactor")] fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { const INVALID_NONCE: Code = Code::Err(AbciErrorCode::INVALID_NONCE.value()); loop { diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs index 1499b0409d..e39971d956 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/mod.rs @@ -43,19 +43,19 @@ macro_rules! impl_belong_to_group { } impl_belong_to_group!( - (SequenceAction, ActionGroup::General), - (TransferAction, ActionGroup::General), - (ValidatorUpdate, ActionGroup::General), + (SequenceAction, ActionGroup::BundleableGeneral), + (TransferAction, ActionGroup::BundleableGeneral), + (ValidatorUpdate, ActionGroup::BundleableGeneral), (SudoAddressChangeAction, ActionGroup::UnbundleableSudo), - (IbcRelayerChangeAction, ActionGroup::Sudo), - (Ics20Withdrawal, ActionGroup::General), + (IbcRelayerChangeAction, ActionGroup::BundleableSudo), + (Ics20Withdrawal, ActionGroup::BundleableGeneral), (InitBridgeAccountAction, ActionGroup::UnbundleableGeneral), - (BridgeLockAction, ActionGroup::General), - (BridgeUnlockAction, ActionGroup::General), + (BridgeLockAction, ActionGroup::BundleableGeneral), + (BridgeUnlockAction, ActionGroup::BundleableGeneral), (BridgeSudoChangeAction, ActionGroup::UnbundleableGeneral), - (FeeChangeAction, ActionGroup::Sudo), - (FeeAssetChangeAction, ActionGroup::Sudo), - (IbcRelay, ActionGroup::General), + (FeeChangeAction, ActionGroup::BundleableSudo), + (FeeAssetChangeAction, ActionGroup::BundleableSudo), + (IbcRelay, ActionGroup::BundleableGeneral), (IbcSudoChangeAction, ActionGroup::UnbundleableSudo), ); @@ -82,28 +82,31 @@ impl Action { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(super) enum ActionGroup { - General, + BundleableGeneral, UnbundleableGeneral, - Sudo, + BundleableSudo, UnbundleableSudo, } impl ActionGroup { pub(super) fn is_bundleable(self) -> bool { - matches!(self, ActionGroup::General | ActionGroup::Sudo) + matches!( + self, + ActionGroup::BundleableGeneral | ActionGroup::BundleableSudo + ) } - pub(super) fn is_sudo(self) -> bool { - matches!(self, ActionGroup::Sudo) + pub(super) fn is_bundleable_sudo(self) -> bool { + matches!(self, ActionGroup::BundleableSudo) } } impl fmt::Display for ActionGroup { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ActionGroup::General => write!(f, "general"), + ActionGroup::BundleableGeneral => write!(f, "bundleable general"), ActionGroup::UnbundleableGeneral => write!(f, "unbundleable general"), - ActionGroup::Sudo => write!(f, "sudo"), + ActionGroup::BundleableSudo => write!(f, "bundleable sudo"), ActionGroup::UnbundleableSudo => write!(f, "unbundleable sudo"), } } diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs index c88dc60d71..6aa2afb353 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action_group/tests.rs @@ -35,7 +35,7 @@ use crate::{ const ASTRIA_ADDRESS_PREFIX: &str = "astria"; #[test] -fn try_from_list_of_actions_general() { +fn try_from_list_of_actions_bundleable_general() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -92,12 +92,12 @@ fn try_from_list_of_actions_general() { assert!(matches!( Actions::try_from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::General) + Some(ActionGroup::BundleableGeneral) )); } #[test] -fn from_list_of_actions_sudo() { +fn from_list_of_actions_bundleable_sudo() { let address: Address<_> = Address::builder() .array([0; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) @@ -116,7 +116,7 @@ fn from_list_of_actions_sudo() { assert!(matches!( Actions::try_from_list_of_actions(actions).unwrap().group(), - Some(ActionGroup::Sudo) + Some(ActionGroup::BundleableSudo) )); } diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs index ba2a9db5d4..67f86add44 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/mod.rs @@ -203,9 +203,9 @@ impl SignedTransaction { } #[must_use] - pub fn is_sudo_action_group(&self) -> bool { + pub fn is_bundleable_sudo_action_group(&self) -> bool { if let Some(group) = self.transaction.actions.group() { - group.is_sudo() + group.is_bundleable_sudo() } else { false } diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index bae20b69df..7982c3df06 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -1012,7 +1012,7 @@ impl App { // flag mempool for cleaning if we ran a fee change action self.recost_mempool = self.recost_mempool - || signed_tx.is_sudo_action_group() + || signed_tx.is_bundleable_sudo_action_group() && signed_tx .actions() .iter() From 503e2e0535654ba943eaa63392581c16bfb0b12d Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 30 Sep 2024 12:38:34 -0400 Subject: [PATCH 15/18] clippy lint --- crates/astria-composer/src/executor/bundle_factory/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index 3a0dbdae77..6d5c7cafa4 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -78,7 +78,7 @@ impl SizedBundle { /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. // Method is expected to never panic because only `SequenceActions` are added to the bundle, // which should produce a valid variant of the `ActionGroup` type. - #[allow( + #[expect( clippy::panic, reason = "method is expected to never panic because only `SequenceActions` are added to \ the bundle, which should produce a valid variant of the `ActionGroup` type; \ From 6a8a937d658dbc9a0b7de7939298c5bd931e54bd Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 30 Sep 2024 12:45:10 -0400 Subject: [PATCH 16/18] clippy lint --- crates/astria-composer/src/executor/bundle_factory/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index 6d5c7cafa4..cd87911551 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -78,6 +78,7 @@ impl SizedBundle { /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. // Method is expected to never panic because only `SequenceActions` are added to the bundle, // which should produce a valid variant of the `ActionGroup` type. + #[allow(unfulfilled_lint_expectations)] #[expect( clippy::panic, reason = "method is expected to never panic because only `SequenceActions` are added to \ From 38cd7a42594fffe66e40400edb998c0c69949136 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 30 Sep 2024 12:59:03 -0400 Subject: [PATCH 17/18] clippy --- .../src/executor/bundle_factory/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index cd87911551..46fe4e0e2e 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -76,15 +76,9 @@ impl SizedBundle { } /// Constructs an [`UnsignedTransaction`] from the actions contained in the bundle and `params`. - // Method is expected to never panic because only `SequenceActions` are added to the bundle, - // which should produce a valid variant of the `ActionGroup` type. - #[allow(unfulfilled_lint_expectations)] - #[expect( - clippy::panic, - reason = "method is expected to never panic because only `SequenceActions` are added to \ - the bundle, which should produce a valid variant of the `ActionGroup` type; \ - this is checked by `tests::transaction_construction_should_not_panic" - )] + /// # Panics + /// Method is expected to never panic because only `SequenceActions` are added to the bundle, + /// which should produce a valid variant of the `ActionGroup` type. pub(super) fn to_unsigned_transaction( &self, nonce: u32, From 33c9d44f109446027854687f9442595bfe0b0f77 Mon Sep 17 00:00:00 2001 From: lilyjjo Date: Mon, 30 Sep 2024 13:07:33 -0400 Subject: [PATCH 18/18] clippy final fix pls --- crates/astria-composer/src/executor/bundle_factory/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/astria-composer/src/executor/bundle_factory/mod.rs b/crates/astria-composer/src/executor/bundle_factory/mod.rs index 46fe4e0e2e..f4fb47eaaa 100644 --- a/crates/astria-composer/src/executor/bundle_factory/mod.rs +++ b/crates/astria-composer/src/executor/bundle_factory/mod.rs @@ -89,7 +89,11 @@ impl SizedBundle { .chain_id(chain_id) .nonce(nonce) .try_build() - .unwrap() + .expect( + "method is expected to never panic because only `SequenceActions` are added to \ + the bundle, which should produce a valid variant of the `ActionGroup` type; this \ + is checked by `tests::transaction_construction_should_not_panic", + ) } /// Buffer `seq_action` into the bundle.