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 {