From ac7222e3a8c230ab187ce7387efccd7f39120795 Mon Sep 17 00:00:00 2001 From: Ethan Oroshiba Date: Fri, 13 Sep 2024 10:49:37 -0500 Subject: [PATCH] chore(sequencer): migrate from `anyhow::Result` to `eyre::Result` (#1387) ## Summary Migrate all instances of `anyhow::Result` to `eyre::Result`. ## Background Sequencer was using `anyhow::Result`, which provides an unhelpful `Display` impl and contrasts our error handling in the rest of the codebase. This change is to flush out our error handling in the sequencer, except for those parts which necessitate using `anyhow`. ## Changes - Add eyre to sequencer's `cargo.toml`. - Migrate all instances of `anyhow::Result` to `eyre::Result`, except for those that touch cnidarium directly. - Create `anyhow_to_eyre()` and `eyre_to_anyhow()` helper functions for moving between the two without breaking the source chain. ## Testing Added unit tests to ensure `eyre` and `anyhow` source chains are maintained when converting between one another. ## Related Issues closes #1386 --- Cargo.lock | 3 +- crates/astria-eyre/Cargo.toml | 1 + crates/astria-eyre/src/lib.rs | 48 ++++++ crates/astria-sequencer/Cargo.toml | 8 +- .../astria-sequencer/src/accounts/action.rs | 41 ++--- .../src/accounts/component.rs | 12 +- crates/astria-sequencer/src/accounts/query.rs | 29 ++-- .../src/accounts/state_ext.rs | 60 ++++--- .../astria-sequencer/src/address/state_ext.rs | 33 ++-- crates/astria-sequencer/src/api_state_ext.rs | 84 +++++---- .../src/app/action_handler.rs | 5 +- crates/astria-sequencer/src/app/mod.rs | 162 ++++++++++-------- crates/astria-sequencer/src/assets/query.rs | 8 +- .../astria-sequencer/src/assets/state_ext.rs | 51 +++--- .../astria-sequencer/src/authority/action.rs | 32 ++-- .../src/authority/component.rs | 27 +-- crates/astria-sequencer/src/authority/mod.rs | 9 +- .../src/authority/state_ext.rs | 38 ++-- .../src/bridge/bridge_lock_action.rs | 35 ++-- .../src/bridge/bridge_sudo_change_action.rs | 29 ++-- .../src/bridge/bridge_unlock_action.rs | 28 +-- .../astria-sequencer/src/bridge/component.rs | 2 +- .../src/bridge/init_bridge_account_action.rs | 28 +-- crates/astria-sequencer/src/bridge/query.rs | 17 +- .../astria-sequencer/src/bridge/state_ext.rs | 92 +++++----- crates/astria-sequencer/src/component.rs | 2 +- .../astria-sequencer/src/fee_asset_change.rs | 10 +- crates/astria-sequencer/src/ibc/component.rs | 10 +- .../src/ibc/host_interface.rs | 16 +- .../src/ibc/ibc_relayer_change.rs | 10 +- .../src/ibc/ics20_transfer.rs | 150 +++++++++------- .../src/ibc/ics20_withdrawal.rs | 53 +++--- crates/astria-sequencer/src/ibc/state_ext.rs | 39 +++-- crates/astria-sequencer/src/main.rs | 4 +- .../src/mempool/mempool_state.rs | 2 +- crates/astria-sequencer/src/mempool/mod.rs | 5 +- .../src/mempool/transactions_container.rs | 23 +-- .../src/proposal/block_size_constraints.rs | 19 +- .../astria-sequencer/src/sequence/action.rs | 27 +-- .../src/sequence/component.rs | 2 +- .../src/sequence/state_ext.rs | 25 +-- crates/astria-sequencer/src/sequencer.rs | 31 ++-- .../astria-sequencer/src/service/consensus.rs | 38 ++-- .../astria-sequencer/src/service/info/mod.rs | 26 +-- .../astria-sequencer/src/service/mempool.rs | 4 +- crates/astria-sequencer/src/state_ext.rs | 43 +++-- crates/astria-sequencer/src/test_utils.rs | 2 +- .../src/transaction/checks.rs | 41 ++--- .../astria-sequencer/src/transaction/mod.rs | 85 ++++----- crates/astria-sequencer/src/utils.rs | 13 +- 50 files changed, 890 insertions(+), 672 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6e24ae1f4..f1cf124250 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -732,6 +732,7 @@ dependencies = [ name = "astria-eyre" version = "0.1.0" dependencies = [ + "anyhow", "eyre", "itoa", "tokio-test", @@ -791,10 +792,10 @@ dependencies = [ name = "astria-sequencer" version = "0.17.0" dependencies = [ - "anyhow", "astria-build-info", "astria-config", "astria-core", + "astria-eyre", "astria-merkle", "astria-telemetry", "async-trait", diff --git a/crates/astria-eyre/Cargo.toml b/crates/astria-eyre/Cargo.toml index c513185ff3..ab413456e4 100644 --- a/crates/astria-eyre/Cargo.toml +++ b/crates/astria-eyre/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] eyre = "0.6" itoa = "1.0.10" +anyhow = { version = "1.0.0", optional = true } [dev-dependencies] tracing = { workspace = true } diff --git a/crates/astria-eyre/src/lib.rs b/crates/astria-eyre/src/lib.rs index a28cd48d14..905fb9e002 100644 --- a/crates/astria-eyre/src/lib.rs +++ b/crates/astria-eyre/src/lib.rs @@ -5,6 +5,13 @@ use std::{ fmt::Write as _, }; +#[cfg(feature = "anyhow")] +pub use anyhow; +#[cfg(feature = "anyhow")] +pub use anyhow_conversion::{ + anyhow_to_eyre, + eyre_to_anyhow, +}; pub use eyre; #[doc(hidden)] pub use eyre::Result; @@ -83,3 +90,44 @@ fn write_value(err: &dyn Error, f: &mut core::fmt::Formatter<'_>) -> core::fmt:: f.write_fmt(format_args!("\"{err}\""))?; Ok(()) } + +#[cfg(feature = "anyhow")] +mod anyhow_conversion { + pub fn anyhow_to_eyre(anyhow_error: anyhow::Error) -> eyre::Report { + let boxed: Box = anyhow_error.into(); + eyre::eyre!(boxed) + } + + #[must_use] + pub fn eyre_to_anyhow(eyre_error: eyre::Report) -> anyhow::Error { + let boxed: Box = eyre_error.into(); + anyhow::anyhow!(boxed) + } + + #[cfg(test)] + mod test { + #[test] + fn anyhow_to_eyre_preserves_source_chain() { + let mut errs = ["foo", "bar", "baz", "qux"]; + let anyhow_error = anyhow::anyhow!(errs[0]).context(errs[1]).context(errs[2]); + let eyre_from_anyhow = super::anyhow_to_eyre(anyhow_error).wrap_err(errs[3]); + + errs.reverse(); + for (i, err) in eyre_from_anyhow.chain().enumerate() { + assert_eq!(errs[i], &err.to_string()); + } + } + + #[test] + fn eyre_to_anyhow_preserves_source_chain() { + let mut errs = ["foo", "bar", "baz", "qux"]; + let eyre_error = eyre::eyre!(errs[0]).wrap_err(errs[1]).wrap_err(errs[2]); + let anyhow_from_eyre = super::eyre_to_anyhow(eyre_error).context(errs[3]); + + errs.reverse(); + for (i, err) in anyhow_from_eyre.chain().enumerate() { + assert_eq!(errs[i], &err.to_string()); + } + } + } +} diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index 24c5e82895..60a35107f8 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -17,13 +17,17 @@ benchmark = ["divan"] [dependencies] astria-core = { path = "../astria-core", features = ["server", "serde"] } astria-build-info = { path = "../astria-build-info", features = ["runtime"] } + +# The "anyhow" feature is only included because it is necessary for the implementation of +# `penumbra_ibc::component::HostInterface` in `crates/astria-sequencer/src/ibc/host_interface.rs`. +# Avoid using "anyhow" results anywhere else. +astria-eyre = { path = "../astria-eyre", features = ["anyhow"] } + config = { package = "astria-config", path = "../astria-config" } merkle = { package = "astria-merkle", path = "../astria-merkle" } telemetry = { package = "astria-telemetry", path = "../astria-telemetry", features = [ "display", ] } - -anyhow = "1" borsh = { version = "1", features = ["derive"] } cnidarium = { git = "https://github.com/penumbra-zone/penumbra.git", rev = "87adc8d6b15f6081c1adf169daed4ca8873bd9f6", features = [ "metrics", diff --git a/crates/astria-sequencer/src/accounts/action.rs b/crates/astria-sequencer/src/accounts/action.rs index d722bdd2e7..fea06788db 100644 --- a/crates/astria-sequencer/src/accounts/action.rs +++ b/crates/astria-sequencer/src/accounts/action.rs @@ -1,12 +1,13 @@ -use anyhow::{ - ensure, - Context, - Result, -}; use astria_core::{ protocol::transaction::v1alpha1::action::TransferAction, Protobuf as _, }; +use astria_eyre::eyre::{ + ensure, + OptionExt as _, + Result, + WrapErr as _, +}; use cnidarium::{ StateRead, StateWrite, @@ -44,7 +45,7 @@ impl ActionHandler for TransferAction { state .get_bridge_account_rollup_id(from) .await - .context("failed to get bridge account rollup id")? + .wrap_err("failed to get bridge account rollup id")? .is_none(), "cannot transfer out of bridge account; BridgeUnlock must be used", ); @@ -60,7 +61,7 @@ pub(crate) async fn execute_transfer( action: &TransferAction, from: TAddress, mut state: S, -) -> anyhow::Result<()> +) -> Result<()> where S: StateWrite, TAddress: AddressBytes, @@ -70,11 +71,11 @@ where let fee = state .get_transfer_base_fee() .await - .context("failed to get transfer base fee")?; + .wrap_err("failed to get transfer base fee")?; state .get_and_increase_block_fees(&action.fee_asset, fee, TransferAction::full_name()) .await - .context("failed to add to block fees")?; + .wrap_err("failed to add to block fees")?; // if fee payment asset is same asset as transfer asset, deduct fee // from same balance as asset transferred @@ -88,28 +89,28 @@ where state .decrease_balance(from, &action.asset, payment_amount) .await - .context("failed decreasing `from` account balance")?; + .wrap_err("failed decreasing `from` account balance")?; state .increase_balance(action.to, &action.asset, action.amount) .await - .context("failed increasing `to` account balance")?; + .wrap_err("failed increasing `to` account balance")?; } else { // otherwise, just transfer the transfer asset and deduct fee from fee asset balance // later state .decrease_balance(from, &action.asset, action.amount) .await - .context("failed decreasing `from` account balance")?; + .wrap_err("failed decreasing `from` account balance")?; state .increase_balance(action.to, &action.asset, action.amount) .await - .context("failed increasing `to` account balance")?; + .wrap_err("failed increasing `to` account balance")?; // deduct fee from fee asset balance state .decrease_balance(from, &action.fee_asset, fee) .await - .context("failed decreasing `from` account balance for fee payment")?; + .wrap_err("failed decreasing `from` account balance for fee payment")?; } Ok(()) } @@ -123,27 +124,27 @@ where S: StateRead, TAddress: AddressBytes, { - state.ensure_base_prefix(&action.to).await.context( + state.ensure_base_prefix(&action.to).await.wrap_err( "failed ensuring that the destination address matches the permitted base prefix", )?; ensure!( state .is_allowed_fee_asset(&action.fee_asset) .await - .context("failed to check allowed fee assets in state")?, + .wrap_err("failed to check allowed fee assets in state")?, "invalid fee asset", ); let fee = state .get_transfer_base_fee() .await - .context("failed to get transfer base fee")?; + .wrap_err("failed to get transfer base fee")?; let transfer_asset = action.asset.clone(); let from_fee_balance = state .get_account_balance(&from, &action.fee_asset) .await - .context("failed getting `from` account balance for fee payment")?; + .wrap_err("failed getting `from` account balance for fee payment")?; // if fee asset is same as transfer asset, ensure accounts has enough funds // to cover both the fee and the amount transferred @@ -151,7 +152,7 @@ where let payment_amount = action .amount .checked_add(fee) - .context("transfer amount plus fee overflowed")?; + .ok_or_eyre("transfer amount plus fee overflowed")?; ensure!( from_fee_balance >= payment_amount, @@ -168,7 +169,7 @@ where let from_transfer_balance = state .get_account_balance(from, transfer_asset) .await - .context("failed to get account balance in transfer check")?; + .wrap_err("failed to get account balance in transfer check")?; ensure!( from_transfer_balance >= action.amount, "insufficient funds for transfer" diff --git a/crates/astria-sequencer/src/accounts/component.rs b/crates/astria-sequencer/src/accounts/component.rs index e08d7b357a..e197a1911f 100644 --- a/crates/astria-sequencer/src/accounts/component.rs +++ b/crates/astria-sequencer/src/accounts/component.rs @@ -1,10 +1,10 @@ use std::sync::Arc; -use anyhow::{ - Context, +use astria_core::protocol::genesis::v1alpha1::GenesisAppState; +use astria_eyre::eyre::{ Result, + WrapErr as _, }; -use astria_core::protocol::genesis::v1alpha1::GenesisAppState; use tendermint::abci::request::{ BeginBlock, EndBlock, @@ -32,16 +32,16 @@ impl Component for AccountsComponent { let native_asset = state .get_native_asset() .await - .context("failed to read native asset from state")?; + .wrap_err("failed to read native asset from state")?; for account in app_state.accounts() { state .put_account_balance(account.address, &native_asset, account.balance) - .context("failed writing account balance to state")?; + .wrap_err("failed writing account balance to state")?; } state .put_transfer_base_fee(app_state.fees().transfer_base_fee) - .context("failed to put transfer base fee")?; + .wrap_err("failed to put transfer base fee")?; Ok(()) } diff --git a/crates/astria-sequencer/src/accounts/query.rs b/crates/astria-sequencer/src/accounts/query.rs index 985bdfeca6..1aaa5f6412 100644 --- a/crates/astria-sequencer/src/accounts/query.rs +++ b/crates/astria-sequencer/src/accounts/query.rs @@ -1,4 +1,3 @@ -use anyhow::Context as _; use astria_core::{ primitive::v1::{ asset, @@ -9,6 +8,11 @@ use astria_core::{ account::v1alpha1::AssetBalance, }, }; +use astria_eyre::eyre::{ + OptionExt as _, + Result, + WrapErr as _, +}; use cnidarium::{ Snapshot, StateRead, @@ -35,19 +39,19 @@ use crate::{ async fn ibc_to_trace( state: S, asset: asset::IbcPrefixed, -) -> anyhow::Result { +) -> Result { state .map_ibc_to_trace_prefixed_asset(asset) .await .context("failed to get ibc asset denom")? - .context("asset not found when user has balance of it; this is a bug") + .ok_or_eyre("asset not found when user has balance of it; this is a bug") } #[instrument(skip_all, fields(%address))] async fn get_trace_prefixed_account_balances( state: &S, address: Address, -) -> anyhow::Result> { +) -> Result> { let stream = state .account_asset_balances(address) .map_ok(|asset_balance| async move { @@ -150,10 +154,7 @@ pub(crate) async fn nonce_request( } } -async fn get_snapshot_and_height( - storage: &Storage, - height: Height, -) -> anyhow::Result<(Snapshot, Height)> { +async fn get_snapshot_and_height(storage: &Storage, height: Height) -> Result<(Snapshot, Height)> { let snapshot = match height.value() { 0 => storage.latest_snapshot(), other => { @@ -161,18 +162,18 @@ async fn get_snapshot_and_height( .latest_snapshot() .get_storage_version_by_height(other) .await - .context("failed to get storage version from height")?; + .wrap_err("failed to get storage version from height")?; storage .snapshot(version) - .context("failed to get storage at version")? + .ok_or_eyre("failed to get storage at version")? } }; let height: Height = snapshot .get_block_height() .await - .context("failed to get block height from snapshot")? + .wrap_err("failed to get block height from snapshot")? .try_into() - .context("internal u64 block height does not fit into tendermint i64 `Height`")?; + .wrap_err("internal u64 block height does not fit into tendermint i64 `Height`")?; Ok((snapshot, height)) } @@ -180,7 +181,7 @@ async fn preprocess_request( storage: &Storage, request: &request::Query, params: &[(String, String)], -) -> anyhow::Result<(Address, Snapshot, Height), response::Query> { +) -> Result<(Address, Snapshot, Height), response::Query> { let Some(address) = params .iter() .find_map(|(k, v)| (k == "account").then_some(v)) @@ -194,7 +195,7 @@ async fn preprocess_request( }; let address = address .parse() - .context("failed to parse argument as address") + .wrap_err("failed to parse argument as address") .map_err(|err| response::Query { code: Code::Err(AbciErrorCode::INVALID_PARAMETER.value()), info: AbciErrorCode::INVALID_PARAMETER.info(), diff --git a/crates/astria-sequencer/src/accounts/state_ext.rs b/crates/astria-sequencer/src/accounts/state_ext.rs index dd74ab3f18..47973be85c 100644 --- a/crates/astria-sequencer/src/accounts/state_ext.rs +++ b/crates/astria-sequencer/src/accounts/state_ext.rs @@ -7,11 +7,16 @@ use std::{ }, }; -use anyhow::{ - Context as _, - Result, -}; use astria_core::primitive::v1::asset; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + eyre, + OptionExt as _, + Result, + WrapErr as _, + }, +}; use async_trait::async_trait; use borsh::{ BorshDeserialize, @@ -88,7 +93,7 @@ where let key = match ready!(this.underlying.as_mut().poll_next(cx)) { Some(Ok(key)) => key, Some(Err(err)) => { - return Poll::Ready(Some(Err(err).context("failed reading from state"))); + return Poll::Ready(Some(Err(err).wrap_err("failed reading from state"))); } None => return Poll::Ready(None), }; @@ -114,7 +119,7 @@ pin_project! { impl Stream for AccountAssetBalancesStream where - St: Stream)>>, + St: Stream)>>, { type Item = Result; @@ -123,7 +128,9 @@ where let (key, bytes) = match ready!(this.underlying.as_mut().poll_next(cx)) { Some(Ok(tup)) => tup, Some(Err(err)) => { - return Poll::Ready(Some(Err(err).context("failed reading from state"))); + return Poll::Ready(Some(Err( + anyhow_to_eyre(err).wrap_err("failed reading from state") + ))); } None => return Poll::Ready(None), }; @@ -149,7 +156,7 @@ where fn extract_asset_from_key(s: &str) -> Result { Ok(s.strip_prefix("accounts/") .and_then(|s| s.split_once("/balance/").map(|(_, asset)| asset)) - .context("failed to strip prefix from account balance key")? + .ok_or_eyre("failed to strip prefix from account balance key")? .parse::() .context("failed to parse storage key suffix as address hunk")? .get()) @@ -192,11 +199,12 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { let Some(bytes) = self .get_raw(&balance_storage_key(address, asset)) .await - .context("failed reading raw account balance from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw account balance from state")? else { return Ok(0); }; - let Balance(balance) = Balance::try_from_slice(&bytes).context("invalid balance bytes")?; + let Balance(balance) = Balance::try_from_slice(&bytes).wrap_err("invalid balance bytes")?; Ok(balance) } @@ -205,13 +213,14 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { let bytes = self .get_raw(&nonce_storage_key(address)) .await - .context("failed reading raw account nonce from state")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw account nonce from state")?; let Some(bytes) = bytes else { // the account has not yet been initialized; return 0 return Ok(0); }; - let Nonce(nonce) = Nonce::try_from_slice(&bytes).context("invalid nonce bytes")?; + let Nonce(nonce) = Nonce::try_from_slice(&bytes).wrap_err("invalid nonce bytes")?; Ok(nonce) } @@ -220,12 +229,13 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { let bytes = self .get_raw(TRANSFER_BASE_FEE_STORAGE_KEY) .await - .context("failed reading raw transfer base fee from state")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw transfer base fee from state")?; let Some(bytes) = bytes else { - return Err(anyhow::anyhow!("transfer base fee not set")); + return Err(eyre!("transfer base fee not set")); }; - let Fee(fee) = Fee::try_from_slice(&bytes).context("invalid fee bytes")?; + let Fee(fee) = Fee::try_from_slice(&bytes).wrap_err("invalid fee bytes")?; Ok(fee) } } @@ -245,14 +255,14 @@ pub(crate) trait StateWriteExt: StateWrite { TAddress: AddressBytes, TAsset: Into + std::fmt::Display + Send, { - let bytes = borsh::to_vec(&Balance(balance)).context("failed to serialize balance")?; + let bytes = borsh::to_vec(&Balance(balance)).wrap_err("failed to serialize balance")?; self.put_raw(balance_storage_key(address, asset), bytes); Ok(()) } #[instrument(skip_all)] fn put_account_nonce(&mut self, address: T, nonce: u32) -> Result<()> { - let bytes = borsh::to_vec(&Nonce(nonce)).context("failed to serialize nonce")?; + let bytes = borsh::to_vec(&Nonce(nonce)).wrap_err("failed to serialize nonce")?; self.put_raw(nonce_storage_key(address), bytes); Ok(()) } @@ -272,15 +282,15 @@ pub(crate) trait StateWriteExt: StateWrite { let balance = self .get_account_balance(&address, asset) .await - .context("failed to get account balance")?; + .wrap_err("failed to get account balance")?; self.put_account_balance( &address, asset, balance .checked_add(amount) - .context("failed to update account balance due to overflow")?, + .ok_or_eyre("failed to update account balance due to overflow")?, ) - .context("failed to store updated account balance in database")?; + .wrap_err("failed to store updated account balance in database")?; Ok(()) } @@ -299,21 +309,21 @@ pub(crate) trait StateWriteExt: StateWrite { let balance = self .get_account_balance(&address, asset) .await - .context("failed to get account balance")?; + .wrap_err("failed to get account balance")?; self.put_account_balance( &address, asset, balance .checked_sub(amount) - .context("subtracting from account balance failed due to insufficient funds")?, + .ok_or_eyre("subtracting from account balance failed due to insufficient funds")?, ) - .context("failed to store updated account balance in database")?; + .wrap_err("failed to store updated account balance in database")?; Ok(()) } #[instrument(skip_all)] fn put_transfer_base_fee(&mut self, fee: u128) -> Result<()> { - let bytes = borsh::to_vec(&Fee(fee)).context("failed to serialize fee")?; + let bytes = borsh::to_vec(&Fee(fee)).wrap_err("failed to serialize fee")?; self.put_raw(TRANSFER_BASE_FEE_STORAGE_KEY.to_string(), bytes); Ok(()) } @@ -813,7 +823,7 @@ mod tests { .expect("increasing account balance for uninitialized account should be ok"); // decrease balance - state + let _ = state .decrease_balance(address, &asset, amount_increase + 1) .await .expect_err("should not be able to subtract larger balance than what existed"); diff --git a/crates/astria-sequencer/src/address/state_ext.rs b/crates/astria-sequencer/src/address/state_ext.rs index 3016dc36f1..ce1fe59342 100644 --- a/crates/astria-sequencer/src/address/state_ext.rs +++ b/crates/astria-sequencer/src/address/state_ext.rs @@ -1,13 +1,16 @@ -use anyhow::{ - bail, - ensure, - Context as _, - Result, -}; use astria_core::primitive::v1::{ Address, Bech32m, }; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + ensure, + Result, + WrapErr as _, + }, +}; use async_trait::async_trait; use cnidarium::{ StateRead, @@ -25,11 +28,11 @@ fn ibc_compat_prefix_key() -> &'static str { #[async_trait] pub(crate) trait StateReadExt: StateRead { - async fn ensure_base_prefix(&self, address: &Address) -> anyhow::Result<()> { + async fn ensure_base_prefix(&self, address: &Address) -> Result<()> { let prefix = self .get_base_prefix() .await - .context("failed to read base prefix from state")?; + .wrap_err("failed to read base prefix from state")?; ensure!( prefix == address.prefix(), "address has prefix `{}` but only `{prefix}` is permitted", @@ -38,16 +41,16 @@ pub(crate) trait StateReadExt: StateRead { Ok(()) } - async fn try_base_prefixed(&self, slice: &[u8]) -> anyhow::Result
{ + async fn try_base_prefixed(&self, slice: &[u8]) -> Result
{ let prefix = self .get_base_prefix() .await - .context("failed to read base prefix from state")?; + .wrap_err("failed to read base prefix from state")?; Address::builder() .slice(slice) .prefix(prefix) .try_build() - .context("failed to construct address from byte slice and state-provided base prefix") + .wrap_err("failed to construct address from byte slice and state-provided base prefix") } #[instrument(skip_all)] @@ -55,7 +58,8 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(base_prefix_key()) .await - .context("failed reading address base prefix from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading address base prefix from state")? else { bail!("no base prefix found in state"); }; @@ -67,11 +71,12 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(ibc_compat_prefix_key()) .await - .context("failed reading address ibc compat prefix from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading address ibc compat prefix from state")? else { bail!("no ibc compat prefix found in state") }; - String::from_utf8(bytes).context("prefix retrieved from storage is not valid utf8") + String::from_utf8(bytes).wrap_err("prefix retrieved from storage is not valid utf8") } } diff --git a/crates/astria-sequencer/src/api_state_ext.rs b/crates/astria-sequencer/src/api_state_ext.rs index fd2c836308..e73b0d7bbd 100644 --- a/crates/astria-sequencer/src/api_state_ext.rs +++ b/crates/astria-sequencer/src/api_state_ext.rs @@ -1,9 +1,3 @@ -use anyhow::{ - anyhow, - bail, - Context as _, - Result, -}; use astria_core::{ generated::{ primitive::v1 as primitiveRaw, @@ -18,6 +12,15 @@ use astria_core::{ }, Protobuf as _, }; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + eyre, + Result, + WrapErr as _, + }, +}; use async_trait::async_trait; use borsh::{ BorshDeserialize, @@ -138,13 +141,14 @@ pub(crate) trait StateReadExt: StateRead { let Some(hash) = self .get_raw(&key) .await - .context("failed to read block hash by height from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read block hash by height from state")? else { bail!("block hash not found for given height"); }; let hash: [u8; 32] = hash.try_into().map_err(|bytes: Vec<_>| { - anyhow!("expected 32 bytes block hash, but got {}", bytes.len()) + eyre!("expected 32 bytes block hash, but got {}", bytes.len()) })?; Ok(hash) } @@ -158,15 +162,16 @@ pub(crate) trait StateReadExt: StateRead { let Some(header_bytes) = self .get_raw(&key) .await - .context("failed to read raw sequencer block from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw sequencer block from state")? else { bail!("header not found for given block hash"); }; let raw = raw::SequencerBlockHeader::decode(header_bytes.as_slice()) - .context("failed to decode sequencer block from raw bytes")?; + .wrap_err("failed to decode sequencer block from raw bytes")?; let header = SequencerBlockHeader::try_from_raw(raw) - .context("failed to convert raw sequencer block to sequencer block")?; + .wrap_err("failed to convert raw sequencer block to sequencer block")?; Ok(header) } @@ -176,13 +181,14 @@ pub(crate) trait StateReadExt: StateRead { let Some(rollup_ids_bytes) = self .get_raw(&key) .await - .context("failed to read rollup IDs by block hash from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read rollup IDs by block hash from state")? else { bail!("rollup IDs not found for given block hash"); }; let RollupIdSeq(rollup_ids) = RollupIdSeq::try_from_slice(&rollup_ids_bytes) - .context("failed to deserialize rollup IDs list")?; + .wrap_err("failed to deserialize rollup IDs list")?; Ok(rollup_ids) } @@ -191,30 +197,31 @@ pub(crate) trait StateReadExt: StateRead { let Some(header_bytes) = self .get_raw(&sequencer_block_header_by_hash_key(hash)) .await - .context("failed to read raw sequencer block from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw sequencer block from state")? else { bail!("header not found for given block hash"); }; let header_raw = raw::SequencerBlockHeader::decode(header_bytes.as_slice()) - .context("failed to decode sequencer block from raw bytes")?; + .wrap_err("failed to decode sequencer block from raw bytes")?; let rollup_ids = self .get_rollup_ids_by_block_hash(hash) .await - .context("failed to get rollup IDs by block hash")?; + .wrap_err("failed to get rollup IDs by block hash")?; let mut rollup_transactions = Vec::with_capacity(rollup_ids.len()); for id in &rollup_ids { let key = rollup_data_by_hash_and_rollup_id_key(hash, id); - let raw = self - .get_raw(&key) - .await - .context("failed to read rollup data by block hash and rollup ID from state")?; + let raw = + self.get_raw(&key).await.map_err(anyhow_to_eyre).wrap_err( + "failed to read rollup data by block hash and rollup ID from state", + )?; if let Some(raw) = raw { let raw = raw.as_slice(); let rollup_data = raw::RollupTransactions::decode(raw) - .context("failed to decode rollup data from raw bytes")?; + .wrap_err("failed to decode rollup data from raw bytes")?; rollup_transactions.push(rollup_data); } } @@ -222,25 +229,27 @@ pub(crate) trait StateReadExt: StateRead { let Some(rollup_transactions_proof) = self .get_raw(&rollup_transactions_proof_by_hash_key(hash)) .await - .context("failed to read rollup transactions proof by block hash from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read rollup transactions proof by block hash from state")? else { bail!("rollup transactions proof not found for given block hash"); }; let rollup_transactions_proof = primitiveRaw::Proof::decode(rollup_transactions_proof.as_slice()) - .context("failed to decode rollup transactions proof from raw bytes")?; + .wrap_err("failed to decode rollup transactions proof from raw bytes")?; let Some(rollup_ids_proof) = self .get_raw(&rollup_ids_proof_by_hash_key(hash)) .await - .context("failed to read rollup IDs proof by block hash from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read rollup IDs proof by block hash from state")? else { bail!("rollup IDs proof not found for given block hash"); }; let rollup_ids_proof = primitiveRaw::Proof::decode(rollup_ids_proof.as_slice()) - .context("failed to decode rollup IDs proof from raw bytes")?; + .wrap_err("failed to decode rollup IDs proof from raw bytes")?; let raw = raw::SequencerBlock { block_hash: Bytes::copy_from_slice(hash), @@ -251,7 +260,7 @@ pub(crate) trait StateReadExt: StateRead { }; let block = SequencerBlock::try_from_raw(raw) - .context("failed to convert raw sequencer block to sequencer block")?; + .wrap_err("failed to convert raw sequencer block to sequencer block")?; Ok(block) } @@ -261,10 +270,10 @@ pub(crate) trait StateReadExt: StateRead { let hash = self .get_block_hash_by_height(height) .await - .context("failed to get block hash by height")?; + .wrap_err("failed to get block hash by height")?; self.get_sequencer_block_by_hash(&hash) .await - .context("failed to get sequencer block by hash") + .wrap_err("failed to get sequencer block by hash") } #[instrument(skip_all)] @@ -277,15 +286,16 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(&key) .await - .context("failed to read rollup data by block hash and rollup ID from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read rollup data by block hash and rollup ID from state")? else { bail!("rollup data not found for given block hash and rollup ID"); }; let raw = raw::RollupTransactions::decode(bytes.as_slice()) - .context("failed to decode rollup data from raw bytes")?; + .wrap_err("failed to decode rollup data from raw bytes")?; let rollup_transactions = RollupTransactions::try_from_raw(raw) - .context("failed to convert raw rollup transaction to rollup transaction")?; + .wrap_err("failed to convert raw rollup transaction to rollup transaction")?; Ok(rollup_transactions) } @@ -298,25 +308,27 @@ pub(crate) trait StateReadExt: StateRead { let Some(rollup_transactions_proof) = self .get_raw(&rollup_transactions_proof_by_hash_key(hash)) .await - .context("failed to read rollup transactions proof by block hash from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read rollup transactions proof by block hash from state")? else { bail!("rollup transactions proof not found for given block hash"); }; let rollup_transactions_proof = primitiveRaw::Proof::decode(rollup_transactions_proof.as_slice()) - .context("failed to decode rollup transactions proof from raw bytes")?; + .wrap_err("failed to decode rollup transactions proof from raw bytes")?; let Some(rollup_ids_proof) = self .get_raw(&rollup_ids_proof_by_hash_key(hash)) .await - .context("failed to read rollup IDs proof by block hash from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read rollup IDs proof by block hash from state")? else { bail!("rollup IDs proof not found for given block hash"); }; let rollup_ids_proof = primitiveRaw::Proof::decode(rollup_ids_proof.as_slice()) - .context("failed to decode rollup IDs proof from raw bytes")?; + .wrap_err("failed to decode rollup IDs proof from raw bytes")?; Ok((rollup_transactions_proof, rollup_ids_proof)) } @@ -350,7 +362,7 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_raw( key, borsh::to_vec(&RollupIdSeq(rollup_ids)) - .context("failed to serialize rollup IDs list")?, + .wrap_err("failed to serialize rollup IDs list")?, ); let key = sequencer_block_header_by_hash_key(&block.block_hash()); diff --git a/crates/astria-sequencer/src/app/action_handler.rs b/crates/astria-sequencer/src/app/action_handler.rs index 180f592d39..a24788a7ea 100644 --- a/crates/astria-sequencer/src/app/action_handler.rs +++ b/crates/astria-sequencer/src/app/action_handler.rs @@ -19,7 +19,8 @@ pub(crate) trait ActionHandler { // } // ``` - async fn check_stateless(&self) -> anyhow::Result<()>; + async fn check_stateless(&self) -> astria_eyre::eyre::Result<()>; - async fn check_and_execute(&self, mut state: S) -> anyhow::Result<()>; + async fn check_and_execute(&self, mut state: S) + -> astria_eyre::eyre::Result<()>; } diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index efb12c8a18..66a9e2c5a8 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -18,11 +18,6 @@ use std::{ }; pub(crate) use action_handler::ActionHandler; -use anyhow::{ - anyhow, - ensure, - Context, -}; use astria_core::{ generated::protocol::transactions::v1alpha1 as raw, protocol::{ @@ -36,6 +31,17 @@ use astria_core::{ }, sequencerblock::v1alpha1::block::SequencerBlock, }; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + ensure, + eyre, + OptionExt as _, + Result, + WrapErr as _, + }, +}; use cnidarium::{ ArcStateDeltaExt, Snapshot, @@ -181,13 +187,14 @@ impl App { snapshot: Snapshot, mempool: Mempool, metrics: &'static Metrics, - ) -> anyhow::Result { + ) -> Result { debug!("initializing App instance"); let app_hash: AppHash = snapshot .root_hash() .await - .context("failed to get current root hash")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to get current root hash")? .0 .to_vec() .try_into() @@ -217,7 +224,7 @@ impl App { genesis_state: GenesisAppState, genesis_validators: Vec, chain_id: String, - ) -> anyhow::Result { + ) -> Result { let mut state_tx = self .state .try_begin_transaction() @@ -230,9 +237,10 @@ impl App { state_tx.put_native_asset(native_asset); state_tx .put_ibc_asset(native_asset) - .context("failed to commit native asset as ibc asset to state")?; + .wrap_err("failed to commit native asset as ibc asset to state")?; - state_tx.put_chain_id_and_revision_number(chain_id.try_into().context("invalid chain ID")?); + state_tx + .put_chain_id_and_revision_number(chain_id.try_into().wrap_err("invalid chain ID")?); state_tx.put_block_height(0); for fee_asset in genesis_state.allowed_fee_assets() { @@ -242,7 +250,7 @@ impl App { // call init_chain on all components AccountsComponent::init_chain(&mut state_tx, &genesis_state) .await - .context("failed to call init_chain on AccountsComponent")?; + .wrap_err("failed to call init_chain on AccountsComponent")?; AuthorityComponent::init_chain( &mut state_tx, &AuthorityComponentAppState { @@ -251,23 +259,23 @@ impl App { }, ) .await - .context("failed to call init_chain on AuthorityComponent")?; + .wrap_err("failed to call init_chain on AuthorityComponent")?; BridgeComponent::init_chain(&mut state_tx, &genesis_state) .await - .context("failed to call init_chain on BridgeComponent")?; + .wrap_err("failed to call init_chain on BridgeComponent")?; IbcComponent::init_chain(&mut state_tx, &genesis_state) .await - .context("failed to call init_chain on IbcComponent")?; + .wrap_err("failed to call init_chain on IbcComponent")?; SequenceComponent::init_chain(&mut state_tx, &genesis_state) .await - .context("failed to call init_chain on SequenceComponent")?; + .wrap_err("failed to call init_chain on SequenceComponent")?; state_tx.apply(); let app_hash = self .prepare_commit(storage) .await - .context("failed to prepare commit")?; + .wrap_err("failed to prepare commit")?; debug!(app_hash = %telemetry::display::base64(&app_hash), "init_chain completed"); Ok(app_hash) } @@ -297,15 +305,15 @@ impl App { &mut self, prepare_proposal: abci::request::PrepareProposal, storage: Storage, - ) -> anyhow::Result { + ) -> Result { self.validator_address = Some(prepare_proposal.proposer_address); self.update_state_for_new_round(&storage); let mut block_size_constraints = BlockSizeConstraints::new( usize::try_from(prepare_proposal.max_tx_bytes) - .context("failed to convert max_tx_bytes to usize")?, + .wrap_err("failed to convert max_tx_bytes to usize")?, ) - .context("failed to create block size constraints")?; + .wrap_err("failed to create block size constraints")?; let block_data = BlockData { misbehavior: prepare_proposal.misbehavior, @@ -317,13 +325,13 @@ impl App { self.pre_execute_transactions(block_data) .await - .context("failed to prepare for executing block")?; + .wrap_err("failed to prepare for executing block")?; // ignore the txs passed by cometbft in favour of our app-side mempool let (included_tx_bytes, signed_txs_included) = self .execute_transactions_prepare_proposal(&mut block_size_constraints) .await - .context("failed to execute transactions")?; + .wrap_err("failed to execute transactions")?; self.metrics .record_proposal_transactions(signed_txs_included.len()); @@ -331,7 +339,7 @@ impl App { .state .get_block_deposits() .await - .context("failed to get block deposits in prepare_proposal")?; + .wrap_err("failed to get block deposits in prepare_proposal")?; self.metrics.record_proposal_deposits(deposits.len()); // generate commitment to sequence::Actions and deposits and commitment to the rollup IDs @@ -351,7 +359,7 @@ impl App { &mut self, process_proposal: abci::request::ProcessProposal, storage: Storage, - ) -> anyhow::Result<()> { + ) -> Result<()> { // if we proposed this block (ie. prepare_proposal was called directly before this), then // we skip execution for this `process_proposal` call. // @@ -377,17 +385,17 @@ impl App { let mut txs = VecDeque::from(process_proposal.txs); let received_rollup_datas_root: [u8; 32] = txs .pop_front() - .context("no transaction commitment in proposal")? + .ok_or_eyre("no transaction commitment in proposal")? .to_vec() .try_into() - .map_err(|_| anyhow!("transaction commitment must be 32 bytes"))?; + .map_err(|_| eyre!("transaction commitment must be 32 bytes"))?; let received_rollup_ids_root: [u8; 32] = txs .pop_front() - .context("no chain IDs commitment in proposal")? + .ok_or_eyre("no chain IDs commitment in proposal")? .to_vec() .try_into() - .map_err(|_| anyhow!("chain IDs commitment must be 32 bytes"))?; + .map_err(|_| eyre!("chain IDs commitment must be 32 bytes"))?; let expected_txs_len = txs.len(); @@ -401,7 +409,7 @@ impl App { self.pre_execute_transactions(block_data) .await - .context("failed to prepare for executing block")?; + .wrap_err("failed to prepare for executing block")?; // we don't care about the cometbft max_tx_bytes here, as cometbft would have // rejected the proposal if it was too large. @@ -420,10 +428,10 @@ impl App { self.execute_transactions_process_proposal(signed_txs.clone(), &mut block_size_constraints) .await - .context("failed to execute transactions")?; + .wrap_err("failed to execute transactions")?; let Some(execution_results) = self.execution_results.as_ref() else { - anyhow::bail!("execution results must be present after executing transactions") + bail!("execution results must be present after executing transactions") }; // all txs in the proposal should be deserializable and executable @@ -440,7 +448,7 @@ impl App { .state .get_block_deposits() .await - .context("failed to get block deposits in process_proposal")?; + .wrap_err("failed to get block deposits in process_proposal")?; self.metrics.record_proposal_deposits(deposits.len()); let GeneratedCommitments { @@ -485,7 +493,7 @@ impl App { async fn execute_transactions_prepare_proposal( &mut self, block_size_constraints: &mut BlockSizeConstraints, - ) -> anyhow::Result<(Vec, Vec)> { + ) -> Result<(Vec, Vec)> { let mempool_len = self.mempool.len().await; debug!(mempool_len, "executing transactions from mempool"); @@ -558,10 +566,10 @@ impl App { }); block_size_constraints .sequencer_checked_add(tx_sequence_data_bytes) - .context("error growing sequencer block size")?; + .wrap_err("error growing sequencer block size")?; block_size_constraints .cometbft_checked_add(tx_len) - .context("error growing cometBFT block size")?; + .wrap_err("error growing cometBFT block size")?; validated_txs.push(bytes.into()); included_signed_txs.push((*tx).clone()); } @@ -637,7 +645,7 @@ impl App { &mut self, txs: Vec, block_size_constraints: &mut BlockSizeConstraints, - ) -> anyhow::Result<()> { + ) -> Result<()> { let mut excluded_tx_count = 0_f64; let mut execution_results = Vec::new(); @@ -674,10 +682,10 @@ impl App { }); block_size_constraints .sequencer_checked_add(tx_sequence_data_bytes) - .context("error growing sequencer block size")?; + .wrap_err("error growing sequencer block size")?; block_size_constraints .cometbft_checked_add(tx_len) - .context("error growing cometBFT block size")?; + .wrap_err("error growing cometBFT block size")?; } Err(e) => { debug!( @@ -707,13 +715,13 @@ impl App { /// /// this *must* be called anytime before a block's txs are executed, whether it's /// during the proposal phase, or finalize_block phase. - #[instrument(name = "App::pre_execute_transactions", skip_all)] - async fn pre_execute_transactions(&mut self, block_data: BlockData) -> anyhow::Result<()> { + #[instrument(name = "App::pre_execute_transactions", skip_all, err)] + async fn pre_execute_transactions(&mut self, block_data: BlockData) -> Result<()> { let chain_id = self .state .get_chain_id() .await - .context("failed to get chain ID from state")?; + .wrap_err("failed to get chain ID from state")?; // reset recost flag self.recost_mempool = false; @@ -753,7 +761,7 @@ impl App { self.begin_block(&begin_block) .await - .context("failed to call begin_block")?; + .wrap_err("failed to call begin_block")?; Ok(()) } @@ -769,17 +777,17 @@ impl App { &mut self, finalize_block: abci::request::FinalizeBlock, storage: Storage, - ) -> anyhow::Result { + ) -> Result { let chain_id = self .state .get_chain_id() .await - .context("failed to get chain ID from state")?; + .wrap_err("failed to get chain ID from state")?; let sudo_address = self .state .get_sudo_address() .await - .context("failed to get sudo address from state")?; + .wrap_err("failed to get sudo address from state")?; // convert tendermint id to astria address; this assumes they are // the same address, as they are both ed25519 keys @@ -788,7 +796,7 @@ impl App { let height = finalize_block.height; let time = finalize_block.time; let Hash::Sha256(block_hash) = finalize_block.hash else { - anyhow::bail!("finalized block hash is empty; this should not occur") + bail!("finalized block hash is empty; this should not occur") }; // If we previously executed txs in a different proposal than is being processed, @@ -821,12 +829,12 @@ impl App { self.pre_execute_transactions(block_data) .await - .context("failed to execute block")?; + .wrap_err("failed to execute block")?; // skip the first two transactions, as they are the rollup data commitments for tx in finalize_block.txs.iter().skip(2) { let signed_tx = signed_transaction_from_bytes(tx) - .context("protocol error; only valid txs should be finalized")?; + .wrap_err("protocol error; only valid txs should be finalized")?; match self.execute_transaction(Arc::new(signed_tx)).await { Ok(events) => tx_results.push(ExecTxResult { @@ -869,11 +877,11 @@ impl App { .state .get_block_deposits() .await - .context("failed to get block deposits in end_block")?; + .wrap_err("failed to get block deposits in end_block")?; state_tx .clear_block_deposits() .await - .context("failed to clear block deposits")?; + .wrap_err("failed to clear block deposits")?; debug!( deposits = %telemetry::display::json(&deposits), "got block deposits from state" @@ -892,10 +900,10 @@ impl App { .collect(), deposits, ) - .context("failed to convert block info and data to SequencerBlock")?; + .wrap_err("failed to convert block info and data to SequencerBlock")?; state_tx .put_sequencer_block(sequencer_block) - .context("failed to write sequencer block to state")?; + .wrap_err("failed to write sequencer block to state")?; // update the priority of any txs in the mempool based on the updated app state if self.recost_mempool { @@ -911,7 +919,7 @@ impl App { let app_hash = self .prepare_commit(storage.clone()) .await - .context("failed to prepare commit")?; + .wrap_err("failed to prepare commit")?; Ok(abci::response::FinalizeBlock { events: end_block.events, @@ -922,7 +930,8 @@ impl App { }) } - async fn prepare_commit(&mut self, storage: Storage) -> anyhow::Result { + #[instrument(skip_all, err)] + async fn prepare_commit(&mut self, storage: Storage) -> Result { // extract the state we've built up to so we can prepare it as a `StagedWriteBatch`. let dummy_state = StateDelta::new(storage.latest_snapshot()); let mut state = Arc::try_unwrap(std::mem::replace(&mut self.state, Arc::new(dummy_state))) @@ -944,13 +953,14 @@ impl App { let write_batch = storage .prepare_commit(state) .await - .context("failed to prepare commit")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed to prepare commit")?; let app_hash: AppHash = write_batch .root_hash() .0 .to_vec() .try_into() - .context("failed to convert app hash")?; + .wrap_err("failed to convert app hash")?; self.write_batch = Some(write_batch); Ok(app_hash) } @@ -959,7 +969,7 @@ impl App { async fn begin_block( &mut self, begin_block: &abci::request::BeginBlock, - ) -> anyhow::Result> { + ) -> Result> { let mut state_tx = StateDelta::new(self.state.clone()); // store the block height @@ -971,19 +981,19 @@ impl App { let mut arc_state_tx = Arc::new(state_tx); AccountsComponent::begin_block(&mut arc_state_tx, begin_block) .await - .context("failed to call begin_block on AccountsComponent")?; + .wrap_err("failed to call begin_block on AccountsComponent")?; AuthorityComponent::begin_block(&mut arc_state_tx, begin_block) .await - .context("failed to call begin_block on AuthorityComponent")?; + .wrap_err("failed to call begin_block on AuthorityComponent")?; BridgeComponent::begin_block(&mut arc_state_tx, begin_block) .await - .context("failed to call begin_block on BridgeComponent")?; + .wrap_err("failed to call begin_block on BridgeComponent")?; IbcComponent::begin_block(&mut arc_state_tx, begin_block) .await - .context("failed to call begin_block on IbcComponent")?; + .wrap_err("failed to call begin_block on IbcComponent")?; SequenceComponent::begin_block(&mut arc_state_tx, begin_block) .await - .context("failed to call begin_block on SequenceComponent")?; + .wrap_err("failed to call begin_block on SequenceComponent")?; let state_tx = Arc::try_unwrap(arc_state_tx) .expect("components should not retain copies of shared state"); @@ -996,11 +1006,11 @@ impl App { async fn execute_transaction( &mut self, signed_tx: Arc, - ) -> anyhow::Result> { + ) -> Result> { signed_tx .check_stateless() .await - .context("stateless check failed")?; + .wrap_err("stateless check failed")?; let mut state_tx = self .state @@ -1010,7 +1020,7 @@ impl App { signed_tx .check_and_execute(&mut state_tx) .await - .context("failed executing transaction")?; + .wrap_err("failed executing transaction")?; // flag mempool for cleaning if we ran a fee change action self.recost_mempool = self.recost_mempool @@ -1027,7 +1037,7 @@ impl App { &mut self, height: u64, fee_recipient: [u8; 20], - ) -> anyhow::Result { + ) -> Result { let state_tx = StateDelta::new(self.state.clone()); let mut arc_state_tx = Arc::new(state_tx); @@ -1040,19 +1050,19 @@ impl App { // call end_block on all components AccountsComponent::end_block(&mut arc_state_tx, &end_block) .await - .context("failed to call end_block on AccountsComponent")?; + .wrap_err("failed to call end_block on AccountsComponent")?; AuthorityComponent::end_block(&mut arc_state_tx, &end_block) .await - .context("failed to call end_block on AuthorityComponent")?; + .wrap_err("failed to call end_block on AuthorityComponent")?; BridgeComponent::end_block(&mut arc_state_tx, &end_block) .await - .context("failed to call end_block on BridgeComponent")?; + .wrap_err("failed to call end_block on BridgeComponent")?; IbcComponent::end_block(&mut arc_state_tx, &end_block) .await - .context("failed to call end_block on IbcComponent")?; + .wrap_err("failed to call end_block on IbcComponent")?; SequenceComponent::end_block(&mut arc_state_tx, &end_block) .await - .context("failed to call end_block on SequenceComponent")?; + .wrap_err("failed to call end_block on SequenceComponent")?; let mut state_tx = Arc::try_unwrap(arc_state_tx) .expect("components should not retain copies of shared state"); @@ -1072,13 +1082,13 @@ impl App { .state .get_block_fees() .await - .context("failed to get block fees")?; + .wrap_err("failed to get block fees")?; for (asset, amount) in fees { state_tx .increase_balance(fee_recipient, asset, amount) .await - .context("failed to increase fee recipient balance")?; + .wrap_err("failed to increase fee recipient balance")?; } // clear block fees @@ -1088,7 +1098,7 @@ impl App { Ok(abci::response::EndBlock { validator_updates: validator_updates .try_into_cometbft() - .context("failed converting astria validators to cometbft compatible type")?, + .wrap_err("failed converting astria validators to cometbft compatible type")?, events, ..Default::default() }) @@ -1163,11 +1173,11 @@ struct BlockData { proposer_address: account::Id, } -fn signed_transaction_from_bytes(bytes: &[u8]) -> anyhow::Result { +fn signed_transaction_from_bytes(bytes: &[u8]) -> Result { let raw = raw::SignedTransaction::decode(bytes) - .context("failed to decode protobuf to signed transaction")?; + .wrap_err("failed to decode protobuf to signed transaction")?; let tx = SignedTransaction::try_from_raw(raw) - .context("failed to transform raw signed transaction to verified type")?; + .wrap_err("failed to transform raw signed transaction to verified type")?; Ok(tx) } diff --git a/crates/astria-sequencer/src/assets/query.rs b/crates/astria-sequencer/src/assets/query.rs index f4f88afa1d..b6b3f1c9ee 100644 --- a/crates/astria-sequencer/src/assets/query.rs +++ b/crates/astria-sequencer/src/assets/query.rs @@ -1,4 +1,3 @@ -use anyhow::Context as _; use astria_core::{ primitive::v1::asset, protocol::{ @@ -6,6 +5,7 @@ use astria_core::{ asset::v1alpha1::AllowedFeeAssetsResponse, }, }; +use astria_eyre::eyre::WrapErr as _; use cnidarium::Storage; use hex::FromHex as _; use prost::Message as _; @@ -89,9 +89,7 @@ pub(crate) async fn denom_request( } } -fn preprocess_request( - params: &[(String, String)], -) -> anyhow::Result { +fn preprocess_request(params: &[(String, String)]) -> Result { let Some(asset_id) = params.iter().find_map(|(k, v)| (k == "id").then_some(v)) else { return Err(response::Query { code: Code::Err(AbciErrorCode::INVALID_PARAMETER.value()), @@ -101,7 +99,7 @@ fn preprocess_request( }); }; let asset = <[u8; 32]>::from_hex(asset_id) - .context("failed decoding hex encoded bytes") + .wrap_err("failed decoding hex encoded bytes") .map(asset::IbcPrefixed::new) .map_err(|err| response::Query { code: Code::Err(AbciErrorCode::INVALID_PARAMETER.value()), diff --git a/crates/astria-sequencer/src/assets/state_ext.rs b/crates/astria-sequencer/src/assets/state_ext.rs index 48deeaf6a0..3a795a87ee 100644 --- a/crates/astria-sequencer/src/assets/state_ext.rs +++ b/crates/astria-sequencer/src/assets/state_ext.rs @@ -1,9 +1,13 @@ -use anyhow::{ - bail, - Context as _, - Result, -}; use astria_core::primitive::v1::asset; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + OptionExt as _, + Result, + WrapErr as _, + }, +}; use async_trait::async_trait; use borsh::{ BorshDeserialize, @@ -69,15 +73,16 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .nonverifiable_get_raw(NATIVE_ASSET_KEY) .await - .context("failed to read raw native asset from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw native asset from state")? else { bail!("native asset denom not found in state"); }; let asset = std::str::from_utf8(&bytes) - .context("bytes stored in state not utf8 encoded")? + .wrap_err("bytes stored in state not utf8 encoded")? .parse::() - .context("failed to parse bytes retrieved from state as trace prefixed IBC asset")?; + .wrap_err("failed to parse bytes retrieved from state as trace prefixed IBC asset")?; Ok(asset) } @@ -89,7 +94,8 @@ pub(crate) trait StateReadExt: StateRead { Ok(self .get_raw(&asset_storage_key(asset)) .await - .context("failed reading raw asset from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw asset from state")? .is_some()) } @@ -101,16 +107,17 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(&asset_storage_key(asset)) .await - .context("failed reading raw asset from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw asset from state")? else { return Ok(None); }; let DenominationTrace(denom_str) = - DenominationTrace::try_from_slice(&bytes).context("invalid asset bytes")?; + DenominationTrace::try_from_slice(&bytes).wrap_err("invalid asset bytes")?; let denom = denom_str .parse() - .context("failed to parse retrieved denom string as a Denom")?; + .wrap_err("failed to parse retrieved denom string as a Denom")?; Ok(Some(denom)) } @@ -127,9 +134,9 @@ pub(crate) trait StateReadExt: StateRead { .strip_prefix(BLOCK_FEES_PREFIX.as_bytes()) .expect("prefix must always be present"); let asset = std::str::from_utf8(suffix) - .context("key suffix was not utf8 encoded; this should not happen")? + .wrap_err("key suffix was not utf8 encoded; this should not happen")? .parse::() - .context("failed to parse storage key suffix as address hunk")? + .wrap_err("failed to parse storage key suffix as address hunk")? .get(); let Ok(bytes): Result<[u8; 16], _> = value.try_into() else { @@ -150,7 +157,8 @@ pub(crate) trait StateReadExt: StateRead { Ok(self .nonverifiable_get_raw(fee_asset_key(asset).as_bytes()) .await - .context("failed to read raw fee asset from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw fee asset from state")? .is_some()) } @@ -166,9 +174,9 @@ pub(crate) trait StateReadExt: StateRead { .strip_prefix(FEE_ASSET_PREFIX.as_bytes()) .expect("prefix must always be present"); let asset = std::str::from_utf8(suffix) - .context("key suffix was not utf8 encoded; this should not happen")? + .wrap_err("key suffix was not utf8 encoded; this should not happen")? .parse::() - .context("failed to parse storage key suffix as address hunk")? + .wrap_err("failed to parse storage key suffix as address hunk")? .get(); assets.push(asset); } @@ -189,7 +197,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_ibc_asset(&mut self, asset: &asset::TracePrefixed) -> Result<()> { let bytes = borsh::to_vec(&DenominationTrace(asset.to_string())) - .context("failed to serialize asset")?; + .wrap_err("failed to serialize asset")?; self.put_raw(asset_storage_key(asset), bytes); Ok(()) } @@ -211,7 +219,8 @@ pub(crate) trait StateWriteExt: StateWrite { let current_amount = self .nonverifiable_get_raw(block_fees_key.as_bytes()) .await - .context("failed to read raw block fees from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw block fees from state")? .map(|bytes| { let Ok(bytes): Result<[u8; 16], _> = bytes.try_into() else { // this shouldn't happen @@ -224,7 +233,7 @@ pub(crate) trait StateWriteExt: StateWrite { let new_amount = current_amount .checked_add(amount) - .context("block fees overflowed u128")?; + .ok_or_eyre("block fees overflowed u128")?; self.nonverifiable_put_raw(block_fees_key.into(), new_amount.to_be_bytes().to_vec()); @@ -297,7 +306,7 @@ mod tests { let mut state = StateDelta::new(snapshot); // doesn't exist at first - state + let _ = state .get_native_asset() .await .expect_err("no native asset denom should exist at first"); diff --git a/crates/astria-sequencer/src/authority/action.rs b/crates/astria-sequencer/src/authority/action.rs index 1997c30f0a..5253461e30 100644 --- a/crates/astria-sequencer/src/authority/action.rs +++ b/crates/astria-sequencer/src/authority/action.rs @@ -1,15 +1,15 @@ -use anyhow::{ - bail, - ensure, - Context as _, - Result, -}; use astria_core::protocol::transaction::v1alpha1::action::{ FeeChange, FeeChangeAction, SudoAddressChangeAction, ValidatorUpdate, }; +use astria_eyre::eyre::{ + bail, + ensure, + Result, + WrapErr as _, +}; use cnidarium::StateWrite; use crate::{ @@ -41,7 +41,7 @@ impl ActionHandler for ValidatorUpdate { let sudo_address = state .get_sudo_address() .await - .context("failed to get sudo address from state")?; + .wrap_err("failed to get sudo address from state")?; ensure!(sudo_address == from, "signer is not the sudo key"); // ensure that we're not removing the last validator or a validator @@ -50,7 +50,7 @@ impl ActionHandler for ValidatorUpdate { let validator_set = state .get_validator_set() .await - .context("failed to get validator set from state")?; + .wrap_err("failed to get validator set from state")?; // check that validator exists if validator_set .get(self.verification_key.address_bytes()) @@ -66,11 +66,11 @@ impl ActionHandler for ValidatorUpdate { let mut validator_updates = state .get_validator_updates() .await - .context("failed getting validator updates from state")?; + .wrap_err("failed getting validator updates from state")?; validator_updates.push_update(self.clone()); state .put_validator_updates(validator_updates) - .context("failed to put validator updates in state")?; + .wrap_err("failed to put validator updates in state")?; Ok(()) } } @@ -91,16 +91,16 @@ impl ActionHandler for SudoAddressChangeAction { state .ensure_base_prefix(&self.new_address) .await - .context("desired new sudo address has an unsupported prefix")?; + .wrap_err("desired new sudo address has an unsupported prefix")?; // ensure signer is the valid `sudo` key in state let sudo_address = state .get_sudo_address() .await - .context("failed to get sudo address from state")?; + .wrap_err("failed to get sudo address from state")?; ensure!(sudo_address == from, "signer is not the sudo key"); state .put_sudo_address(self.new_address) - .context("failed to put sudo address in state")?; + .wrap_err("failed to put sudo address in state")?; Ok(()) } } @@ -122,14 +122,14 @@ impl ActionHandler for FeeChangeAction { let sudo_address = state .get_sudo_address() .await - .context("failed to get sudo address from state")?; + .wrap_err("failed to get sudo address from state")?; ensure!(sudo_address == from, "signer is not the sudo key"); match self.fee_change { FeeChange::TransferBaseFee => { state .put_transfer_base_fee(self.new_value) - .context("failed to put transfer base fee in state")?; + .wrap_err("failed to put transfer base fee in state")?; } FeeChange::SequenceBaseFee => state.put_sequence_action_base_fee(self.new_value), FeeChange::SequenceByteCostMultiplier => { @@ -147,7 +147,7 @@ impl ActionHandler for FeeChangeAction { FeeChange::Ics20WithdrawalBaseFee => { state .put_ics20_withdrawal_base_fee(self.new_value) - .context("failed to put ics20 withdrawal base fee in state")?; + .wrap_err("failed to put ics20 withdrawal base fee in state")?; } } diff --git a/crates/astria-sequencer/src/authority/component.rs b/crates/astria-sequencer/src/authority/component.rs index 91d8d71c7b..1ab9f1a321 100644 --- a/crates/astria-sequencer/src/authority/component.rs +++ b/crates/astria-sequencer/src/authority/component.rs @@ -1,13 +1,14 @@ use std::sync::Arc; -use anyhow::{ - Context, - Result, -}; use astria_core::{ primitive::v1::Address, protocol::transaction::v1alpha1::action::ValidatorUpdate, }; +use astria_eyre::eyre::{ + OptionExt as _, + Result, + WrapErr as _, +}; use tendermint::abci::request::{ BeginBlock, EndBlock, @@ -39,11 +40,11 @@ impl Component for AuthorityComponent { // set sudo key and initial validator set state .put_sudo_address(app_state.authority_sudo_address) - .context("failed to set sudo key")?; + .wrap_err("failed to set sudo key")?; let genesis_validators = app_state.genesis_validators.clone(); state .put_validator_set(ValidatorSet::new_from_updates(genesis_validators)) - .context("failed to set validator set")?; + .wrap_err("failed to set validator set")?; Ok(()) } @@ -55,17 +56,17 @@ impl Component for AuthorityComponent { let mut current_set = state .get_validator_set() .await - .context("failed getting validator set")?; + .wrap_err("failed getting validator set")?; for misbehaviour in &begin_block.byzantine_validators { current_set.remove(misbehaviour.validator.address); } let state = Arc::get_mut(state) - .context("must only have one reference to the state; this is a bug")?; + .ok_or_eyre("must only have one reference to the state; this is a bug")?; state .put_validator_set(current_set) - .context("failed putting validator set")?; + .wrap_err("failed putting validator set")?; Ok(()) } @@ -78,19 +79,19 @@ impl Component for AuthorityComponent { let validator_updates = state .get_validator_updates() .await - .context("failed getting validator updates")?; + .wrap_err("failed getting validator updates")?; let mut current_set = state .get_validator_set() .await - .context("failed getting validator set")?; + .wrap_err("failed getting validator set")?; current_set.apply_updates(validator_updates); let state = Arc::get_mut(state) - .context("must only have one reference to the state; this is a bug")?; + .ok_or_eyre("must only have one reference to the state; this is a bug")?; state .put_validator_set(current_set) - .context("failed putting validator set")?; + .wrap_err("failed putting validator set")?; Ok(()) } } diff --git a/crates/astria-sequencer/src/authority/mod.rs b/crates/astria-sequencer/src/authority/mod.rs index 5dba34b9a1..b3d8050e85 100644 --- a/crates/astria-sequencer/src/authority/mod.rs +++ b/crates/astria-sequencer/src/authority/mod.rs @@ -4,12 +4,15 @@ mod state_ext; use std::collections::BTreeMap; -use anyhow::Context as _; use astria_core::{ crypto::VerificationKey, primitive::v1::ADDRESS_LEN, protocol::transaction::v1alpha1::action::ValidatorUpdate, }; +use astria_eyre::eyre::{ + Result, + WrapErr as _, +}; use serde::{ Deserialize, Serialize, @@ -79,11 +82,11 @@ impl ValidatorSet { } } - pub(crate) fn try_into_cometbft(self) -> anyhow::Result> { + pub(crate) fn try_into_cometbft(self) -> Result> { self.0 .into_values() .map(crate::utils::sequencer_to_cometbft_validator) .collect::, _>>() - .context("failed to map one or more astria validators to cometbft validators") + .wrap_err("failed to map one or more astria validators to cometbft validators") } } diff --git a/crates/astria-sequencer/src/authority/state_ext.rs b/crates/astria-sequencer/src/authority/state_ext.rs index 2219b4ca49..3196cd212e 100644 --- a/crates/astria-sequencer/src/authority/state_ext.rs +++ b/crates/astria-sequencer/src/authority/state_ext.rs @@ -1,11 +1,14 @@ use std::collections::BTreeMap; -use anyhow::{ - bail, - Context, - Result, -}; use astria_core::primitive::v1::ADDRESS_LEN; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + Result, + WrapErr as _, + }, +}; use async_trait::async_trait; use borsh::{ BorshDeserialize, @@ -35,13 +38,14 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(SUDO_STORAGE_KEY) .await - .context("failed reading raw sudo key from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw sudo key from state")? else { // return error because sudo key must be set bail!("sudo key not found"); }; let SudoAddress(address_bytes) = - SudoAddress::try_from_slice(&bytes).context("invalid sudo key bytes")?; + SudoAddress::try_from_slice(&bytes).wrap_err("invalid sudo key bytes")?; Ok(address_bytes) } @@ -50,14 +54,15 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(VALIDATOR_SET_STORAGE_KEY) .await - .context("failed reading raw validator set from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw validator set from state")? else { // return error because validator set must be set bail!("validator set not found") }; let ValidatorSet(validator_set) = - serde_json::from_slice(&bytes).context("invalid validator set bytes")?; + serde_json::from_slice(&bytes).wrap_err("invalid validator set bytes")?; Ok(ValidatorSet(validator_set)) } @@ -66,14 +71,15 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .nonverifiable_get_raw(VALIDATOR_UPDATES_KEY) .await - .context("failed reading raw validator updates from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw validator updates from state")? else { // return empty set because validator updates are optional return Ok(ValidatorSet(BTreeMap::new())); }; let validator_updates: ValidatorSet = - serde_json::from_slice(&bytes).context("invalid validator updates bytes")?; + serde_json::from_slice(&bytes).wrap_err("invalid validator updates bytes")?; Ok(validator_updates) } } @@ -87,7 +93,7 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_raw( SUDO_STORAGE_KEY.to_string(), borsh::to_vec(&SudoAddress(address.address_bytes())) - .context("failed to convert sudo address to vec")?, + .wrap_err("failed to convert sudo address to vec")?, ); Ok(()) } @@ -96,7 +102,7 @@ pub(crate) trait StateWriteExt: StateWrite { fn put_validator_set(&mut self, validator_set: ValidatorSet) -> Result<()> { self.put_raw( VALIDATOR_SET_STORAGE_KEY.to_string(), - serde_json::to_vec(&validator_set).context("failed to serialize validator set")?, + serde_json::to_vec(&validator_set).wrap_err("failed to serialize validator set")?, ); Ok(()) } @@ -106,7 +112,7 @@ pub(crate) trait StateWriteExt: StateWrite { self.nonverifiable_put_raw( VALIDATOR_UPDATES_KEY.to_vec(), serde_json::to_vec(&validator_updates) - .context("failed to serialize validator updates")?, + .wrap_err("failed to serialize validator updates")?, ); Ok(()) } @@ -153,7 +159,7 @@ mod tests { state.put_base_prefix(ASTRIA_PREFIX); // doesn't exist at first - state + let _ = state .get_sudo_address() .await .expect_err("no sudo address should exist at first"); @@ -194,7 +200,7 @@ mod tests { let state = StateDelta::new(snapshot); // doesn't exist at first - state + let _ = state .get_validator_set() .await .expect_err("no validator set should exist at first"); diff --git a/crates/astria-sequencer/src/bridge/bridge_lock_action.rs b/crates/astria-sequencer/src/bridge/bridge_lock_action.rs index da913778d6..14c3af5e62 100644 --- a/crates/astria-sequencer/src/bridge/bridge_lock_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_lock_action.rs @@ -1,8 +1,3 @@ -use anyhow::{ - ensure, - Context as _, - Result, -}; use astria_core::{ protocol::transaction::v1alpha1::action::{ BridgeLockAction, @@ -11,6 +6,12 @@ use astria_core::{ sequencerblock::v1alpha1::block::Deposit, Protobuf as _, }; +use astria_eyre::eyre::{ + ensure, + OptionExt as _, + Result, + WrapErr as _, +}; use cnidarium::StateWrite; use crate::{ @@ -47,18 +48,18 @@ impl ActionHandler for BridgeLockAction { state .ensure_base_prefix(&self.to) .await - .context("failed check for base prefix of destination address")?; + .wrap_err("failed check for base prefix of destination address")?; // ensure the recipient is a bridge account. let rollup_id = state .get_bridge_account_rollup_id(self.to) .await - .context("failed to get bridge account rollup id")? - .ok_or_else(|| anyhow::anyhow!("bridge lock must be sent to a bridge account"))?; + .wrap_err("failed to get bridge account rollup id")? + .ok_or_eyre("bridge lock must be sent to a bridge account")?; let allowed_asset = state .get_bridge_account_ibc_asset(self.to) .await - .context("failed to get bridge account asset ID")?; + .wrap_err("failed to get bridge account asset ID")?; ensure!( allowed_asset == self.asset.to_ibc_prefixed(), "asset ID is not authorized for transfer to bridge account", @@ -67,7 +68,7 @@ impl ActionHandler for BridgeLockAction { let from_balance = state .get_account_balance(from, &self.fee_asset) .await - .context("failed to get sender account balance")?; + .wrap_err("failed to get sender account balance")?; let transfer_fee = state .get_transfer_base_fee() .await @@ -96,7 +97,7 @@ impl ActionHandler for BridgeLockAction { let byte_cost_multiplier = state .get_bridge_lock_byte_cost_multiplier() .await - .context("failed to get byte cost multiplier")?; + .wrap_err("failed to get byte cost multiplier")?; let fee = byte_cost_multiplier .saturating_mul(get_deposit_byte_len(&deposit)) .saturating_add(transfer_fee); @@ -124,22 +125,22 @@ impl ActionHandler for BridgeLockAction { let byte_cost_multiplier = state .get_bridge_lock_byte_cost_multiplier() .await - .context("failed to get byte cost multiplier")?; + .wrap_err("failed to get byte cost multiplier")?; let fee = byte_cost_multiplier.saturating_mul(get_deposit_byte_len(&deposit)); state .get_and_increase_block_fees(&self.fee_asset, fee, Self::full_name()) .await - .context("failed to add to block fees")?; + .wrap_err("failed to add to block fees")?; state .decrease_balance(from, &self.fee_asset, fee) .await - .context("failed to deduct fee from account balance")?; + .wrap_err("failed to deduct fee from account balance")?; state.record(deposit_abci_event); state .put_deposit_event(deposit) .await - .context("failed to put deposit event into state")?; + .wrap_err("failed to put deposit event into state")?; Ok(()) } } @@ -164,7 +165,7 @@ mod tests { use crate::{ address::StateWriteExt as _, test_utils::{ - assert_anyhow_error, + assert_eyre_error, astria_address, ASTRIA_PREFIX, }, @@ -218,7 +219,7 @@ mod tests { state .put_account_balance(from_address, &asset, 100 + transfer_fee) .unwrap(); - assert_anyhow_error( + assert_eyre_error( &bridge_lock.check_and_execute(&mut state).await.unwrap_err(), "insufficient funds for fee payment", ); diff --git a/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs b/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs index a7390df048..39ae6e2e48 100644 --- a/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs @@ -1,12 +1,13 @@ -use anyhow::{ - ensure, - Context as _, - Result, -}; use astria_core::{ protocol::transaction::v1alpha1::action::BridgeSudoChangeAction, Protobuf as _, }; +use astria_eyre::eyre::{ + bail, + ensure, + Result, + WrapErr as _, +}; use cnidarium::StateWrite; use crate::{ @@ -37,25 +38,25 @@ impl ActionHandler for BridgeSudoChangeAction { state .ensure_base_prefix(&self.bridge_address) .await - .context("failed check for base prefix of bridge address")?; + .wrap_err("failed check for base prefix of bridge address")?; if let Some(new_sudo_address) = &self.new_sudo_address { state .ensure_base_prefix(new_sudo_address) .await - .context("failed check for base prefix of new sudo address")?; + .wrap_err("failed check for base prefix of new sudo address")?; } if let Some(new_withdrawer_address) = &self.new_withdrawer_address { state .ensure_base_prefix(new_withdrawer_address) .await - .context("failed check for base prefix of new withdrawer address")?; + .wrap_err("failed check for base prefix of new withdrawer address")?; } ensure!( state .is_allowed_fee_asset(&self.fee_asset) .await - .context("failed to check allowed fee assets in state")?, + .wrap_err("failed to check allowed fee assets in state")?, "invalid fee asset", ); @@ -63,11 +64,11 @@ impl ActionHandler for BridgeSudoChangeAction { let Some(sudo_address) = state .get_bridge_account_sudo_address(self.bridge_address) .await - .context("failed to get bridge account sudo address")? + .wrap_err("failed to get bridge account sudo address")? else { // TODO: if the sudo address is unset, should we still allow this action // if the sender if the bridge address itself? - anyhow::bail!("bridge account does not have an associated sudo address"); + bail!("bridge account does not have an associated sudo address"); }; ensure!( @@ -78,15 +79,15 @@ impl ActionHandler for BridgeSudoChangeAction { let fee = state .get_bridge_sudo_change_base_fee() .await - .context("failed to get bridge sudo change fee")?; + .wrap_err("failed to get bridge sudo change fee")?; state .get_and_increase_block_fees(&self.fee_asset, fee, Self::full_name()) .await - .context("failed to add to block fees")?; + .wrap_err("failed to add to block fees")?; state .decrease_balance(self.bridge_address, &self.fee_asset, fee) .await - .context("failed to decrease balance for bridge sudo change fee")?; + .wrap_err("failed to decrease balance for bridge sudo change fee")?; if let Some(sudo_address) = self.new_sudo_address { state.put_bridge_account_sudo_address(self.bridge_address, sudo_address); diff --git a/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs b/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs index 505671f2cb..a5641c9720 100644 --- a/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs @@ -1,13 +1,13 @@ -use anyhow::{ - bail, - ensure, - Context as _, - Result, -}; use astria_core::protocol::transaction::v1alpha1::action::{ BridgeUnlockAction, TransferAction, }; +use astria_eyre::eyre::{ + bail, + ensure, + Result, + WrapErr as _, +}; use cnidarium::StateWrite; use crate::{ @@ -53,22 +53,22 @@ impl ActionHandler for BridgeUnlockAction { state .ensure_base_prefix(&self.to) .await - .context("failed check for base prefix of destination address")?; + .wrap_err("failed check for base prefix of destination address")?; state .ensure_base_prefix(&self.bridge_address) .await - .context("failed check for base prefix of bridge address")?; + .wrap_err("failed check for base prefix of bridge address")?; let asset = state .get_bridge_account_ibc_asset(self.bridge_address) .await - .context("failed to get bridge's asset id, must be a bridge account")?; + .wrap_err("failed to get bridge's asset id, must be a bridge account")?; // check that the sender of this tx is the authorized withdrawer for the bridge account let Some(withdrawer_address) = state .get_bridge_account_withdrawer_address(self.bridge_address) .await - .context("failed to get bridge account withdrawer address")? + .wrap_err("failed to get bridge account withdrawer address")? else { bail!("bridge account does not have an associated withdrawer address"); }; @@ -119,7 +119,7 @@ mod tests { assets::StateWriteExt as _, bridge::StateWriteExt as _, test_utils::{ - assert_anyhow_error, + assert_eyre_error, astria_address, ASTRIA_PREFIX, }, @@ -166,7 +166,7 @@ mod tests { }; // invalid sender, doesn't match action's `from`, should fail - assert_anyhow_error( + assert_eyre_error( &bridge_unlock.check_and_execute(state).await.unwrap_err(), "bridge account does not have an associated withdrawer address", ); @@ -207,7 +207,7 @@ mod tests { }; // invalid sender, doesn't match action's bridge account's withdrawer, should fail - assert_anyhow_error( + assert_eyre_error( &bridge_unlock.check_and_execute(state).await.unwrap_err(), "unauthorized to unlock bridge account", ); @@ -265,7 +265,7 @@ mod tests { .check_and_execute(&mut state) .await .unwrap(); - assert_anyhow_error( + assert_eyre_error( &bridge_unlock_second .check_and_execute(&mut state) .await diff --git a/crates/astria-sequencer/src/bridge/component.rs b/crates/astria-sequencer/src/bridge/component.rs index 88c105e9d0..0c38b7d178 100644 --- a/crates/astria-sequencer/src/bridge/component.rs +++ b/crates/astria-sequencer/src/bridge/component.rs @@ -1,7 +1,7 @@ use std::sync::Arc; -use anyhow::Result; use astria_core::protocol::genesis::v1alpha1::GenesisAppState; +use astria_eyre::eyre::Result; use tendermint::abci::request::{ BeginBlock, EndBlock, diff --git a/crates/astria-sequencer/src/bridge/init_bridge_account_action.rs b/crates/astria-sequencer/src/bridge/init_bridge_account_action.rs index 5a9c1575a1..d8a2dedaad 100644 --- a/crates/astria-sequencer/src/bridge/init_bridge_account_action.rs +++ b/crates/astria-sequencer/src/bridge/init_bridge_account_action.rs @@ -1,14 +1,14 @@ -use anyhow::{ - bail, - ensure, - Context as _, - Result, -}; use astria_core::{ primitive::v1::Address, protocol::transaction::v1alpha1::action::InitBridgeAccountAction, Protobuf as _, }; +use astria_eyre::eyre::{ + bail, + ensure, + Result, + WrapErr as _, +}; use cnidarium::StateWrite; use crate::{ @@ -44,13 +44,13 @@ impl ActionHandler for InitBridgeAccountAction { state .ensure_base_prefix(withdrawer_address) .await - .context("failed check for base prefix of withdrawer address")?; + .wrap_err("failed check for base prefix of withdrawer address")?; } if let Some(sudo_address) = &self.sudo_address { state .ensure_base_prefix(sudo_address) .await - .context("failed check for base prefix of sudo address")?; + .wrap_err("failed check for base prefix of sudo address")?; } ensure!( @@ -61,7 +61,7 @@ impl ActionHandler for InitBridgeAccountAction { let fee = state .get_init_bridge_account_base_fee() .await - .context("failed to get base fee for initializing bridge account")?; + .wrap_err("failed to get base fee for initializing bridge account")?; // this prevents the address from being registered as a bridge account // if it's been previously initialized as a bridge account. @@ -77,7 +77,7 @@ impl ActionHandler for InitBridgeAccountAction { if state .get_bridge_account_rollup_id(from) .await - .context("failed getting rollup ID of bridge account")? + .wrap_err("failed getting rollup ID of bridge account")? .is_some() { bail!("bridge account already exists"); @@ -86,7 +86,7 @@ impl ActionHandler for InitBridgeAccountAction { let balance = state .get_account_balance(from, &self.fee_asset) .await - .context("failed getting `from` account balance for fee payment")?; + .wrap_err("failed getting `from` account balance for fee payment")?; ensure!( balance >= fee, @@ -96,7 +96,7 @@ impl ActionHandler for InitBridgeAccountAction { state.put_bridge_account_rollup_id(from, &self.rollup_id); state .put_bridge_account_ibc_asset(from, &self.asset) - .context("failed to put asset ID")?; + .wrap_err("failed to put asset ID")?; state.put_bridge_account_sudo_address(from, self.sudo_address.map_or(from, Address::bytes)); state.put_bridge_account_withdrawer_address( from, @@ -105,11 +105,11 @@ impl ActionHandler for InitBridgeAccountAction { state .get_and_increase_block_fees(&self.fee_asset, fee, Self::full_name()) .await - .context("failed to get and increase block fees")?; + .wrap_err("failed to get and increase block fees")?; state .decrease_balance(from, &self.fee_asset, fee) .await - .context("failed to deduct fee from account balance")?; + .wrap_err("failed to deduct fee from account balance")?; Ok(()) } } diff --git a/crates/astria-sequencer/src/bridge/query.rs b/crates/astria-sequencer/src/bridge/query.rs index 25f4fdf6f3..6c0d054fe1 100644 --- a/crates/astria-sequencer/src/bridge/query.rs +++ b/crates/astria-sequencer/src/bridge/query.rs @@ -1,4 +1,3 @@ -use anyhow::Context as _; use astria_core::{ primitive::v1::Address, protocol::{ @@ -6,6 +5,10 @@ use astria_core::{ bridge::v1alpha1::BridgeAccountInfo, }, }; +use astria_eyre::eyre::{ + eyre, + WrapErr as _, +}; use cnidarium::Storage; use prost::Message as _; use tendermint::abci::{ @@ -22,7 +25,7 @@ use crate::{ }; fn error_query_response( - err: Option, + err: Option, code: AbciErrorCode, msg: &str, ) -> response::Query { @@ -44,7 +47,7 @@ fn error_query_response( async fn get_bridge_account_info( snapshot: cnidarium::Snapshot, address: Address, -) -> anyhow::Result, response::Query> { +) -> Result, response::Query> { let rollup_id = match snapshot.get_bridge_account_rollup_id(address).await { Ok(Some(rollup_id)) => rollup_id, Ok(None) => { @@ -176,7 +179,7 @@ pub(crate) async fn bridge_account_info_request( Ok(height) => height, Err(err) => { return error_query_response( - Some(err), + Some(eyre!(err)), AbciErrorCode::INTERNAL_ERROR, "failed to get block height", ); @@ -225,7 +228,7 @@ pub(crate) async fn bridge_account_last_tx_hash_request( Ok(height) => height, Err(err) => { return error_query_response( - Some(err), + Some(eyre!(err)), AbciErrorCode::INTERNAL_ERROR, "failed to get block height", ); @@ -264,7 +267,7 @@ pub(crate) async fn bridge_account_last_tx_hash_request( } } -fn preprocess_request(params: &[(String, String)]) -> anyhow::Result { +fn preprocess_request(params: &[(String, String)]) -> Result { let Some(address) = params .iter() .find_map(|(k, v)| (k == "address").then_some(v)) @@ -277,7 +280,7 @@ fn preprocess_request(params: &[(String, String)]) -> anyhow::Result(address: #[async_trait] pub(crate) trait StateReadExt: StateRead + address::StateReadExt { #[instrument(skip_all)] - async fn is_a_bridge_account(&self, address: T) -> anyhow::Result { + async fn is_a_bridge_account(&self, address: T) -> Result { let maybe_id = self.get_bridge_account_rollup_id(address).await?; Ok(maybe_id.is_some()) } @@ -179,14 +183,15 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let Some(rollup_id_bytes) = self .get_raw(&rollup_id_storage_key(&address)) .await - .context("failed reading raw account rollup ID from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw account rollup ID from state")? else { debug!("account rollup ID not found, returning None"); return Ok(None); }; let rollup_id = - RollupId::try_from_slice(&rollup_id_bytes).context("invalid rollup ID bytes")?; + RollupId::try_from_slice(&rollup_id_bytes).wrap_err("invalid rollup ID bytes")?; Ok(Some(rollup_id)) } @@ -198,10 +203,11 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let bytes = self .get_raw(&asset_id_storage_key(&address)) .await - .context("failed reading raw asset ID from state")? - .ok_or_else(|| anyhow!("asset ID not found"))?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw asset ID from state")? + .ok_or_eyre("asset ID not found")?; let id = borsh::from_slice::(&bytes) - .context("failed to reconstruct asset ID from storage")?; + .wrap_err("failed to reconstruct asset ID from storage")?; Ok(asset::IbcPrefixed::new(id.0)) } @@ -213,13 +219,14 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let Some(sudo_address_bytes) = self .get_raw(&bridge_account_sudo_address_storage_key(&bridge_address)) .await - .context("failed reading raw bridge account sudo address from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw bridge account sudo address from state")? else { debug!("bridge account sudo address not found, returning None"); return Ok(None); }; let sudo_address = sudo_address_bytes.try_into().map_err(|bytes: Vec<_>| { - anyhow::format_err!( + format_err!( "failed to convert address `{}` bytes read from state to fixed length address", bytes.len() ) @@ -237,7 +244,8 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { &bridge_address, )) .await - .context("failed reading raw bridge account withdrawer address from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw bridge account withdrawer address from state")? else { debug!("bridge account withdrawer address not found, returning None"); return Ok(None); @@ -245,7 +253,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let addr = withdrawer_address_bytes .try_into() .map_err(|bytes: Vec<_>| { - anyhow::Error::msg(format!( + astria_eyre::eyre::Error::msg(format!( "failed converting `{}` bytes retrieved from storage to fixed address length", bytes.len() )) @@ -258,7 +266,8 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let bytes = self .nonverifiable_get_raw(&deposit_nonce_storage_key(rollup_id)) .await - .context("failed reading raw deposit nonce from state")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw deposit nonce from state")?; let Some(bytes) = bytes else { // no deposits for this rollup id yet; return 0 return Ok(0); @@ -278,15 +287,15 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { while let Some(Ok((key, _))) = stream.next().await { // the deposit key is of the form "deposit/{rollup_id}/{nonce}" let key_str = - String::from_utf8(key).context("failed to convert deposit key to string")?; + String::from_utf8(key).wrap_err("failed to convert deposit key to string")?; let key_parts = key_str.split('/').collect::>(); if key_parts.len() != 3 { continue; } let rollup_id_bytes = - hex::decode(key_parts[1]).context("invalid rollup ID hex string")?; + hex::decode(key_parts[1]).wrap_err("invalid rollup ID hex string")?; let rollup_id = - RollupId::try_from_slice(&rollup_id_bytes).context("invalid rollup ID bytes")?; + RollupId::try_from_slice(&rollup_id_bytes).wrap_err("invalid rollup ID bytes")?; rollup_ids.insert(rollup_id); } Ok(rollup_ids) @@ -299,8 +308,8 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { ); let mut deposits = Vec::new(); while let Some(Ok((_, value))) = stream.next().await { - let raw = RawDeposit::decode(value.as_ref()).context("invalid deposit bytes")?; - let deposit = Deposit::try_from_raw(raw).context("invalid deposit raw proto")?; + let raw = RawDeposit::decode(value.as_ref()).wrap_err("invalid deposit bytes")?; + let deposit = Deposit::try_from_raw(raw).wrap_err("invalid deposit raw proto")?; deposits.push(deposit); } Ok(deposits) @@ -311,13 +320,13 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let deposit_rollup_ids = self .get_deposit_rollup_ids() .await - .context("failed to get deposit rollup IDs")?; + .wrap_err("failed to get deposit rollup IDs")?; let mut deposit_events = HashMap::new(); for rollup_id in deposit_rollup_ids { let rollup_deposit_events = self .get_deposit_events(&rollup_id) .await - .context("failed to get deposit events")?; + .wrap_err("failed to get deposit events")?; deposit_events.insert(rollup_id, rollup_deposit_events); } Ok(deposit_events) @@ -328,9 +337,10 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let bytes = self .get_raw(INIT_BRIDGE_ACCOUNT_BASE_FEE_STORAGE_KEY) .await - .context("failed reading raw init bridge account base fee from state")? - .ok_or_else(|| anyhow!("init bridge account base fee not found"))?; - let Fee(fee) = Fee::try_from_slice(&bytes).context("invalid fee bytes")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw init bridge account base fee from state")? + .ok_or_eyre("init bridge account base fee not found")?; + let Fee(fee) = Fee::try_from_slice(&bytes).wrap_err("invalid fee bytes")?; Ok(fee) } @@ -339,9 +349,10 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let bytes = self .get_raw(BRIDGE_LOCK_BYTE_COST_MULTIPLIER_STORAGE_KEY) .await - .context("failed reading raw bridge lock byte cost multiplier from state")? - .ok_or_else(|| anyhow!("bridge lock byte cost multiplier not found"))?; - let Fee(fee) = Fee::try_from_slice(&bytes).context("invalid fee bytes")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw bridge lock byte cost multiplier from state")? + .ok_or_eyre("bridge lock byte cost multiplier not found")?; + let Fee(fee) = Fee::try_from_slice(&bytes).wrap_err("invalid fee bytes")?; Ok(fee) } @@ -350,9 +361,10 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let bytes = self .get_raw(BRIDGE_SUDO_CHANGE_FEE_STORAGE_KEY) .await - .context("failed reading raw bridge sudo change fee from state")? - .ok_or_else(|| anyhow!("bridge sudo change fee not found"))?; - let Fee(fee) = Fee::try_from_slice(&bytes).context("invalid fee bytes")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw bridge sudo change fee from state")? + .ok_or_eyre("bridge sudo change fee not found")?; + let Fee(fee) = Fee::try_from_slice(&bytes).wrap_err("invalid fee bytes")?; Ok(fee) } @@ -364,7 +376,8 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { let Some(tx_hash_bytes) = self .nonverifiable_get_raw(&last_transaction_id_for_bridge_account_storage_key(address)) .await - .context("failed reading raw last transaction hash for bridge account from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw last transaction hash for bridge account from state")? else { return Ok(None); }; @@ -399,7 +412,7 @@ pub(crate) trait StateWriteExt: StateWrite { let ibc = asset.into(); self.put_raw( asset_id_storage_key(&address), - borsh::to_vec(&AssetId(ibc.get())).context("failed to serialize asset IDs")?, + borsh::to_vec(&AssetId(ibc.get())).wrap_err("failed to serialize asset IDs")?, ); Ok(()) } @@ -447,7 +460,8 @@ pub(crate) trait StateWriteExt: StateWrite { let bytes = self .get_raw(&key) .await - .context("failed reading raw withdrawal event from state")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw withdrawal event from state")?; if let Some(bytes) = bytes { let existing_block_num = u64::from_be_bytes( bytes @@ -481,7 +495,7 @@ pub(crate) trait StateWriteExt: StateWrite { let nonce = self.get_deposit_nonce(deposit.rollup_id()).await?; self.put_deposit_nonce( deposit.rollup_id(), - nonce.checked_add(1).context("nonce overflowed")?, + nonce.checked_add(1).ok_or_eyre("nonce overflowed")?, ); let key = deposit_storage_key(deposit.rollup_id(), nonce); @@ -506,7 +520,7 @@ pub(crate) trait StateWriteExt: StateWrite { let deposit_rollup_ids = self .get_deposit_rollup_ids() .await - .context("failed to get deposit rollup ids")?; + .wrap_err("failed to get deposit rollup ids")?; for rollup_id in deposit_rollup_ids { self.clear_deposit_info(&rollup_id).await; } @@ -668,7 +682,7 @@ mod test { let state = StateDelta::new(snapshot); let address = astria_address(&[42u8; 20]); - state + let _ = state .get_bridge_account_ibc_asset(address) .await .expect_err("call to get bridge account asset ids should fail if no assets"); diff --git a/crates/astria-sequencer/src/component.rs b/crates/astria-sequencer/src/component.rs index ecfc02af28..f5e79beafc 100644 --- a/crates/astria-sequencer/src/component.rs +++ b/crates/astria-sequencer/src/component.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use anyhow::Result; +use astria_eyre::eyre::Result; use async_trait::async_trait; use cnidarium::StateWrite; use tendermint::abci; diff --git a/crates/astria-sequencer/src/fee_asset_change.rs b/crates/astria-sequencer/src/fee_asset_change.rs index e51cd3461f..00099ffe1b 100644 --- a/crates/astria-sequencer/src/fee_asset_change.rs +++ b/crates/astria-sequencer/src/fee_asset_change.rs @@ -1,10 +1,10 @@ -use anyhow::{ +use astria_core::protocol::transaction::v1alpha1::action::FeeAssetChangeAction; +use astria_eyre::eyre::{ bail, ensure, - Context as _, Result, + WrapErr as _, }; -use astria_core::protocol::transaction::v1alpha1::action::FeeAssetChangeAction; use async_trait::async_trait; use cnidarium::StateWrite; @@ -32,7 +32,7 @@ impl ActionHandler for FeeAssetChangeAction { let authority_sudo_address = state .get_sudo_address() .await - .context("failed to get authority sudo address")?; + .wrap_err("failed to get authority sudo address")?; ensure!( authority_sudo_address == from, "unauthorized address for fee asset change" @@ -47,7 +47,7 @@ impl ActionHandler for FeeAssetChangeAction { if state .get_allowed_fee_assets() .await - .context("failed to retrieve allowed fee assets")? + .wrap_err("failed to retrieve allowed fee assets")? .is_empty() { bail!("cannot remove last allowed fee asset"); diff --git a/crates/astria-sequencer/src/ibc/component.rs b/crates/astria-sequencer/src/ibc/component.rs index 6f0ac0d8db..488fcf6ebd 100644 --- a/crates/astria-sequencer/src/ibc/component.rs +++ b/crates/astria-sequencer/src/ibc/component.rs @@ -1,10 +1,10 @@ use std::sync::Arc; -use anyhow::{ - Context, +use astria_core::protocol::genesis::v1alpha1::GenesisAppState; +use astria_eyre::eyre::{ Result, + WrapErr as _, }; -use astria_core::protocol::genesis::v1alpha1::GenesisAppState; use penumbra_ibc::{ component::Ibc, genesis::Content, @@ -42,7 +42,7 @@ impl Component for IbcComponent { state .put_ibc_sudo_address(*app_state.ibc_sudo_address()) - .context("failed to set IBC sudo key")?; + .wrap_err("failed to set IBC sudo key")?; for address in app_state.ibc_relayer_addresses() { state.put_ibc_relayer_address(address); @@ -50,7 +50,7 @@ impl Component for IbcComponent { state .put_ics20_withdrawal_base_fee(app_state.fees().ics20_withdrawal_base_fee) - .context("failed to put ics20 withdrawal base fee")?; + .wrap_err("failed to put ics20 withdrawal base fee")?; Ok(()) } diff --git a/crates/astria-sequencer/src/ibc/host_interface.rs b/crates/astria-sequencer/src/ibc/host_interface.rs index fad0f15e3d..c8e8aaea73 100644 --- a/crates/astria-sequencer/src/ibc/host_interface.rs +++ b/crates/astria-sequencer/src/ibc/host_interface.rs @@ -1,3 +1,7 @@ +use astria_eyre::{ + anyhow, + eyre_to_anyhow, +}; use cnidarium::StateRead; use penumbra_ibc::component::HostInterface; @@ -9,18 +13,22 @@ pub(crate) struct AstriaHost; #[async_trait::async_trait] impl HostInterface for AstriaHost { async fn get_chain_id(state: S) -> anyhow::Result { - state.get_chain_id().await.map(|s| s.to_string()) + state + .get_chain_id() + .await + .map_err(eyre_to_anyhow) + .map(|s| s.to_string()) } async fn get_revision_number(state: S) -> anyhow::Result { - state.get_revision_number().await + state.get_revision_number().await.map_err(eyre_to_anyhow) } async fn get_block_height(state: S) -> anyhow::Result { - state.get_block_height().await + state.get_block_height().await.map_err(eyre_to_anyhow) } async fn get_block_timestamp(state: S) -> anyhow::Result { - state.get_block_timestamp().await + state.get_block_timestamp().await.map_err(eyre_to_anyhow) } } diff --git a/crates/astria-sequencer/src/ibc/ibc_relayer_change.rs b/crates/astria-sequencer/src/ibc/ibc_relayer_change.rs index 81f06b050d..57c2da04b1 100644 --- a/crates/astria-sequencer/src/ibc/ibc_relayer_change.rs +++ b/crates/astria-sequencer/src/ibc/ibc_relayer_change.rs @@ -1,9 +1,9 @@ -use anyhow::{ +use astria_core::protocol::transaction::v1alpha1::action::IbcRelayerChangeAction; +use astria_eyre::eyre::{ ensure, - Context as _, Result, + WrapErr as _, }; -use astria_core::protocol::transaction::v1alpha1::action::IbcRelayerChangeAction; use async_trait::async_trait; use cnidarium::StateWrite; @@ -30,7 +30,7 @@ impl ActionHandler for IbcRelayerChangeAction { .address_bytes(); match self { IbcRelayerChangeAction::Addition(addr) | IbcRelayerChangeAction::Removal(addr) => { - state.ensure_base_prefix(addr).await.context( + state.ensure_base_prefix(addr).await.wrap_err( "failed check for base prefix of provided address to be added/removed", )?; } @@ -39,7 +39,7 @@ impl ActionHandler for IbcRelayerChangeAction { let ibc_sudo_address = state .get_ibc_sudo_address() .await - .context("failed to get IBC sudo address")?; + .wrap_err("failed to get IBC sudo address")?; ensure!( ibc_sudo_address == from, "unauthorized address for IBC relayer change" diff --git a/crates/astria-sequencer/src/ibc/ics20_transfer.rs b/crates/astria-sequencer/src/ibc/ics20_transfer.rs index f6b716a5e0..4ac8c7b441 100644 --- a/crates/astria-sequencer/src/ibc/ics20_transfer.rs +++ b/crates/astria-sequencer/src/ibc/ics20_transfer.rs @@ -11,13 +11,6 @@ //! [`AppHandlerExecute`] is used for execution. use std::borrow::Cow; -use anyhow::{ - anyhow, - bail, - ensure, - Context as _, - Result, -}; use astria_core::{ primitive::v1::{ asset::{ @@ -31,6 +24,20 @@ use astria_core::{ protocol::memos, sequencerblock::v1alpha1::block::Deposit, }; +use astria_eyre::{ + anyhow::{ + self, + Context as _, + }, + eyre::{ + bail, + ensure, + OptionExt as _, + Result, + WrapErr as _, + }, + eyre_to_anyhow, +}; use cnidarium::{ StateRead, StateWrite, @@ -97,7 +104,10 @@ pub(crate) struct Ics20Transfer; #[async_trait::async_trait] impl AppHandlerCheck for Ics20Transfer { - async fn chan_open_init_check(_: S, msg: &MsgChannelOpenInit) -> Result<()> { + async fn chan_open_init_check( + _: S, + msg: &MsgChannelOpenInit, + ) -> anyhow::Result<()> { if msg.ordering != channel::Order::Unordered { anyhow::bail!("channel order must be unordered for Ics20 transfer"); } @@ -109,7 +119,10 @@ impl AppHandlerCheck for Ics20Transfer { Ok(()) } - async fn chan_open_try_check(_: S, msg: &MsgChannelOpenTry) -> Result<()> { + async fn chan_open_try_check( + _: S, + msg: &MsgChannelOpenTry, + ) -> anyhow::Result<()> { if msg.ordering != channel::Order::Unordered { anyhow::bail!("channel order must be unordered for Ics20 transfer"); } @@ -121,7 +134,10 @@ impl AppHandlerCheck for Ics20Transfer { Ok(()) } - async fn chan_open_ack_check(_: S, msg: &MsgChannelOpenAck) -> Result<()> { + async fn chan_open_ack_check( + _: S, + msg: &MsgChannelOpenAck, + ) -> anyhow::Result<()> { if msg.version_on_b.as_str() != "ics20-1" { anyhow::bail!("counterparty version must be ics20-1 for Ics20 transfer"); } @@ -129,25 +145,31 @@ impl AppHandlerCheck for Ics20Transfer { Ok(()) } - async fn chan_open_confirm_check(_: S, _: &MsgChannelOpenConfirm) -> Result<()> { + async fn chan_open_confirm_check( + _: S, + _: &MsgChannelOpenConfirm, + ) -> anyhow::Result<()> { // accept channel confirmations, port has already been validated, version has already been // validated Ok(()) } - async fn chan_close_init_check(_: S, _: &MsgChannelCloseInit) -> Result<()> { + async fn chan_close_init_check( + _: S, + _: &MsgChannelCloseInit, + ) -> anyhow::Result<()> { anyhow::bail!("ics20 always aborts on chan_close_init"); } async fn chan_close_confirm_check( _: S, _: &MsgChannelCloseConfirm, - ) -> Result<()> { + ) -> anyhow::Result<()> { // no action needed Ok(()) } - async fn recv_packet_check(_: S, msg: &MsgRecvPacket) -> Result<()> { + async fn recv_packet_check(_: S, msg: &MsgRecvPacket) -> anyhow::Result<()> { // most checks performed in `execute` // perform stateless checks here if msg.packet.data.is_empty() { @@ -161,7 +183,7 @@ impl AppHandlerCheck for Ics20Transfer { Ok(()) } - async fn timeout_packet_check(state: S, msg: &MsgTimeout) -> Result<()> { + async fn timeout_packet_check(state: S, msg: &MsgTimeout) -> anyhow::Result<()> { refund_tokens_check( state, msg.packet.data.as_slice(), @@ -169,12 +191,13 @@ impl AppHandlerCheck for Ics20Transfer { &msg.packet.chan_on_a, ) .await + .map_err(eyre_to_anyhow) } async fn acknowledge_packet_check( state: S, msg: &MsgAcknowledgement, - ) -> Result<()> { + ) -> anyhow::Result<()> { // see https://github.com/cosmos/ibc-go/blob/3f5b2b6632e0fa37056e5805b289a9307870ac9a/modules/core/04-channel/types/acknowledgement.go // and https://github.com/cosmos/ibc-go/blob/3f5b2b6632e0fa37056e5805b289a9307870ac9a/proto/ibc/core/channel/v1/channel.proto#L155 // for formatting @@ -191,6 +214,7 @@ impl AppHandlerCheck for Ics20Transfer { &msg.packet.chan_on_a, ) .await + .map_err(eyre_to_anyhow) } } @@ -200,17 +224,17 @@ async fn refund_tokens_check( source_port: &PortId, source_channel: &ChannelId, ) -> Result<()> { - let packet_data: FungibleTokenPacketData = - serde_json::from_slice(data).context("failed to decode fungible token packet data json")?; + let packet_data: FungibleTokenPacketData = serde_json::from_slice(data) + .wrap_err("failed to decode fungible token packet data json")?; let denom = { let denom = packet_data .denom .parse::() - .context("failed parsing denom packet data")?; + .wrap_err("failed parsing denom packet data")?; convert_denomination_if_ibc_prefixed(&mut state, denom) .await - .context("failed to convert denomination if ibc/ prefixed")? + .wrap_err("failed to convert denomination if ibc/ prefixed")? }; let is_source = !denom.starts_with_str(&format!("{source_port}/{source_channel}")); @@ -221,14 +245,14 @@ async fn refund_tokens_check( let balance = state .get_ibc_channel_balance(source_channel, denom) .await - .context("failed to get channel balance in refund_tokens_check")?; + .wrap_err("failed to get channel balance in refund_tokens_check")?; let packet_amount: u128 = packet_data .amount .parse() - .context("failed to parse packet amount as u128")?; + .wrap_err("failed to parse packet amount as u128")?; if balance < packet_amount { - anyhow::bail!("insufficient balance to refund tokens to sender"); + bail!("insufficient balance to refund tokens to sender"); } } @@ -300,7 +324,9 @@ impl AppHandlerExecute for Ics20Transfer { true, ) .await - .context("failed to refund tokens during timeout_packet_execute") + .map_err(|err| { + eyre_to_anyhow(err).context("failed to refund tokens during timeout_packet_execute") + }) } async fn acknowledge_packet_execute( @@ -327,7 +353,9 @@ impl AppHandlerExecute for Ics20Transfer { true, ) .await - .context("failed to refund tokens during acknowledge_packet_execute") + .map_err(|err| { + eyre_to_anyhow(err).context("failed to refund tokens during timeout_packet_execute") + }) } } @@ -346,8 +374,8 @@ async fn convert_denomination_if_ibc_prefixed( Denom::IbcPrefixed(ibc) => state .map_ibc_to_trace_prefixed_asset(ibc) .await - .context("failed to get denom trace from asset id")? - .context("denom for given asset id not found in state")?, + .wrap_err("failed to get denom trace from asset id")? + .ok_or_eyre("denom for given asset id not found in state")?, }; Ok(denom) } @@ -385,7 +413,7 @@ async fn execute_ics20_transfer( is_refund: bool, ) -> Result<()> { let packet_data: FungibleTokenPacketData = - serde_json::from_slice(data).context("failed to decode FungibleTokenPacketData")?; + serde_json::from_slice(data).wrap_err("failed to decode FungibleTokenPacketData")?; // if the memo deserializes into an `Ics20WithdrawalFromRollupMemo`, // we can assume this is a refund from an attempted withdrawal from @@ -399,24 +427,24 @@ async fn execute_ics20_transfer( { execute_withdrawal_refund_to_rollup(state, packet_data) .await - .context("failed to execute rollup withdrawal refund")?; + .wrap_err("failed to execute rollup withdrawal refund")?; return Ok(()); } let packet_amount: u128 = packet_data .amount .parse() - .context("failed to parse packet data amount to u128")?; + .wrap_err("failed to parse packet data amount to u128")?; let mut denom_trace = { let denom = packet_data .denom .parse::() - .context("failed parsing denom in packet data as Denom")?; + .wrap_err("failed parsing denom in packet data as Denom")?; // convert denomination if it's prefixed with `ibc/` // note: this denomination might have a prefix, but it wasn't prefixed by us right now. convert_denomination_if_ibc_prefixed(state, denom) .await - .context("failed to convert denomination if ibc/ prefixed")? + .wrap_err("failed to convert denomination if ibc/ prefixed")? }; // the IBC packet should have the address as a bech32 string @@ -425,7 +453,7 @@ async fn execute_ics20_transfer( } else { packet_data.receiver }; - let recipient = recipient.parse().context("invalid recipient address")?; + let recipient = recipient.parse().wrap_err("invalid recipient address")?; let is_prefixed = denom_trace.starts_with_str(&format!("{source_port}/{source_channel}")); let is_source = if is_refund { @@ -451,7 +479,7 @@ async fn execute_ics20_transfer( is_refund, ) .await - .context("failed to execute ics20 transfer to bridge account")?; + .wrap_err("failed to execute ics20 transfer to bridge account")?; if is_source { // the asset being transferred in is an asset that originated from astria @@ -460,7 +488,7 @@ async fn execute_ics20_transfer( // strip the prefix from the denom, as we're back on the source chain // note: if this is a refund, this is a no-op. if !is_refund { - denom_trace.pop_trace_segment().context( + denom_trace.pop_trace_segment().ok_or_eyre( "there must be a source segment because above it was checked if the denom trace \ contains a segment", )?; @@ -474,38 +502,38 @@ async fn execute_ics20_transfer( let escrow_balance = state .get_ibc_channel_balance(escrow_channel, &denom_trace) .await - .context("failed to get IBC channel balance in execute_ics20_transfer")?; + .wrap_err("failed to get IBC channel balance in execute_ics20_transfer")?; state .put_ibc_channel_balance( escrow_channel, &denom_trace, - escrow_balance.checked_sub(packet_amount).ok_or(anyhow!( - "insufficient balance in escrow account to transfer tokens" - ))?, + escrow_balance + .checked_sub(packet_amount) + .ok_or_eyre("insufficient balance in escrow account to transfer tokens")?, ) - .context("failed to update escrow account balance in execute_ics20_transfer")?; + .wrap_err("failed to update escrow account balance in execute_ics20_transfer")?; state .increase_balance(recipient, &denom_trace, packet_amount) .await - .context("failed to update user account balance in execute_ics20_transfer")?; + .wrap_err("failed to update user account balance in execute_ics20_transfer")?; } else { // register denomination in global ID -> denom map if it's not already there if !state .has_ibc_asset(&*trace_with_dest) .await - .context("failed to check if ibc asset exists in state")? + .wrap_err("failed to check if ibc asset exists in state")? { state .put_ibc_asset(&trace_with_dest) - .context("failed to put IBC asset in storage")?; + .wrap_err("failed to put IBC asset in storage")?; } state .increase_balance(recipient, &*trace_with_dest, packet_amount) .await - .context("failed to update user account balance in execute_ics20_transfer")?; + .wrap_err("failed to update user account balance in execute_ics20_transfer")?; } Ok(()) @@ -538,12 +566,12 @@ async fn execute_withdrawal_refund_to_rollup( let amount: u128 = packet_data .amount .parse() - .context("failed to parse packet data amount to u128")?; + .wrap_err("failed to parse packet data amount to u128")?; let denom = { let denom = packet_data .denom .parse::() - .context("failed parsing denom in packet data as Denom")?; + .wrap_err("failed parsing denom in packet data as Denom")?; // convert denomination if it's prefixed with `ibc/` // note: this denomination might have a prefix, but it wasn't prefixed by us right now. convert_denomination_if_ibc_prefixed(state, denom) @@ -566,27 +594,27 @@ async fn execute_withdrawal_refund_to_rollup( state .increase_balance(bridge_address, denom, amount) .await - .context("failed to update bridge account account balance")?; + .wrap_err("failed to update bridge account account balance")?; Ok(()) } -async fn parse_refund_sender(state: &S, sender: &str) -> anyhow::Result
{ +async fn parse_refund_sender(state: &S, sender: &str) -> Result
{ use futures::TryFutureExt as _; let (base_prefix, compat_prefix) = match try_join!( state .get_base_prefix() - .map_err(|e| e.context("failed to read base prefix from state")), + .map_err(|e| e.wrap_err("failed to read base prefix from state")), state .get_ibc_compat_prefix() - .map_err(|e| e.context("failed to read ibc compat prefix from state")) + .map_err(|e| e.wrap_err("failed to read ibc compat prefix from state")) ) { Ok(prefixes) => prefixes, Err(err) => return Err(err), }; sender .parse::>() - .context("failed to parse address in bech32m format") + .wrap_err("failed to parse address in bech32m format") .and_then(|addr| { ensure!( addr.prefix() == base_prefix, @@ -597,14 +625,14 @@ async fn parse_refund_sender(state: &S, sender: &str) -> anyhow::R .or_else(|_| { sender .parse::>() - .context("failed to parse address in bech32/compat format") + .wrap_err("failed to parse address in bech32/compat format") .and_then(|addr| { ensure!( addr.prefix() == compat_prefix, "address prefix is not base prefix stored in state" ); addr.to_prefix(&base_prefix) - .context( + .wrap_err( "failed to convert ibc compat prefixed address to standard base \ prefixed address", ) @@ -631,7 +659,7 @@ async fn execute_ics20_transfer_bridge_lock( let is_bridge_lock = state .get_bridge_account_rollup_id(recipient) .await - .context("failed to get bridge account rollup ID from state")? + .wrap_err("failed to get bridge account rollup ID from state")? .is_some(); // if account being transferred to is not a bridge account, or @@ -652,7 +680,7 @@ async fn execute_ics20_transfer_bridge_lock( // assert memo is valid let deposit_memo: memos::v1alpha1::Ics20TransferDeposit = - serde_json::from_str(&memo).context("failed to parse memo as Ics20TransferDepositMemo")?; + serde_json::from_str(&memo).wrap_err("failed to parse memo as Ics20TransferDepositMemo")?; ensure!( !deposit_memo.rollup_deposit_address.is_empty(), @@ -687,7 +715,7 @@ async fn execute_deposit( let Some(rollup_id) = state .get_bridge_account_rollup_id(bridge_address) .await - .context("failed to get bridge account rollup ID from state")? + .wrap_err("failed to get bridge account rollup ID from state")? else { bail!("bridge account rollup ID not found in state; invalid bridge address?") }; @@ -695,7 +723,7 @@ async fn execute_deposit( let allowed_asset = state .get_bridge_account_ibc_asset(bridge_address) .await - .context("failed to get bridge account asset ID")?; + .wrap_err("failed to get bridge account asset ID")?; ensure!( allowed_asset == denom.to_ibc_prefixed(), "asset ID is not authorized for transfer to bridge account", @@ -703,7 +731,7 @@ async fn execute_deposit( let transaction_context = state .get_transaction_context() - .context("transaction source should be present in state when executing an action")?; + .ok_or_eyre("transaction source should be present in state when executing an action")?; let transaction_id = transaction_context.transaction_id; let index_of_action = transaction_context.source_action_index; @@ -721,7 +749,7 @@ async fn execute_deposit( state .put_deposit_event(deposit) .await - .context("failed to put deposit event into state")?; + .wrap_err("failed to put deposit event into state")?; Ok(()) } @@ -937,7 +965,7 @@ mod test { }; let packet_bytes = serde_json::to_vec(&packet).unwrap(); - execute_ics20_transfer( + let _ = execute_ics20_transfer( &mut state_tx, &packet_bytes, &"source_port".to_string().parse().unwrap(), @@ -975,7 +1003,7 @@ mod test { }; let packet_bytes = serde_json::to_vec(&packet).unwrap(); - execute_ics20_transfer( + let _ = execute_ics20_transfer( &mut state_tx, &packet_bytes, &"source_port".to_string().parse().unwrap(), diff --git a/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs b/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs index 327194185b..158ff8835c 100644 --- a/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs +++ b/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs @@ -1,9 +1,3 @@ -use anyhow::{ - bail, - ensure, - Context as _, - Result, -}; use astria_core::{ primitive::v1::{ asset::Denom, @@ -16,6 +10,16 @@ use astria_core::{ }, Protobuf as _, }; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + ensure, + OptionExt as _, + Result, + WrapErr as _, + }, +}; use cnidarium::{ StateRead, StateWrite, @@ -55,7 +59,7 @@ use crate::{ async fn create_ibc_packet_from_withdrawal( withdrawal: &action::Ics20Withdrawal, state: S, -) -> anyhow::Result> { +) -> Result> { let sender = if withdrawal.use_compat_address { let ibc_compat_prefix = state.get_ibc_compat_prefix().await.context( "need to construct bech32 compatible address for IBC communication but failed reading \ @@ -112,7 +116,7 @@ async fn establish_withdrawal_target( let Some(withdrawer) = state .get_bridge_account_withdrawer_address(bridge_address) .await - .context("failed to get bridge withdrawer")? + .wrap_err("failed to get bridge withdrawer")? else { bail!("bridge address must have a withdrawer address set"); }; @@ -184,10 +188,10 @@ impl ActionHandler for action::Ics20Withdrawal { state .ensure_base_prefix(&self.return_address) .await - .context("failed to verify that return address address has permitted base prefix")?; + .wrap_err("failed to verify that return address address has permitted base prefix")?; if let Some(bridge_address) = &self.bridge_address { - state.ensure_base_prefix(bridge_address).await.context( + state.ensure_base_prefix(bridge_address).await.wrap_err( "failed to verify that bridge address address has permitted base prefix", )?; let parsed_bridge_memo: Ics20WithdrawalFromRollup = serde_json::from_str(&self.memo) @@ -205,17 +209,17 @@ impl ActionHandler for action::Ics20Withdrawal { let withdrawal_target = establish_withdrawal_target(self, &state, from) .await - .context("failed establishing which account to withdraw funds from")?; + .wrap_err("failed establishing which account to withdraw funds from")?; let fee = state .get_ics20_withdrawal_base_fee() .await - .context("failed to get ics20 withdrawal base fee")?; + .wrap_err("failed to get ics20 withdrawal base fee")?; let current_timestamp = state .get_block_timestamp() .await - .context("failed to get block timestamp")?; + .wrap_err("failed to get block timestamp")?; let packet = { let packet = create_ibc_packet_from_withdrawal(self, &state) .await @@ -223,23 +227,24 @@ impl ActionHandler for action::Ics20Withdrawal { state .send_packet_check(packet, current_timestamp) .await - .context("packet failed send check")? + .map_err(anyhow_to_eyre) + .wrap_err("packet failed send check")? }; state .get_and_increase_block_fees(self.fee_asset(), fee, Self::full_name()) .await - .context("failed to get and increase block fees")?; + .wrap_err("failed to get and increase block fees")?; state .decrease_balance(withdrawal_target, self.denom(), self.amount()) .await - .context("failed to decrease sender or bridge balance")?; + .wrap_err("failed to decrease sender or bridge balance")?; state .decrease_balance(from, self.fee_asset(), fee) .await - .context("failed to subtract fee from sender balance")?; + .wrap_err("failed to subtract fee from sender balance")?; // if we're the source, move tokens to the escrow account, // otherwise the tokens are just burned @@ -247,7 +252,7 @@ impl ActionHandler for action::Ics20Withdrawal { let channel_balance = state .get_ibc_channel_balance(self.source_channel(), self.denom()) .await - .context("failed to get channel balance")?; + .wrap_err("failed to get channel balance")?; state .put_ibc_channel_balance( @@ -255,9 +260,9 @@ impl ActionHandler for action::Ics20Withdrawal { self.denom(), channel_balance .checked_add(self.amount()) - .context("overflow when adding to channel balance")?, + .ok_or_eyre("overflow when adding to channel balance")?, ) - .context("failed to update channel balance")?; + .wrap_err("failed to update channel balance")?; } state.send_packet_execute(packet).await; @@ -283,7 +288,7 @@ mod tests { use crate::{ address::StateWriteExt as _, test_utils::{ - assert_anyhow_error, + assert_eyre_error, astria_address, ASTRIA_PREFIX, }, @@ -350,7 +355,7 @@ mod tests { use_compat_address: false, }; - assert_anyhow_error( + assert_eyre_error( &establish_withdrawal_target(&action, &state, bridge_address) .await .unwrap_err(), @@ -402,7 +407,7 @@ mod tests { astria_address(&[2u8; 20]), ); - assert_anyhow_error( + assert_eyre_error( &establish_withdrawal_target(&action, &state, bridge_address()) .await .unwrap_err(), @@ -482,7 +487,7 @@ mod tests { use_compat_address: false, }; - assert_anyhow_error( + assert_eyre_error( &establish_withdrawal_target(&action, &state, not_bridge_address) .await .unwrap_err(), diff --git a/crates/astria-sequencer/src/ibc/state_ext.rs b/crates/astria-sequencer/src/ibc/state_ext.rs index b5c230bf98..69daaf9c6a 100644 --- a/crates/astria-sequencer/src/ibc/state_ext.rs +++ b/crates/astria-sequencer/src/ibc/state_ext.rs @@ -1,12 +1,15 @@ -use anyhow::{ - bail, - Context, - Result, -}; use astria_core::primitive::v1::{ asset, ADDRESS_LEN, }; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + Result, + WrapErr as _, + }, +}; use async_trait::async_trait; use borsh::{ BorshDeserialize, @@ -80,12 +83,13 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(&channel_balance_storage_key(channel, asset)) .await - .context("failed reading ibc channel balance from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading ibc channel balance from state")? else { debug!("ibc channel balance not found, returning 0"); return Ok(0); }; - let Balance(balance) = Balance::try_from_slice(&bytes).context("invalid balance bytes")?; + let Balance(balance) = Balance::try_from_slice(&bytes).wrap_err("invalid balance bytes")?; Ok(balance) } @@ -94,13 +98,14 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(IBC_SUDO_STORAGE_KEY) .await - .context("failed reading raw ibc sudo key from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw ibc sudo key from state")? else { // ibc sudo key must be set bail!("ibc sudo key not found"); }; let SudoAddress(address_bytes) = - SudoAddress::try_from_slice(&bytes).context("invalid ibc sudo key bytes")?; + SudoAddress::try_from_slice(&bytes).wrap_err("invalid ibc sudo key bytes")?; Ok(address_bytes) } @@ -109,7 +114,8 @@ pub(crate) trait StateReadExt: StateRead { Ok(self .get_raw(&ibc_relayer_key(&address)) .await - .context("failed to read ibc relayer key from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read ibc relayer key from state")? .is_some()) } @@ -118,11 +124,12 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(ICS20_WITHDRAWAL_BASE_FEE_STORAGE_KEY) .await - .context("failed reading ics20 withdrawal fee from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed reading ics20 withdrawal fee from state")? else { bail!("ics20 withdrawal fee not found"); }; - let Fee(fee) = Fee::try_from_slice(&bytes).context("invalid fee bytes")?; + let Fee(fee) = Fee::try_from_slice(&bytes).wrap_err("invalid fee bytes")?; Ok(fee) } } @@ -141,7 +148,7 @@ pub(crate) trait StateWriteExt: StateWrite { where TAsset: Into + std::fmt::Display + Send, { - let bytes = borsh::to_vec(&Balance(balance)).context("failed to serialize balance")?; + let bytes = borsh::to_vec(&Balance(balance)).wrap_err("failed to serialize balance")?; self.put_raw(channel_balance_storage_key(channel, asset), bytes); Ok(()) } @@ -151,7 +158,7 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_raw( IBC_SUDO_STORAGE_KEY.to_string(), borsh::to_vec(&SudoAddress(address.address_bytes())) - .context("failed to convert sudo address to vec")?, + .wrap_err("failed to convert sudo address to vec")?, ); Ok(()) } @@ -170,7 +177,7 @@ pub(crate) trait StateWriteExt: StateWrite { fn put_ics20_withdrawal_base_fee(&mut self, fee: u128) -> Result<()> { self.put_raw( ICS20_WITHDRAWAL_BASE_FEE_STORAGE_KEY.to_string(), - borsh::to_vec(&Fee(fee)).context("failed to serialize fee")?, + borsh::to_vec(&Fee(fee)).wrap_err("failed to serialize fee")?, ); Ok(()) } @@ -215,7 +222,7 @@ mod tests { let state = StateDelta::new(snapshot); // should fail if not set - state + let _ = state .get_ibc_sudo_address() .await .expect_err("sudo address should be set"); diff --git a/crates/astria-sequencer/src/main.rs b/crates/astria-sequencer/src/main.rs index 0c45a9d53e..150c83302a 100644 --- a/crates/astria-sequencer/src/main.rs +++ b/crates/astria-sequencer/src/main.rs @@ -1,6 +1,6 @@ use std::process::ExitCode; -use anyhow::Context as _; +use astria_eyre::eyre::WrapErr as _; use astria_sequencer::{ Config, Sequencer, @@ -39,7 +39,7 @@ async fn main() -> ExitCode { let (metrics, _telemetry_guard) = match telemetry_conf .try_init(&()) - .context("failed to setup telemetry") + .wrap_err("failed to setup telemetry") { Err(e) => { eprintln!("initializing sequencer failed:\n{e:?}"); diff --git a/crates/astria-sequencer/src/mempool/mempool_state.rs b/crates/astria-sequencer/src/mempool/mempool_state.rs index 767479637d..9bcec845ef 100644 --- a/crates/astria-sequencer/src/mempool/mempool_state.rs +++ b/crates/astria-sequencer/src/mempool/mempool_state.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -use anyhow::Result; use astria_core::primitive::v1::asset; +use astria_eyre::eyre::Result; use cnidarium::StateRead; use tracing::instrument; diff --git a/crates/astria-sequencer/src/mempool/mod.rs b/crates/astria-sequencer/src/mempool/mod.rs index 7de2e6576c..c015965ab0 100644 --- a/crates/astria-sequencer/src/mempool/mod.rs +++ b/crates/astria-sequencer/src/mempool/mod.rs @@ -17,6 +17,7 @@ use astria_core::{ primitive::v1::asset::IbcPrefixed, protocol::transaction::v1alpha1::SignedTransaction, }; +use astria_eyre::eyre::Result; pub(crate) use mempool_state::get_account_balances; use tokio::{ join, @@ -168,7 +169,7 @@ impl Mempool { current_account_nonce: u32, current_account_balances: HashMap, transaction_cost: HashMap, - ) -> anyhow::Result<(), InsertionError> { + ) -> Result<(), InsertionError> { let timemarked_tx = TimemarkedTransaction::new(tx, transaction_cost); let (mut pending, mut parked) = self.acquire_both_locks().await; @@ -230,7 +231,7 @@ impl Mempool { pub(crate) async fn builder_queue( &self, state: &S, - ) -> anyhow::Result)>> { + ) -> Result)>> { self.pending.read().await.builder_queue(state).await } diff --git a/crates/astria-sequencer/src/mempool/transactions_container.rs b/crates/astria-sequencer/src/mempool/transactions_container.rs index f642b5e51b..a0d1e5ee08 100644 --- a/crates/astria-sequencer/src/mempool/transactions_container.rs +++ b/crates/astria-sequencer/src/mempool/transactions_container.rs @@ -11,14 +11,15 @@ use std::{ sync::Arc, }; -use anyhow::{ - anyhow, - Context, -}; use astria_core::{ primitive::v1::asset::IbcPrefixed, protocol::transaction::v1alpha1::SignedTransaction, }; +use astria_eyre::eyre::{ + eyre, + Result, + WrapErr as _, +}; use tokio::time::{ Duration, Instant, @@ -57,9 +58,9 @@ impl TimemarkedTransaction { } } - fn priority(&self, current_account_nonce: u32) -> anyhow::Result { + fn priority(&self, current_account_nonce: u32) -> Result { let Some(nonce_diff) = self.signed_tx.nonce().checked_sub(current_account_nonce) else { - return Err(anyhow::anyhow!( + return Err(eyre!( "transaction nonce {} is less than current account nonce {current_account_nonce}", self.signed_tx.nonce() )); @@ -74,16 +75,16 @@ impl TimemarkedTransaction { pub(super) fn deduct_costs( &self, available_balances: &mut HashMap, - ) -> anyhow::Result<()> { + ) -> Result<()> { self.cost.iter().try_for_each(|(denom, cost)| { if *cost == 0 { return Ok(()); } let Some(current_balance) = available_balances.get_mut(denom) else { - return Err(anyhow!("account missing balance for {denom}")); + return Err(eyre!("account missing balance for {denom}")); }; let Some(new_balance) = current_balance.checked_sub(*cost) else { - return Err(anyhow!("cost greater than account's balance for {denom}")); + return Err(eyre!("cost greater than account's balance for {denom}")); }; *current_balance = new_balance; Ok(()) @@ -705,7 +706,7 @@ impl TransactionsContainer { pub(super) async fn builder_queue( &self, state: &S, - ) -> anyhow::Result)>> { + ) -> Result)>> { // Used to hold the values in Vec for sorting. struct QueueEntry { tx: Arc, @@ -719,7 +720,7 @@ impl TransactionsContainer { let current_account_nonce = state .get_account_nonce(*address) .await - .context("failed to fetch account nonce for builder queue")?; + .wrap_err("failed to fetch account nonce for builder queue")?; for ttx in account_txs.txs.values() { let priority = match ttx.priority(current_account_nonce) { Ok(priority) => priority, diff --git a/crates/astria-sequencer/src/proposal/block_size_constraints.rs b/crates/astria-sequencer/src/proposal/block_size_constraints.rs index e20d3be645..d344a88a77 100644 --- a/crates/astria-sequencer/src/proposal/block_size_constraints.rs +++ b/crates/astria-sequencer/src/proposal/block_size_constraints.rs @@ -1,7 +1,8 @@ -use anyhow::{ - anyhow, +use astria_eyre::eyre::{ ensure, - Context, + eyre, + OptionExt as _, + Result, }; use super::commitment::GeneratedCommitments; @@ -19,9 +20,9 @@ pub(crate) struct BlockSizeConstraints { } impl BlockSizeConstraints { - pub(crate) fn new(cometbft_max_size: usize) -> anyhow::Result { + pub(crate) fn new(cometbft_max_size: usize) -> Result { if cometbft_max_size < GeneratedCommitments::TOTAL_SIZE { - return Err(anyhow!( + return Err(eyre!( "cometbft_max_size must be at least GeneratedCommitments::TOTAL_SIZE" )); } @@ -55,11 +56,11 @@ impl BlockSizeConstraints { .saturating_sub(self.current_size_cometbft) } - pub(crate) fn sequencer_checked_add(&mut self, size: usize) -> anyhow::Result<()> { + pub(crate) fn sequencer_checked_add(&mut self, size: usize) -> Result<()> { let new_size = self .current_size_sequencer .checked_add(size) - .context("overflow adding to sequencer size")?; + .ok_or_eyre("overflow adding to sequencer size")?; ensure!( new_size <= self.max_size_sequencer, "max sequencer size reached" @@ -68,11 +69,11 @@ impl BlockSizeConstraints { Ok(()) } - pub(crate) fn cometbft_checked_add(&mut self, size: usize) -> anyhow::Result<()> { + pub(crate) fn cometbft_checked_add(&mut self, size: usize) -> Result<()> { let new_size = self .current_size_cometbft .checked_add(size) - .context("overflow adding to cometBFT size")?; + .ok_or_eyre("overflow adding to cometBFT size")?; ensure!( new_size <= self.max_size_cometbft, "max cometBFT size reached" diff --git a/crates/astria-sequencer/src/sequence/action.rs b/crates/astria-sequencer/src/sequence/action.rs index 04b3d8eb67..e294a1bbcd 100644 --- a/crates/astria-sequencer/src/sequence/action.rs +++ b/crates/astria-sequencer/src/sequence/action.rs @@ -1,12 +1,13 @@ -use anyhow::{ - ensure, - Context, - Result, -}; use astria_core::{ protocol::transaction::v1alpha1::action::SequenceAction, Protobuf as _, }; +use astria_eyre::eyre::{ + ensure, + OptionExt as _, + Result, + WrapErr as _, +}; use cnidarium::StateWrite; use crate::{ @@ -45,27 +46,27 @@ impl ActionHandler for SequenceAction { state .is_allowed_fee_asset(&self.fee_asset) .await - .context("failed accessing state to check if fee is allowed")?, + .wrap_err("failed accessing state to check if fee is allowed")?, "invalid fee asset", ); let curr_balance = state .get_account_balance(from, &self.fee_asset) .await - .context("failed getting `from` account balance for fee payment")?; + .wrap_err("failed getting `from` account balance for fee payment")?; let fee = calculate_fee_from_state(&self.data, &state) .await - .context("calculated fee overflows u128")?; + .wrap_err("calculated fee overflows u128")?; ensure!(curr_balance >= fee, "insufficient funds"); state .get_and_increase_block_fees(&self.fee_asset, fee, Self::full_name()) .await - .context("failed to add to block fees")?; + .wrap_err("failed to add to block fees")?; state .decrease_balance(from, &self.fee_asset, fee) .await - .context("failed updating `from` account balance")?; + .wrap_err("failed updating `from` account balance")?; Ok(()) } } @@ -78,12 +79,12 @@ pub(crate) async fn calculate_fee_from_state( let base_fee = state .get_sequence_action_base_fee() .await - .context("failed to get base fee")?; + .wrap_err("failed to get base fee")?; let fee_per_byte = state .get_sequence_action_byte_cost_multiplier() .await - .context("failed to get fee per byte")?; - calculate_fee(data, fee_per_byte, base_fee).context("calculated fee overflows u128") + .wrap_err("failed to get fee per byte")?; + calculate_fee(data, fee_per_byte, base_fee).ok_or_eyre("calculated fee overflows u128") } /// Calculates the fee for a sequence `Action` based on the length of the `data`. diff --git a/crates/astria-sequencer/src/sequence/component.rs b/crates/astria-sequencer/src/sequence/component.rs index 742fb6ea1f..36fa827363 100644 --- a/crates/astria-sequencer/src/sequence/component.rs +++ b/crates/astria-sequencer/src/sequence/component.rs @@ -1,7 +1,7 @@ use std::sync::Arc; -use anyhow::Result; use astria_core::protocol::genesis::v1alpha1::GenesisAppState; +use astria_eyre::eyre::Result; use tendermint::abci::request::{ BeginBlock, EndBlock, diff --git a/crates/astria-sequencer/src/sequence/state_ext.rs b/crates/astria-sequencer/src/sequence/state_ext.rs index ad17e4ef72..9ea6ec1dba 100644 --- a/crates/astria-sequencer/src/sequence/state_ext.rs +++ b/crates/astria-sequencer/src/sequence/state_ext.rs @@ -1,7 +1,10 @@ -use anyhow::{ - anyhow, - Context, - Result, +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + OptionExt as _, + Result, + WrapErr as _, + }, }; use async_trait::async_trait; use borsh::{ @@ -28,9 +31,10 @@ pub(crate) trait StateReadExt: StateRead { let bytes = self .get_raw(SEQUENCE_ACTION_BASE_FEE_STORAGE_KEY) .await - .context("failed reading raw sequence action base fee from state")? - .ok_or_else(|| anyhow!("sequence action base fee not found"))?; - let Fee(fee) = Fee::try_from_slice(&bytes).context("invalid fee bytes")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw sequence action base fee from state")? + .ok_or_eyre("sequence action base fee not found")?; + let Fee(fee) = Fee::try_from_slice(&bytes).wrap_err("invalid fee bytes")?; Ok(fee) } @@ -39,9 +43,10 @@ pub(crate) trait StateReadExt: StateRead { let bytes = self .get_raw(SEQUENCE_ACTION_BYTE_COST_MULTIPLIER_STORAGE_KEY) .await - .context("failed reading raw sequence action byte cost multiplier from state")? - .ok_or_else(|| anyhow!("sequence action byte cost multiplier not found"))?; - let Fee(fee) = Fee::try_from_slice(&bytes).context("invalid fee bytes")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed reading raw sequence action byte cost multiplier from state")? + .ok_or_eyre("sequence action byte cost multiplier not found")?; + let Fee(fee) = Fee::try_from_slice(&bytes).wrap_err("invalid fee bytes")?; Ok(fee) } } diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index ac5661ed7d..3bd1bfd64c 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -1,9 +1,13 @@ -use anyhow::{ - anyhow, - Context as _, - Result, -}; use astria_core::generated::sequencerblock::v1alpha1::sequencer_service_server::SequencerServiceServer; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + eyre, + OptionExt as _, + Result, + WrapErr as _, + }, +}; use penumbra_tower_trace::{ trace::request_span, v038::RequestExt as _, @@ -76,13 +80,14 @@ impl Sequencer { .collect(), ) .await - .context("failed to load storage backing chain state")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed to load storage backing chain state")?; let snapshot = storage.latest_snapshot(); let mempool = Mempool::new(); let app = App::new(snapshot, mempool.clone(), metrics) .await - .context("failed to initialize app")?; + .wrap_err("failed to initialize app")?; let consensus_service = tower::ServiceBuilder::new() .layer(request_span::layer(|req: &ConsensusRequest| { @@ -94,7 +99,7 @@ impl Sequencer { })); let mempool_service = service::Mempool::new(storage.clone(), mempool.clone(), metrics); let info_service = - service::Info::new(storage.clone()).context("failed initializing info service")?; + service::Info::new(storage.clone()).wrap_err("failed initializing info service")?; let snapshot_service = service::Snapshot; let server = Server::builder() @@ -103,7 +108,7 @@ impl Sequencer { .mempool(mempool_service) .snapshot(snapshot_service) .finish() - .ok_or_else(|| anyhow!("server builder didn't return server; are all fields set?"))?; + .ok_or_eyre("server builder didn't return server; are all fields set?")?; let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); let (server_exit_tx, server_exit_rx) = tokio::sync::oneshot::channel(); @@ -111,7 +116,7 @@ impl Sequencer { let grpc_addr = config .grpc_addr .parse() - .context("failed to parse grpc_addr address")?; + .wrap_err("failed to parse grpc_addr address")?; let grpc_server_handle = start_grpc_server(&storage, mempool, grpc_addr, shutdown_rx); info!(config.listen_addr, "starting sequencer"); @@ -140,11 +145,11 @@ impl Sequencer { shutdown_tx .send(()) - .map_err(|()| anyhow!("failed to send shutdown signal to grpc server"))?; + .map_err(|()| eyre!("failed to send shutdown signal to grpc server"))?; grpc_server_handle .await - .context("grpc server task failed")? - .context("grpc server failed")?; + .wrap_err("grpc server task failed")? + .wrap_err("grpc server failed")?; server_handle.abort(); Ok(()) } diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index a5384c9359..4fd31287cb 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -1,8 +1,9 @@ -use anyhow::{ +use astria_core::protocol::genesis::v1alpha1::GenesisAppState; +use astria_eyre::eyre::{ bail, - Context, + Result, + WrapErr as _, }; -use astria_core::protocol::genesis::v1alpha1::GenesisAppState; use cnidarium::Storage; use tendermint::v0_38::abci::{ request, @@ -74,13 +75,13 @@ impl Consensus { ConsensusRequest::InitChain(init_chain) => ConsensusResponse::InitChain( self.init_chain(init_chain) .await - .context("failed initializing chain")?, + .wrap_err("failed initializing chain")?, ), ConsensusRequest::PrepareProposal(prepare_proposal) => { ConsensusResponse::PrepareProposal( self.handle_prepare_proposal(prepare_proposal) .await - .context("failed to prepare proposal")?, + .wrap_err("failed to prepare proposal")?, ) } ConsensusRequest::ProcessProposal(process_proposal) => { @@ -108,26 +109,23 @@ impl Consensus { ConsensusRequest::FinalizeBlock(finalize_block) => ConsensusResponse::FinalizeBlock( self.finalize_block(finalize_block) .await - .context("failed to finalize block")?, + .wrap_err("failed to finalize block")?, ), ConsensusRequest::Commit => { - ConsensusResponse::Commit(self.commit().await.context("failed to commit")?) + ConsensusResponse::Commit(self.commit().await.wrap_err("failed to commit")?) } }) } - #[instrument(skip_all)] - async fn init_chain( - &mut self, - init_chain: request::InitChain, - ) -> anyhow::Result { + #[instrument(skip_all, err)] + async fn init_chain(&mut self, init_chain: request::InitChain) -> Result { // the storage version is set to u64::MAX by default when first created if self.storage.latest_version() != u64::MAX { bail!("database already initialized"); } let genesis_state: GenesisAppState = serde_json::from_slice(&init_chain.app_state_bytes) - .context("failed to parse genesis app state from init chain request")?; + .wrap_err("failed to parse app_state in genesis file")?; let app_hash = self .app .init_chain( @@ -139,13 +137,13 @@ impl Consensus { .cloned() .map(crate::utils::cometbft_to_sequencer_validator) .collect::>() - .context( + .wrap_err( "failed converting cometbft genesis validators to astria validators", )?, init_chain.chain_id, ) .await - .context("failed to call init_chain")?; + .wrap_err("failed to call init_chain")?; self.app.commit(self.storage.clone()).await; Ok(response::InitChain { @@ -159,7 +157,7 @@ impl Consensus { async fn handle_prepare_proposal( &mut self, prepare_proposal: request::PrepareProposal, - ) -> anyhow::Result { + ) -> Result { self.app .prepare_proposal(prepare_proposal, self.storage.clone()) .await @@ -169,7 +167,7 @@ impl Consensus { async fn handle_process_proposal( &mut self, process_proposal: request::ProcessProposal, - ) -> anyhow::Result<()> { + ) -> Result<()> { self.app .process_proposal(process_proposal, self.storage.clone()) .await?; @@ -181,17 +179,17 @@ impl Consensus { async fn finalize_block( &mut self, finalize_block: request::FinalizeBlock, - ) -> anyhow::Result { + ) -> Result { let finalize_block = self .app .finalize_block(finalize_block, self.storage.clone()) .await - .context("failed to call App::finalize_block")?; + .wrap_err("failed to call App::finalize_block")?; Ok(finalize_block) } #[instrument(skip_all)] - async fn commit(&mut self) -> anyhow::Result { + async fn commit(&mut self) -> Result { self.app.commit(self.storage.clone()).await; Ok(response::Commit::default()) } diff --git a/crates/astria-sequencer/src/service/info/mod.rs b/crates/astria-sequencer/src/service/info/mod.rs index 11e1348036..e27935e4bc 100644 --- a/crates/astria-sequencer/src/service/info/mod.rs +++ b/crates/astria-sequencer/src/service/info/mod.rs @@ -6,8 +6,8 @@ use std::{ }, }; -use anyhow::Context as _; use astria_core::protocol::abci::AbciErrorCode; +use astria_eyre::eyre::WrapErr as _; use cnidarium::Storage; use futures::{ Future, @@ -32,6 +32,11 @@ use tracing::{ mod abci_query_router; +use astria_eyre::{ + anyhow_to_eyre, + eyre::Result, +}; + use crate::state_ext::StateReadExt; #[derive(Clone)] @@ -41,47 +46,47 @@ pub(crate) struct Info { } impl Info { - pub(crate) fn new(storage: Storage) -> anyhow::Result { + pub(crate) fn new(storage: Storage) -> Result { let mut query_router = abci_query_router::Router::new(); query_router .insert( "accounts/balance/:account", crate::accounts::query::balance_request, ) - .context("invalid path: `accounts/balance/:account`")?; + .wrap_err("invalid path: `accounts/balance/:account`")?; query_router .insert( "accounts/nonce/:account", crate::accounts::query::nonce_request, ) - .context("invalid path: `accounts/nonce/:account`")?; + .wrap_err("invalid path: `accounts/nonce/:account`")?; query_router .insert("asset/denom/:id", crate::assets::query::denom_request) - .context("invalid path: `asset/denom/:id`")?; + .wrap_err("invalid path: `asset/denom/:id`")?; query_router .insert( "asset/allowed_fee_assets", crate::assets::query::allowed_fee_assets_request, ) - .context("invalid path: `asset/allowed_fee_asset_ids`")?; + .wrap_err("invalid path: `asset/allowed_fee_asset_ids`")?; query_router .insert( "bridge/account_last_tx_hash/:address", crate::bridge::query::bridge_account_last_tx_hash_request, ) - .context("invalid path: `bridge/account_last_tx_hash/:address`")?; + .wrap_err("invalid path: `bridge/account_last_tx_hash/:address`")?; query_router .insert( "transaction/fee", crate::transaction::query::transaction_fee_request, ) - .context("invalid path: `transaction/fee`")?; + .wrap_err("invalid path: `transaction/fee`")?; query_router .insert( "bridge/account_info/:address", crate::bridge::query::bridge_account_info_request, ) - .context("invalid path: `bridge/account_info/:address`")?; + .wrap_err("invalid path: `bridge/account_info/:address`")?; Ok(Self { storage, query_router, @@ -103,7 +108,8 @@ impl Info { .latest_snapshot() .root_hash() .await - .context("failed to get app hash")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed to get app hash")?; let response = InfoResponse::Info(response::Info { version: env!("CARGO_PKG_VERSION").to_string(), diff --git a/crates/astria-sequencer/src/service/mempool.rs b/crates/astria-sequencer/src/service/mempool.rs index 4e027e0ce7..9dc31e0f02 100644 --- a/crates/astria-sequencer/src/service/mempool.rs +++ b/crates/astria-sequencer/src/service/mempool.rs @@ -9,7 +9,6 @@ use std::{ time::Instant, }; -use anyhow::Context as _; use astria_core::{ generated::protocol::transactions::v1alpha1 as raw, primitive::v1::asset::IbcPrefixed, @@ -18,6 +17,7 @@ use astria_core::{ transaction::v1alpha1::SignedTransaction, }, }; +use astria_eyre::eyre::WrapErr as _; use cnidarium::Storage; use futures::{ Future, @@ -285,7 +285,7 @@ async fn handle_check_tx { return response::CheckTx { diff --git a/crates/astria-sequencer/src/state_ext.rs b/crates/astria-sequencer/src/state_ext.rs index 78eb4c32cd..1e5f4c69da 100644 --- a/crates/astria-sequencer/src/state_ext.rs +++ b/crates/astria-sequencer/src/state_ext.rs @@ -1,7 +1,11 @@ -use anyhow::{ - bail, - Context as _, - Result, +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + bail, + eyre, + Result, + WrapErr as _, + }, }; use async_trait::async_trait; use cnidarium::{ @@ -24,13 +28,14 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw("chain_id") .await - .context("failed to read raw chain_id from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw chain_id from state")? else { bail!("chain id not found in state"); }; Ok(String::from_utf8(bytes) - .context("failed to parse chain id from raw bytes")? + .wrap_err("failed to parse chain id from raw bytes")? .try_into() .expect("only valid chain ids should be stored in the state")) } @@ -40,13 +45,14 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(REVISION_NUMBER_KEY) .await - .context("failed to read raw revision number from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw revision number from state")? else { bail!("revision number not found in state"); }; let bytes = TryInto::<[u8; 8]>::try_into(bytes).map_err(|b| { - anyhow::anyhow!( + eyre!( "expected 8 revision number bytes but got {}; this is a bug", b.len() ) @@ -60,7 +66,8 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw("block_height") .await - .context("failed to read raw block_height from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw block_height from state")? else { bail!("block height not found state"); }; @@ -75,22 +82,26 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw("block_timestamp") .await - .context("failed to read raw block_timestamp from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw block_timestamp from state")? else { bail!("block timestamp not found"); }; // no extra allocations in the happy path (meaning the bytes are utf8) Time::parse_from_rfc3339(&String::from_utf8_lossy(&bytes)) - .context("failed to parse timestamp from raw timestamp bytes") + .wrap_err("failed to parse timestamp from raw timestamp bytes") } #[instrument(skip_all)] async fn get_storage_version_by_height(&self, height: u64) -> Result { + use astria_eyre::eyre::WrapErr as _; + let key = storage_version_by_height_key(height); let Some(bytes) = self .nonverifiable_get_raw(&key) .await - .context("failed to read raw storage_version from state")? + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw storage_version from state")? else { bail!("storage version not found"); }; @@ -197,7 +208,7 @@ mod tests { let mut state = StateDelta::new(snapshot); // doesn't exist at first - state + let _ = state .get_chain_id() .await .expect_err("no chain ID should exist at first"); @@ -273,7 +284,7 @@ mod tests { let mut state = StateDelta::new(snapshot); // doesn't exist at first - state + let _ = state .get_block_height() .await .expect_err("no block height should exist at first"); @@ -310,7 +321,7 @@ mod tests { let mut state = StateDelta::new(snapshot); // doesn't exist at first - state + let _ = state .get_block_timestamp() .await .expect_err("no block timestamp should exist at first"); @@ -348,7 +359,7 @@ mod tests { // doesn't exist at first let block_height_orig = 0; - state + let _ = state .get_storage_version_by_height(block_height_orig) .await .expect_err("no block height should exist at first"); diff --git a/crates/astria-sequencer/src/test_utils.rs b/crates/astria-sequencer/src/test_utils.rs index f39ee12d71..0d3bdd35df 100644 --- a/crates/astria-sequencer/src/test_utils.rs +++ b/crates/astria-sequencer/src/test_utils.rs @@ -47,7 +47,7 @@ pub(crate) fn verification_key(seed: u64) -> astria_core::crypto::VerificationKe #[cfg(test)] #[track_caller] -pub(crate) fn assert_anyhow_error(error: &anyhow::Error, expected: &'static str) { +pub(crate) fn assert_eyre_error(error: &astria_eyre::eyre::Error, expected: &'static str) { let msg = error.to_string(); assert!( msg.contains(expected), diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index 9604800f91..8eac5f5205 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -1,9 +1,5 @@ use std::collections::HashMap; -use anyhow::{ - ensure, - Context as _, -}; use astria_core::{ primitive::v1::{ asset, @@ -19,6 +15,11 @@ use astria_core::{ UnsignedTransaction, }, }; +use astria_eyre::eyre::{ + ensure, + Result, + WrapErr as _, +}; use cnidarium::StateRead; use tracing::instrument; @@ -34,18 +35,18 @@ use crate::{ pub(crate) async fn check_nonce_mempool( tx: &SignedTransaction, state: &S, -) -> anyhow::Result<()> { +) -> Result<()> { let signer_address = state .try_base_prefixed(&tx.verification_key().address_bytes()) .await - .context( + .wrap_err( "failed constructing the signer address from signed transaction verification and \ prefix provided by app state", )?; let curr_nonce = state .get_account_nonce(signer_address) .await - .context("failed to get account nonce")?; + .wrap_err("failed to get account nonce")?; ensure!(tx.nonce() >= curr_nonce, "nonce already used by account"); Ok(()) } @@ -54,11 +55,11 @@ pub(crate) async fn check_nonce_mempool( pub(crate) async fn check_chain_id_mempool( tx: &SignedTransaction, state: &S, -) -> anyhow::Result<()> { +) -> Result<()> { let chain_id = state .get_chain_id() .await - .context("failed to get chain id")?; + .wrap_err("failed to get chain id")?; ensure!(tx.chain_id() == chain_id.as_str(), "chain id mismatch"); Ok(()) } @@ -67,27 +68,27 @@ pub(crate) async fn check_chain_id_mempool( pub(crate) async fn get_fees_for_transaction( tx: &UnsignedTransaction, state: &S, -) -> anyhow::Result> { +) -> Result> { let transfer_fee = state .get_transfer_base_fee() .await - .context("failed to get transfer base fee")?; + .wrap_err("failed to get transfer base fee")?; let ics20_withdrawal_fee = state .get_ics20_withdrawal_base_fee() .await - .context("failed to get ics20 withdrawal base fee")?; + .wrap_err("failed to get ics20 withdrawal base fee")?; let init_bridge_account_fee = state .get_init_bridge_account_base_fee() .await - .context("failed to get init bridge account base fee")?; + .wrap_err("failed to get init bridge account base fee")?; let bridge_lock_byte_cost_multiplier = state .get_bridge_lock_byte_cost_multiplier() .await - .context("failed to get bridge lock byte cost multiplier")?; + .wrap_err("failed to get bridge lock byte cost multiplier")?; let bridge_sudo_change_fee = state .get_bridge_sudo_change_base_fee() .await - .context("failed to get bridge sudo change fee")?; + .wrap_err("failed to get bridge sudo change fee")?; let mut fees_by_asset = HashMap::new(); for (i, action) in tx.actions.iter().enumerate() { @@ -146,7 +147,7 @@ pub(crate) async fn get_fees_for_transaction( pub(crate) async fn check_balance_for_total_fees_and_transfers( tx: &SignedTransaction, state: &S, -) -> anyhow::Result<()> { +) -> Result<()> { let cost_by_asset = get_total_transaction_cost(tx, state) .await .context("failed to get transaction costs")?; @@ -172,7 +173,7 @@ pub(crate) async fn check_balance_for_total_fees_and_transfers( pub(crate) async fn get_total_transaction_cost( tx: &SignedTransaction, state: &S, -) -> anyhow::Result> { +) -> Result> { let mut cost_by_asset: HashMap = get_fees_for_transaction(tx.unsigned_transaction(), state) .await @@ -203,7 +204,7 @@ pub(crate) async fn get_total_transaction_cost( let asset = state .get_bridge_account_ibc_asset(tx) .await - .context("failed to get bridge account asset id")?; + .wrap_err("failed to get bridge account asset id")?; cost_by_asset .entry(asset) .and_modify(|amt| *amt = amt.saturating_add(act.amount)) @@ -242,10 +243,10 @@ async fn sequence_update_fees( fee_asset: &asset::Denom, fees_by_asset: &mut HashMap, data: &[u8], -) -> anyhow::Result<()> { +) -> Result<()> { let fee = crate::sequence::calculate_fee_from_state(data, state) .await - .context("fee for sequence action overflowed; data too large")?; + .wrap_err("fee for sequence action overflowed; data too large")?; fees_by_asset .entry(fee_asset.to_ibc_prefixed()) .and_modify(|amt| *amt = amt.saturating_add(fee)) diff --git a/crates/astria-sequencer/src/transaction/mod.rs b/crates/astria-sequencer/src/transaction/mod.rs index 1ff6ce1d28..97ccb63c13 100644 --- a/crates/astria-sequencer/src/transaction/mod.rs +++ b/crates/astria-sequencer/src/transaction/mod.rs @@ -4,14 +4,19 @@ mod state_ext; use std::fmt; -use anyhow::{ - ensure, - Context as _, -}; use astria_core::protocol::transaction::v1alpha1::{ action::Action, SignedTransaction, }; +use astria_eyre::{ + anyhow_to_eyre, + eyre::{ + ensure, + OptionExt as _, + Result, + WrapErr as _, + }, +}; pub(crate) use checks::{ check_balance_for_total_fees_and_transfers, check_chain_id_mempool, @@ -77,7 +82,7 @@ impl std::error::Error for InvalidNonce {} #[async_trait::async_trait] impl ActionHandler for SignedTransaction { - async fn check_stateless(&self) -> anyhow::Result<()> { + async fn check_stateless(&self) -> Result<()> { ensure!(!self.actions().is_empty(), "must have at least one action"); for action in self.actions() { @@ -85,23 +90,23 @@ impl ActionHandler for SignedTransaction { Action::Transfer(act) => act .check_stateless() .await - .context("stateless check failed for TransferAction")?, + .wrap_err("stateless check failed for TransferAction")?, Action::Sequence(act) => act .check_stateless() .await - .context("stateless check failed for SequenceAction")?, + .wrap_err("stateless check failed for SequenceAction")?, Action::ValidatorUpdate(act) => act .check_stateless() .await - .context("stateless check failed for ValidatorUpdateAction")?, + .wrap_err("stateless check failed for ValidatorUpdateAction")?, Action::SudoAddressChange(act) => act .check_stateless() .await - .context("stateless check failed for SudoAddressChangeAction")?, + .wrap_err("stateless check failed for SudoAddressChangeAction")?, Action::FeeChange(act) => act .check_stateless() .await - .context("stateless check failed for FeeChangeAction")?, + .wrap_err("stateless check failed for FeeChangeAction")?, Action::Ibc(act) => { let action = act .clone() @@ -109,36 +114,37 @@ impl ActionHandler for SignedTransaction { action .check_stateless(()) .await - .context("stateless check failed for IbcAction")?; + .map_err(anyhow_to_eyre) + .wrap_err("stateless check failed for IbcAction")?; } Action::Ics20Withdrawal(act) => act .check_stateless() .await - .context("stateless check failed for Ics20WithdrawalAction")?, + .wrap_err("stateless check failed for Ics20WithdrawalAction")?, Action::IbcRelayerChange(act) => act .check_stateless() .await - .context("stateless check failed for IbcRelayerChangeAction")?, + .wrap_err("stateless check failed for IbcRelayerChangeAction")?, Action::FeeAssetChange(act) => act .check_stateless() .await - .context("stateless check failed for FeeAssetChangeAction")?, + .wrap_err("stateless check failed for FeeAssetChangeAction")?, Action::InitBridgeAccount(act) => act .check_stateless() .await - .context("stateless check failed for InitBridgeAccountAction")?, + .wrap_err("stateless check failed for InitBridgeAccountAction")?, Action::BridgeLock(act) => act .check_stateless() .await - .context("stateless check failed for BridgeLockAction")?, + .wrap_err("stateless check failed for BridgeLockAction")?, Action::BridgeUnlock(act) => act .check_stateless() .await - .context("stateless check failed for BridgeLockAction")?, + .wrap_err("stateless check failed for BridgeLockAction")?, Action::BridgeSudoChange(act) => act .check_stateless() .await - .context("stateless check failed for BridgeSudoChangeAction")?, + .wrap_err("stateless check failed for BridgeSudoChangeAction")?, } } Ok(()) @@ -148,7 +154,7 @@ impl ActionHandler for SignedTransaction { // individual actions. This could be tidied up by implementing `ActionHandler for Action` // and letting it delegate. #[allow(clippy::too_many_lines)] - async fn check_and_execute(&self, mut state: S) -> anyhow::Result<()> { + async fn check_and_execute(&self, mut state: S) -> Result<()> { // Add the current signed transaction into the ephemeral state in case // downstream actions require access to it. // XXX: This must be deleted at the end of `check_stateful`. @@ -166,18 +172,18 @@ impl ActionHandler for SignedTransaction { let curr_nonce = state .get_account_nonce(self.address_bytes()) .await - .context("failed to get nonce for transaction signer")?; + .wrap_err("failed to get nonce for transaction signer")?; ensure!(curr_nonce == self.nonce(), InvalidNonce(self.nonce())); // Should have enough balance to cover all actions. check_balance_for_total_fees_and_transfers(self, &state) .await - .context("failed to check balance for total fees and transfers")?; + .wrap_err("failed to check balance for total fees and transfers")?; if state .get_bridge_account_rollup_id(self) .await - .context("failed to check account rollup id")? + .wrap_err("failed to check account rollup id")? .is_some() { state.put_last_transaction_id_for_bridge_account( @@ -189,13 +195,13 @@ impl ActionHandler for SignedTransaction { let from_nonce = state .get_account_nonce(self) .await - .context("failed getting nonce of transaction signer")?; + .wrap_err("failed getting nonce of transaction signer")?; let next_nonce = from_nonce .checked_add(1) - .context("overflow occurred incrementing stored nonce")?; + .ok_or_eyre("overflow occurred incrementing stored nonce")?; state .put_account_nonce(self, next_nonce) - .context("failed updating `from` nonce")?; + .wrap_err("failed updating `from` nonce")?; // FIXME: this should create one span per `check_and_execute` for (i, action) in (0..).zip(self.actions().iter()) { @@ -206,23 +212,23 @@ impl ActionHandler for SignedTransaction { Action::Transfer(act) => act .check_and_execute(&mut state) .await - .context("executing transfer action failed")?, + .wrap_err("executing transfer action failed")?, Action::Sequence(act) => act .check_and_execute(&mut state) .await - .context("executing sequence action failed")?, + .wrap_err("executing sequence action failed")?, Action::ValidatorUpdate(act) => act .check_and_execute(&mut state) .await - .context("executing validor update")?, + .wrap_err("executing validor update")?, Action::SudoAddressChange(act) => act .check_and_execute(&mut state) .await - .context("executing sudo address change failed")?, + .wrap_err("executing sudo address change failed")?, Action::FeeChange(act) => act .check_and_execute(&mut state) .await - .context("executing fee change failed")?, + .wrap_err("executing fee change failed")?, Action::Ibc(act) => { // FIXME: this check should be moved to check_and_execute, as it now has // access to the the signer through state. However, what's the correct @@ -232,7 +238,7 @@ impl ActionHandler for SignedTransaction { state .is_ibc_relayer(self) .await - .context("failed to check if address is IBC relayer")?, + .wrap_err("failed to check if address is IBC relayer")?, "only IBC sudo address can execute IBC actions" ); let action = act @@ -241,36 +247,37 @@ impl ActionHandler for SignedTransaction { action .check_and_execute(&mut state) .await - .context("failed executing ibc action")?; + .map_err(anyhow_to_eyre) + .wrap_err("failed executing ibc action")?; } Action::Ics20Withdrawal(act) => act .check_and_execute(&mut state) .await - .context("failed executing ics20 withdrawal")?, + .wrap_err("failed executing ics20 withdrawal")?, Action::IbcRelayerChange(act) => act .check_and_execute(&mut state) .await - .context("failed executing ibc relayer change")?, + .wrap_err("failed executing ibc relayer change")?, Action::FeeAssetChange(act) => act .check_and_execute(&mut state) .await - .context("failed executing fee asseet change")?, + .wrap_err("failed executing fee asseet change")?, Action::InitBridgeAccount(act) => act .check_and_execute(&mut state) .await - .context("failed executing init bridge account")?, + .wrap_err("failed executing init bridge account")?, Action::BridgeLock(act) => act .check_and_execute(&mut state) .await - .context("failed executing bridge lock")?, + .wrap_err("failed executing bridge lock")?, Action::BridgeUnlock(act) => act .check_and_execute(&mut state) .await - .context("failed executing bridge unlock")?, + .wrap_err("failed executing bridge unlock")?, Action::BridgeSudoChange(act) => act .check_and_execute(&mut state) .await - .context("failed executing bridge sudo change")?, + .wrap_err("failed executing bridge sudo change")?, } } diff --git a/crates/astria-sequencer/src/utils.rs b/crates/astria-sequencer/src/utils.rs index 61ea3451be..8de01d431f 100644 --- a/crates/astria-sequencer/src/utils.rs +++ b/crates/astria-sequencer/src/utils.rs @@ -1,10 +1,13 @@ -use anyhow::Context as _; use astria_core::{ generated::astria_vendored::tendermint::abci as raw, protocol::transaction::v1alpha1::action::ValidatorUpdate, sequencerblock::v1alpha1::block::Deposit, Protobuf as _, }; +use astria_eyre::eyre::{ + Result, + WrapErr as _, +}; use tendermint::abci::{ self, EventAttributeIndexExt as _, @@ -23,7 +26,7 @@ impl<'a> std::fmt::Display for Hex<'a> { pub(crate) fn cometbft_to_sequencer_validator( value: tendermint::validator::Update, -) -> anyhow::Result { +) -> Result { let tendermint_proto::abci::ValidatorUpdate { pub_key, power, @@ -32,7 +35,7 @@ pub(crate) fn cometbft_to_sequencer_validator( power, pub_key: pub_key.map(pubkey::cometbft_to_astria), }) - .context("failed converting cometbft validator update to astria validator update") + .wrap_err("failed converting cometbft validator update to astria validator update") } pub(crate) fn create_deposit_event(deposit: &Deposit) -> abci::Event { @@ -64,7 +67,7 @@ pub(crate) fn create_deposit_event(deposit: &Deposit) -> abci::Event { pub(crate) fn sequencer_to_cometbft_validator( value: ValidatorUpdate, -) -> anyhow::Result { +) -> Result { let astria_core::generated::astria_vendored::tendermint::abci::ValidatorUpdate { pub_key, power, @@ -74,7 +77,7 @@ pub(crate) fn sequencer_to_cometbft_validator( power, } .try_into() - .context("failed converting astria validator update to cometbft validator update") + .wrap_err("failed converting astria validator update to cometbft validator update") } mod pubkey {