From bb6c435d2be3118685ae855df63cc5b5eb9bd5d4 Mon Sep 17 00:00:00 2001 From: Fraser Hutchison <190532+Fraser999@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:55:13 +0100 Subject: [PATCH] chore(sequencer)!: update storage keys locations and values (ENG-898) (#1616) ## Summary The storage keys for all components were moved from the top of `state_ext` modules into new `storage::keys` modules. The key values were updated to a consistent style. ## Background We wanted the keys to be easy to find and to follow a consistent pattern in terms of their values and locations. ## Changes - All key consts and functions were moved to `/storage/keys`. - All keys were given a prefix matching the component name followed by a `/`. - All keys now use full words (except for a few instances of well-know abbreviations like "init") and `_` as word separators. - Existing helper structs in various `state_ext` modules for formatting keys were unified into a new struct `AddressPrefixer` in `crate::storage`. - The `Asset` functionality was moved from `crate::storage_keys` to `crate::storage`. ## Testing - All keys have snapshot tests (including const keys - this might be considered overkill?) - All keys have tests ensuring they have a component name as prefix. - All prefix consts or functions which are used in `state_ext` modules are tested to ensure they are actually prefixes of the given keys. ## Breaking Changelist - This breaks the on-disk format of both the verifiable and non-verifiable store. ## Related Issues Closes #1611. --- Cargo.lock | 1 + .../src/primitive/v1/asset/denom.rs | 32 +++- crates/astria-core/src/primitive/v1/mod.rs | 4 +- crates/astria-sequencer/Cargo.toml | 1 + .../astria-sequencer/src/accounts/action.rs | 7 +- ...ests__storage_keys_have_not_changed-2.snap | 6 - ..._tests__storage_keys_have_not_changed.snap | 6 - .../src/accounts/state_ext.rs | 106 +++-------- .../src/accounts/storage/keys.rs | 113 +++++++++++ .../src/accounts/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...keys__tests__keys_should_not_change-3.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + .../astria-sequencer/src/address/state_ext.rs | 21 +-- .../src/address/storage/keys.rs | 21 +++ .../src/address/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + ...ransaction_with_every_action_snapshot.snap | 56 +++--- ..._changes__app_finalize_block_snapshot.snap | 62 +++--- ...reaking_changes__app_genesis_snapshot.snap | 60 +++--- crates/astria-sequencer/src/app/state_ext.rs | 43 ++--- .../astria-sequencer/src/app/storage/keys.rs | 33 ++++ .../astria-sequencer/src/app/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...keys__tests__keys_should_not_change-3.snap | 6 + ...keys__tests__keys_should_not_change-4.snap | 6 + ...keys__tests__keys_should_not_change-5.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + ...__tests__storage_keys_are_unchanged-2.snap | 5 - ...__tests__storage_keys_are_unchanged-3.snap | 5 - ...xt__tests__storage_keys_are_unchanged.snap | 5 - .../astria-sequencer/src/assets/state_ext.rs | 170 +++++------------ .../src/assets/storage/keys.rs | 106 +++++++++++ .../src/assets/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...keys__tests__keys_should_not_change-3.snap | 6 + ...keys__tests__keys_should_not_change-4.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + .../src/authority/state_ext.rs | 34 ++-- .../src/authority/storage/keys.rs | 24 +++ .../src/authority/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...keys__tests__keys_should_not_change-3.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + .../src/bridge/bridge_lock_action.rs | 3 +- .../src/bridge/bridge_sudo_change_action.rs | 7 +- .../src/bridge/init_bridge_account_action.rs | 3 +- ...ests__storage_keys_have_not_changed-2.snap | 6 - ...ests__storage_keys_have_not_changed-3.snap | 6 - ...ests__storage_keys_have_not_changed-4.snap | 6 - ..._tests__storage_keys_have_not_changed.snap | 6 - .../astria-sequencer/src/bridge/state_ext.rs | 178 +++--------------- .../src/bridge/storage/keys.rs | 141 ++++++++++++++ .../src/bridge/storage/mod.rs | 1 + ...eys__tests__keys_should_not_change-10.snap | 6 + ...eys__tests__keys_should_not_change-11.snap | 6 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...keys__tests__keys_should_not_change-3.snap | 6 + ...keys__tests__keys_should_not_change-4.snap | 6 + ...keys__tests__keys_should_not_change-5.snap | 6 + ...keys__tests__keys_should_not_change-6.snap | 6 + ...keys__tests__keys_should_not_change-7.snap | 6 + ...keys__tests__keys_should_not_change-8.snap | 6 + ...keys__tests__keys_should_not_change-9.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + crates/astria-sequencer/src/grpc/state_ext.rs | 67 +++---- .../astria-sequencer/src/grpc/storage/keys.rs | 70 +++++++ .../astria-sequencer/src/grpc/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...keys__tests__keys_should_not_change-3.snap | 6 + ...keys__tests__keys_should_not_change-4.snap | 6 + ...keys__tests__keys_should_not_change-5.snap | 6 + ...keys__tests__keys_should_not_change-6.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + .../src/ibc/ics20_withdrawal.rs | 3 +- ...ests__storage_keys_have_not_changed-2.snap | 5 - ..._tests__storage_keys_have_not_changed.snap | 5 - crates/astria-sequencer/src/ibc/state_ext.rs | 98 +++------- .../astria-sequencer/src/ibc/storage/keys.rs | 73 +++++++ .../astria-sequencer/src/ibc/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...keys__tests__keys_should_not_change-3.snap | 6 + ...keys__tests__keys_should_not_change-4.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + crates/astria-sequencer/src/lib.rs | 1 - .../astria-sequencer/src/sequence/action.rs | 7 +- .../src/sequence/state_ext.rs | 21 +-- .../src/sequence/storage/keys.rs | 22 +++ .../src/sequence/storage/mod.rs | 1 + ...keys__tests__keys_should_not_change-2.snap | 6 + ...__keys__tests__keys_should_not_change.snap | 6 + crates/astria-sequencer/src/storage/keys.rs | 91 +++++++++ crates/astria-sequencer/src/storage/mod.rs | 1 + crates/astria-sequencer/src/storage_keys.rs | 60 ------ 95 files changed, 1277 insertions(+), 773 deletions(-) delete mode 100644 crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed-2.snap delete mode 100644 crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed.snap create mode 100644 crates/astria-sequencer/src/accounts/storage/keys.rs create mode 100644 crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-3.snap create mode 100644 crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change.snap create mode 100644 crates/astria-sequencer/src/address/storage/keys.rs create mode 100644 crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change.snap create mode 100644 crates/astria-sequencer/src/app/storage/keys.rs create mode 100644 crates/astria-sequencer/src/app/storage/snapshots/astria_sequencer__app__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/app/storage/snapshots/astria_sequencer__app__storage__keys__tests__keys_should_not_change-3.snap create mode 100644 crates/astria-sequencer/src/app/storage/snapshots/astria_sequencer__app__storage__keys__tests__keys_should_not_change-4.snap create mode 100644 crates/astria-sequencer/src/app/storage/snapshots/astria_sequencer__app__storage__keys__tests__keys_should_not_change-5.snap create mode 100644 crates/astria-sequencer/src/app/storage/snapshots/astria_sequencer__app__storage__keys__tests__keys_should_not_change.snap delete mode 100644 crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-2.snap delete mode 100644 crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-3.snap delete mode 100644 crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged.snap create mode 100644 crates/astria-sequencer/src/assets/storage/keys.rs create mode 100644 crates/astria-sequencer/src/assets/storage/snapshots/astria_sequencer__assets__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/assets/storage/snapshots/astria_sequencer__assets__storage__keys__tests__keys_should_not_change-3.snap create mode 100644 crates/astria-sequencer/src/assets/storage/snapshots/astria_sequencer__assets__storage__keys__tests__keys_should_not_change-4.snap create mode 100644 crates/astria-sequencer/src/assets/storage/snapshots/astria_sequencer__assets__storage__keys__tests__keys_should_not_change.snap create mode 100644 crates/astria-sequencer/src/authority/storage/keys.rs create mode 100644 crates/astria-sequencer/src/authority/storage/snapshots/astria_sequencer__authority__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/authority/storage/snapshots/astria_sequencer__authority__storage__keys__tests__keys_should_not_change-3.snap create mode 100644 crates/astria-sequencer/src/authority/storage/snapshots/astria_sequencer__authority__storage__keys__tests__keys_should_not_change.snap delete mode 100644 crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__tests__storage_keys_have_not_changed-2.snap delete mode 100644 crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__tests__storage_keys_have_not_changed-3.snap delete mode 100644 crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__tests__storage_keys_have_not_changed-4.snap delete mode 100644 crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__tests__storage_keys_have_not_changed.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/keys.rs create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-10.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-11.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-3.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-4.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-5.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-6.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-7.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-8.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change-9.snap create mode 100644 crates/astria-sequencer/src/bridge/storage/snapshots/astria_sequencer__bridge__storage__keys__tests__keys_should_not_change.snap create mode 100644 crates/astria-sequencer/src/grpc/storage/keys.rs create mode 100644 crates/astria-sequencer/src/grpc/storage/snapshots/astria_sequencer__grpc__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/grpc/storage/snapshots/astria_sequencer__grpc__storage__keys__tests__keys_should_not_change-3.snap create mode 100644 crates/astria-sequencer/src/grpc/storage/snapshots/astria_sequencer__grpc__storage__keys__tests__keys_should_not_change-4.snap create mode 100644 crates/astria-sequencer/src/grpc/storage/snapshots/astria_sequencer__grpc__storage__keys__tests__keys_should_not_change-5.snap create mode 100644 crates/astria-sequencer/src/grpc/storage/snapshots/astria_sequencer__grpc__storage__keys__tests__keys_should_not_change-6.snap create mode 100644 crates/astria-sequencer/src/grpc/storage/snapshots/astria_sequencer__grpc__storage__keys__tests__keys_should_not_change.snap delete mode 100644 crates/astria-sequencer/src/ibc/snapshots/astria_sequencer__ibc__state_ext__tests__storage_keys_have_not_changed-2.snap delete mode 100644 crates/astria-sequencer/src/ibc/snapshots/astria_sequencer__ibc__state_ext__tests__storage_keys_have_not_changed.snap create mode 100644 crates/astria-sequencer/src/ibc/storage/keys.rs create mode 100644 crates/astria-sequencer/src/ibc/storage/snapshots/astria_sequencer__ibc__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/ibc/storage/snapshots/astria_sequencer__ibc__storage__keys__tests__keys_should_not_change-3.snap create mode 100644 crates/astria-sequencer/src/ibc/storage/snapshots/astria_sequencer__ibc__storage__keys__tests__keys_should_not_change-4.snap create mode 100644 crates/astria-sequencer/src/ibc/storage/snapshots/astria_sequencer__ibc__storage__keys__tests__keys_should_not_change.snap create mode 100644 crates/astria-sequencer/src/sequence/storage/keys.rs create mode 100644 crates/astria-sequencer/src/sequence/storage/snapshots/astria_sequencer__sequence__storage__keys__tests__keys_should_not_change-2.snap create mode 100644 crates/astria-sequencer/src/sequence/storage/snapshots/astria_sequencer__sequence__storage__keys__tests__keys_should_not_change.snap create mode 100644 crates/astria-sequencer/src/storage/keys.rs delete mode 100644 crates/astria-sequencer/src/storage_keys.rs diff --git a/Cargo.lock b/Cargo.lock index 5995e436cb..063e3a9d2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -795,6 +795,7 @@ dependencies = [ "astria-merkle", "astria-telemetry", "async-trait", + "base64 0.21.7", "borsh", "bytes", "cnidarium", diff --git a/crates/astria-core/src/primitive/v1/asset/denom.rs b/crates/astria-core/src/primitive/v1/asset/denom.rs index ec685dba7d..6913f2321b 100644 --- a/crates/astria-core/src/primitive/v1/asset/denom.rs +++ b/crates/astria-core/src/primitive/v1/asset/denom.rs @@ -1,4 +1,5 @@ use std::{ + borrow::Cow, collections::VecDeque, str::FromStr, }; @@ -140,6 +141,27 @@ impl<'a> From<&'a IbcPrefixed> for IbcPrefixed { } } +impl<'a> From<&'a IbcPrefixed> for Cow<'a, IbcPrefixed> { + fn from(ibc_prefixed: &'a IbcPrefixed) -> Self { + Cow::Borrowed(ibc_prefixed) + } +} + +impl<'a> From<&'a TracePrefixed> for Cow<'a, IbcPrefixed> { + fn from(trace_prefixed: &'a TracePrefixed) -> Self { + Cow::Owned(trace_prefixed.to_ibc_prefixed()) + } +} + +impl<'a> From<&'a Denom> for Cow<'a, IbcPrefixed> { + fn from(value: &'a Denom) -> Self { + match value { + Denom::TracePrefixed(trace_prefixed) => Cow::from(trace_prefixed), + Denom::IbcPrefixed(ibc_prefixed) => Cow::from(ibc_prefixed), + } + } +} + impl FromStr for Denom { type Err = ParseDenomError; @@ -543,20 +565,22 @@ pub struct IbcPrefixed { } impl IbcPrefixed { + pub const ENCODED_HASH_LEN: usize = 32; + #[must_use] - pub fn new(id: [u8; 32]) -> Self { + pub const fn new(id: [u8; Self::ENCODED_HASH_LEN]) -> Self { Self { id, } } #[must_use] - pub fn as_bytes(&self) -> &[u8; 32] { + pub const fn as_bytes(&self) -> &[u8; Self::ENCODED_HASH_LEN] { &self.id } #[must_use] - pub fn display_len(&self) -> usize { + pub const fn display_len(&self) -> usize { 68 // "ibc/" + 64 hex characters } } @@ -586,7 +610,7 @@ impl FromStr for IbcPrefixed { if segments.next().is_some() { return Err(ParseIbcPrefixedError::too_many_segments()); } - let id = <[u8; 32]>::from_hex(hex).map_err(Self::Err::hex)?; + let id = <[u8; Self::ENCODED_HASH_LEN]>::from_hex(hex).map_err(Self::Err::hex)?; Ok(Self { id, }) diff --git a/crates/astria-core/src/primitive/v1/mod.rs b/crates/astria-core/src/primitive/v1/mod.rs index 531ebe8f72..36d3f0d583 100644 --- a/crates/astria-core/src/primitive/v1/mod.rs +++ b/crates/astria-core/src/primitive/v1/mod.rs @@ -8,7 +8,7 @@ use std::{ use base64::{ display::Base64Display, - prelude::BASE64_STANDARD, + prelude::BASE64_URL_SAFE, }; use bytes::Bytes; use sha2::{ @@ -231,7 +231,7 @@ impl From<&RollupId> for RollupId { impl std::fmt::Display for RollupId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Base64Display::new(self.as_ref(), &BASE64_STANDARD).fmt(f) + Base64Display::new(self.as_ref(), &BASE64_URL_SAFE).fmt(f) } } diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index a31c86ac27..5f5f2cec24 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -47,6 +47,7 @@ tower-actor = "0.1.0" tower-http = { version = "0.4", features = ["cors"] } async-trait = { workspace = true } +base64 = { workspace = true } bytes = { workspace = true } divan = { workspace = true, optional = true } futures = { workspace = true } diff --git a/crates/astria-sequencer/src/accounts/action.rs b/crates/astria-sequencer/src/accounts/action.rs index b248bc3624..32b2a482b0 100644 --- a/crates/astria-sequencer/src/accounts/action.rs +++ b/crates/astria-sequencer/src/accounts/action.rs @@ -1,7 +1,4 @@ -use astria_core::{ - protocol::transaction::v1alpha1::action::Transfer, - Protobuf as _, -}; +use astria_core::protocol::transaction::v1alpha1::action::Transfer; use astria_eyre::eyre::{ ensure, OptionExt as _, @@ -71,7 +68,7 @@ where .await .wrap_err("failed to get transfer base fee")?; state - .get_and_increase_block_fees(&action.fee_asset, fee, Transfer::full_name()) + .get_and_increase_block_fees::(&action.fee_asset, fee) .await .wrap_err("failed to add to block fees")?; diff --git a/crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed-2.snap b/crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed-2.snap deleted file mode 100644 index 9b2ee0652b..0000000000 --- a/crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed-2.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/astria-sequencer/src/accounts/state_ext.rs -assertion_line: 855 -expression: nonce_storage_key(&address) ---- -accounts/1c0c490f1b5528d8173c5de46d131160e4b2c0c3/nonce diff --git a/crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed.snap b/crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed.snap deleted file mode 100644 index 9125e1ba51..0000000000 --- a/crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/astria-sequencer/src/accounts/state_ext.rs -assertion_line: 854 -expression: "balance_storage_key(&address, &asset)" ---- -accounts/1c0c490f1b5528d8173c5de46d131160e4b2c0c3/balance/be429a02d00837245167a2616674a979a2ac6f9806468b48a975b156ad711320 diff --git a/crates/astria-sequencer/src/accounts/state_ext.rs b/crates/astria-sequencer/src/accounts/state_ext.rs index 1aca12616c..5348d0ee2d 100644 --- a/crates/astria-sequencer/src/accounts/state_ext.rs +++ b/crates/astria-sequencer/src/accounts/state_ext.rs @@ -1,4 +1,5 @@ use std::{ + borrow::Cow, fmt::Display, pin::Pin, task::{ @@ -27,44 +28,18 @@ use futures::Stream; use pin_project_lite::pin_project; use tracing::instrument; -use super::storage; +use super::storage::{ + self, + keys::{ + self, + extract_asset_from_key, + }, +}; use crate::{ accounts::AddressBytes, storage::StoredValue, }; -const ACCOUNTS_PREFIX: &str = "accounts"; -const TRANSFER_BASE_FEE_STORAGE_KEY: &str = "transferfee"; - -struct StorageKey<'a, T>(&'a T); -impl<'a, T: AddressBytes> std::fmt::Display for StorageKey<'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(ACCOUNTS_PREFIX)?; - f.write_str("/")?; - for byte in self.0.address_bytes() { - f.write_fmt(format_args!("{byte:02x}"))?; - } - Ok(()) - } -} - -fn balance_storage_key<'a, TAddress, TAsset>(address: &TAddress, asset: &'a TAsset) -> String -where - TAddress: AddressBytes, - asset::IbcPrefixed: From<&'a TAsset>, -{ - let asset: asset::IbcPrefixed = asset.into(); - format!( - "{}/balance/{}", - StorageKey(address), - crate::storage_keys::hunks::Asset::from(asset) - ) -} - -fn nonce_storage_key(address: &T) -> String { - format!("{}/nonce", StorageKey(address)) -} - pin_project! { /// A stream of IBC prefixed assets for a given account. pub(crate) struct AccountAssetsStream { @@ -141,15 +116,6 @@ where } } -fn extract_asset_from_key(s: &str) -> Result { - Ok(s.strip_prefix("accounts/") - .and_then(|s| s.split_once("/balance/").map(|(_, asset)| asset)) - .ok_or_eyre("failed to strip prefix from account balance key")? - .parse::() - .context("failed to parse storage key suffix as address hunk")? - .get()) -} - #[async_trait] pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { #[instrument(skip_all)] @@ -157,7 +123,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { &self, address: &T, ) -> AccountAssetsStream { - let prefix = format!("{}/balance/", StorageKey(address)); + let prefix = keys::balance_prefix(address); AccountAssetsStream { underlying: self.prefix_keys(&prefix), } @@ -168,7 +134,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { &self, address: &T, ) -> AccountAssetBalancesStream { - let prefix = format!("{}/balance/", StorageKey(address)); + let prefix = keys::balance_prefix(address); AccountAssetBalancesStream { underlying: self.prefix_raw(&prefix), } @@ -183,10 +149,10 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { where TAddress: AddressBytes, TAsset: Sync + Display, - asset::IbcPrefixed: From<&'a TAsset> + Send + Sync, + &'a TAsset: Into>, { let Some(bytes) = self - .get_raw(&balance_storage_key(address, asset)) + .get_raw(&keys::balance(address, asset)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw account balance from state")? @@ -201,7 +167,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { #[instrument(skip_all)] async fn get_account_nonce(&self, address: &T) -> Result { let bytes = self - .get_raw(&nonce_storage_key(address)) + .get_raw(&keys::nonce(address)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw account nonce from state")?; @@ -217,7 +183,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { #[instrument(skip_all)] async fn get_transfer_base_fee(&self) -> Result { let bytes = self - .get_raw(TRANSFER_BASE_FEE_STORAGE_KEY) + .get_raw(keys::TRANSFER_BASE_FEE) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw transfer base fee from state")?; @@ -244,12 +210,12 @@ pub(crate) trait StateWriteExt: StateWrite { where TAddress: AddressBytes, TAsset: Display, - asset::IbcPrefixed: From<&'a TAsset> + Send, + &'a TAsset: Into>, { let bytes = StoredValue::from(storage::Balance::from(balance)) .serialize() .wrap_err("failed to serialize balance")?; - self.put_raw(balance_storage_key(address, asset), bytes); + self.put_raw(keys::balance(address, asset), bytes); Ok(()) } @@ -258,7 +224,7 @@ pub(crate) trait StateWriteExt: StateWrite { let bytes = StoredValue::from(storage::Nonce::from(nonce)) .serialize() .wrap_err("failed to serialize nonce")?; - self.put_raw(nonce_storage_key(address), bytes); + self.put_raw(keys::nonce(address), bytes); Ok(()) } @@ -272,7 +238,7 @@ pub(crate) trait StateWriteExt: StateWrite { where TAddress: AddressBytes, TAsset: Sync + Display, - asset::IbcPrefixed: From<&'a TAsset> + Send, + &'a TAsset: Into>, { let balance = self .get_account_balance(address, asset) @@ -299,7 +265,7 @@ pub(crate) trait StateWriteExt: StateWrite { where TAddress: AddressBytes, TAsset: Sync + Display, - asset::IbcPrefixed: From<&'a TAsset> + Send, + &'a TAsset: Into>, { let balance = self .get_account_balance(address, asset) @@ -321,7 +287,7 @@ pub(crate) trait StateWriteExt: StateWrite { let bytes = StoredValue::from(storage::Fee::from(fee)) .serialize() .wrap_err("failed to serialize fee")?; - self.put_raw(TRANSFER_BASE_FEE_STORAGE_KEY.to_string(), bytes); + self.put_raw(keys::TRANSFER_BASE_FEE.to_string(), bytes); Ok(()) } } @@ -330,17 +296,10 @@ impl StateWriteExt for T {} #[cfg(test)] mod tests { - use astria_core::primitive::v1::Address; use cnidarium::StateDelta; use futures::TryStreamExt as _; - use insta::assert_snapshot; - use super::{ - balance_storage_key, - nonce_storage_key, - StateReadExt as _, - StateWriteExt as _, - }; + use super::*; use crate::{ assets::{ StateReadExt as _, @@ -352,14 +311,15 @@ mod tests { }, }; - fn asset_0() -> astria_core::primitive::v1::asset::Denom { + fn asset_0() -> asset::Denom { "asset_0".parse().unwrap() } - fn asset_1() -> astria_core::primitive::v1::asset::Denom { + fn asset_1() -> asset::Denom { "asset_1".parse().unwrap() } - fn asset_2() -> astria_core::primitive::v1::asset::Denom { + + fn asset_2() -> asset::Denom { "asset_2".parse().unwrap() } @@ -834,20 +794,4 @@ mod tests { let retrieved_fee = state.get_transfer_base_fee().await.unwrap(); assert_eq!(retrieved_fee, 123); } - - #[test] - fn storage_keys_have_not_changed() { - let address: Address = "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - .parse() - .unwrap(); - let asset = "an/asset/with/a/prefix" - .parse::() - .unwrap(); - assert_eq!( - balance_storage_key(&address, &asset), - balance_storage_key(&address, &asset.to_ibc_prefixed()) - ); - assert_snapshot!(balance_storage_key(&address, &asset)); - assert_snapshot!(nonce_storage_key(&address)); - } } diff --git a/crates/astria-sequencer/src/accounts/storage/keys.rs b/crates/astria-sequencer/src/accounts/storage/keys.rs new file mode 100644 index 0000000000..e23e368bcf --- /dev/null +++ b/crates/astria-sequencer/src/accounts/storage/keys.rs @@ -0,0 +1,113 @@ +use std::borrow::Cow; + +use astria_core::primitive::v1::asset::IbcPrefixed; +use astria_eyre::eyre::{ + OptionExt as _, + Result, + WrapErr as _, +}; + +use crate::{ + accounts::AddressBytes, + storage::keys::{ + AccountPrefixer, + Asset, + }, +}; + +pub(in crate::accounts) const TRANSFER_BASE_FEE: &str = "accounts/transfer_base_fee"; +const COMPONENT_PREFIX: &str = "accounts/"; +const BALANCE_PREFIX: &str = "balance/"; +const NONCE: &str = "nonce"; + +/// Example: `accounts/gGhH....zZ4=/balance/`. +/// |base64 chars| +pub(in crate::accounts) fn balance_prefix(address: &TAddress) -> String { + format!( + "{}/{BALANCE_PREFIX}", + AccountPrefixer::new(COMPONENT_PREFIX, address) + ) +} + +/// Example: `accounts/gGhH....zZ4=/balance/0202....0202`. +/// |base64 chars| |64 hex chars| +pub(in crate::accounts) fn balance<'a, TAddress, TAsset>( + address: &TAddress, + asset: &'a TAsset, +) -> String +where + TAddress: AddressBytes, + &'a TAsset: Into>, +{ + format!( + "{}/{BALANCE_PREFIX}{}", + AccountPrefixer::new(COMPONENT_PREFIX, address), + Asset::from(asset) + ) +} + +/// Example: `accounts/gGhH....zZ4=/nonce`. +/// |base64 chars| +pub(in crate::accounts) fn nonce(address: &TAddress) -> String { + format!( + "{}/{NONCE}", + AccountPrefixer::new(COMPONENT_PREFIX, address) + ) +} + +pub(in crate::accounts) fn extract_asset_from_key(key: &str) -> Result { + Ok(key + .strip_prefix(COMPONENT_PREFIX) + .and_then(|s| s.split_once(BALANCE_PREFIX).map(|(_, asset)| asset)) + .ok_or_eyre("failed to strip prefix from account balance key")? + .parse::() + .wrap_err("failed to parse storage key suffix as address hunk")? + .get()) +} + +#[cfg(test)] +mod tests { + use astria_core::primitive::v1::{ + asset::Denom, + Address, + }; + + use super::*; + + fn address() -> Address { + "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + .parse() + .unwrap() + } + + fn asset() -> Denom { + "an/asset/with/a/prefix".parse().unwrap() + } + + #[test] + fn keys_should_not_change() { + insta::assert_snapshot!(TRANSFER_BASE_FEE); + insta::assert_snapshot!(balance(&address(), &asset())); + insta::assert_snapshot!(nonce(&address())); + } + + #[test] + fn keys_should_have_component_prefix() { + assert!(TRANSFER_BASE_FEE.starts_with(COMPONENT_PREFIX)); + assert!(balance(&address(), &asset()).starts_with(COMPONENT_PREFIX)); + assert!(nonce(&address()).starts_with(COMPONENT_PREFIX)); + } + + #[test] + fn balance_prefix_should_be_prefix_of_balance_key() { + assert!(balance(&address(), &asset()).starts_with(&balance_prefix(&address()))); + } + + #[test] + fn should_extract_asset_from_key() { + let asset = IbcPrefixed::new([2; 32]); + let key = balance(&[1; 20], &asset); + let recovered_asset = extract_asset_from_key(&key).unwrap(); + assert_eq!(asset, recovered_asset); + } +} diff --git a/crates/astria-sequencer/src/accounts/storage/mod.rs b/crates/astria-sequencer/src/accounts/storage/mod.rs index 5fb23de750..8d61ec9f93 100644 --- a/crates/astria-sequencer/src/accounts/storage/mod.rs +++ b/crates/astria-sequencer/src/accounts/storage/mod.rs @@ -1,3 +1,4 @@ +pub(super) mod keys; mod values; pub(crate) use values::Value; diff --git a/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-2.snap b/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-2.snap new file mode 100644 index 0000000000..f3ff1952e1 --- /dev/null +++ b/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-2.snap @@ -0,0 +1,6 @@ +--- +source: crates/astria-sequencer/src/accounts/storage/keys.rs +assertion_line: 90 +expression: "balance(&address(), &asset())" +--- +accounts/HAxJDxtVKNgXPF3kbRMRYOSywMM=/balance/ibc/be429a02d00837245167a2616674a979a2ac6f9806468b48a975b156ad711320 diff --git a/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-3.snap b/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-3.snap new file mode 100644 index 0000000000..6febfe41e9 --- /dev/null +++ b/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change-3.snap @@ -0,0 +1,6 @@ +--- +source: crates/astria-sequencer/src/accounts/storage/keys.rs +assertion_line: 91 +expression: nonce(&address()) +--- +accounts/HAxJDxtVKNgXPF3kbRMRYOSywMM=/nonce diff --git a/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change.snap b/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change.snap new file mode 100644 index 0000000000..a57562a2da --- /dev/null +++ b/crates/astria-sequencer/src/accounts/storage/snapshots/astria_sequencer__accounts__storage__keys__tests__keys_should_not_change.snap @@ -0,0 +1,6 @@ +--- +source: crates/astria-sequencer/src/accounts/storage/keys.rs +assertion_line: 79 +expression: TRANSFER_BASE_FEE_KEY +--- +accounts/transfer_base_fee diff --git a/crates/astria-sequencer/src/address/state_ext.rs b/crates/astria-sequencer/src/address/state_ext.rs index a0c074777b..0f5cd5bf30 100644 --- a/crates/astria-sequencer/src/address/state_ext.rs +++ b/crates/astria-sequencer/src/address/state_ext.rs @@ -18,12 +18,12 @@ use cnidarium::{ }; use tracing::instrument; -use super::storage; +use super::storage::{ + self, + keys, +}; use crate::storage::StoredValue; -const BASE_PREFIX_KEY: &str = "prefixes/base"; -const IBC_COMPAT_PREFIX_KEY: &str = "prefixes/ibc-compat"; - #[async_trait] pub(crate) trait StateReadExt: StateRead { async fn ensure_base_prefix(&self, address: &Address) -> Result<()> { @@ -54,7 +54,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all, err)] async fn get_base_prefix(&self) -> Result { let Some(bytes) = self - .get_raw(BASE_PREFIX_KEY) + .get_raw(keys::BASE_PREFIX) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading address base prefix from state")? @@ -69,7 +69,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all, err)] async fn get_ibc_compat_prefix(&self) -> Result { let Some(bytes) = self - .get_raw(IBC_COMPAT_PREFIX_KEY) + .get_raw(keys::IBC_COMPAT_PREFIX) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading address ibc compat prefix from state")? @@ -91,7 +91,7 @@ pub(crate) trait StateWriteExt: StateWrite { let bytes = StoredValue::from(storage::AddressPrefix::from(prefix.as_str())) .serialize() .context("failed to serialize base prefix")?; - self.put_raw(BASE_PREFIX_KEY.to_string(), bytes); + self.put_raw(keys::BASE_PREFIX.to_string(), bytes); Ok(()) } @@ -100,7 +100,7 @@ pub(crate) trait StateWriteExt: StateWrite { let bytes = StoredValue::from(storage::AddressPrefix::from(prefix.as_str())) .serialize() .context("failed to serialize ibc-compat prefix")?; - self.put_raw(IBC_COMPAT_PREFIX_KEY.to_string(), bytes); + self.put_raw(keys::IBC_COMPAT_PREFIX.to_string(), bytes); Ok(()) } } @@ -111,10 +111,7 @@ impl StateWriteExt for T {} mod tests { use cnidarium::StateDelta; - use super::{ - StateReadExt as _, - StateWriteExt as _, - }; + use super::*; #[tokio::test] async fn put_and_get_base_prefix() { diff --git a/crates/astria-sequencer/src/address/storage/keys.rs b/crates/astria-sequencer/src/address/storage/keys.rs new file mode 100644 index 0000000000..af58a1d79f --- /dev/null +++ b/crates/astria-sequencer/src/address/storage/keys.rs @@ -0,0 +1,21 @@ +pub(in crate::address) const BASE_PREFIX: &str = "address/prefixes/base"; +pub(in crate::address) const IBC_COMPAT_PREFIX: &str = "address/prefixes/ibc_compat"; + +#[cfg(test)] +mod tests { + use super::*; + + const COMPONENT_PREFIX: &str = "address/"; + + #[test] + fn keys_should_not_change() { + insta::assert_snapshot!(BASE_PREFIX); + insta::assert_snapshot!(IBC_COMPAT_PREFIX); + } + + #[test] + fn keys_should_have_component_prefix() { + assert!(BASE_PREFIX.starts_with(COMPONENT_PREFIX)); + assert!(IBC_COMPAT_PREFIX.starts_with(COMPONENT_PREFIX)); + } +} diff --git a/crates/astria-sequencer/src/address/storage/mod.rs b/crates/astria-sequencer/src/address/storage/mod.rs index 9d97b02d12..66527bffe6 100644 --- a/crates/astria-sequencer/src/address/storage/mod.rs +++ b/crates/astria-sequencer/src/address/storage/mod.rs @@ -1,3 +1,4 @@ +pub(super) mod keys; mod values; pub(super) use values::AddressPrefix; diff --git a/crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change-2.snap b/crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change-2.snap new file mode 100644 index 0000000000..78e5051afb --- /dev/null +++ b/crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change-2.snap @@ -0,0 +1,6 @@ +--- +source: crates/astria-sequencer/src/address/storage/keys.rs +assertion_line: 11 +expression: IBC_COMPAT_PREFIX_KEY +--- +address/prefixes/ibc_compat diff --git a/crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change.snap b/crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change.snap new file mode 100644 index 0000000000..d0121e06ae --- /dev/null +++ b/crates/astria-sequencer/src/address/storage/snapshots/astria_sequencer__address__storage__keys__tests__keys_should_not_change.snap @@ -0,0 +1,6 @@ +--- +source: crates/astria-sequencer/src/address/storage/keys.rs +assertion_line: 10 +expression: BASE_PREFIX_KEY +--- +address/prefixes/base diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index ce1e424533..00738ae01d 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -4,36 +4,36 @@ assertion_line: 350 expression: app.app_hash.as_bytes() --- [ - 152, - 102, + 65, + 106, + 222, + 239, + 12, 37, - 231, - 116, + 108, + 130, 83, - 54, - 99, - 99, - 61, + 78, + 58, + 50, + 178, + 164, + 93, + 245, 18, - 167, - 133, - 246, - 157, - 153, - 240, 12, - 160, - 204, - 116, - 164, - 109, - 125, - 163, - 141, - 253, - 161, - 54, - 116, - 76, - 131 + 205, + 129, + 14, + 158, + 134, + 107, + 50, + 190, + 88, + 71, + 110, + 9, + 148, + 233 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap index 991e89c41c..e36d49f879 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap @@ -1,39 +1,39 @@ --- source: crates/astria-sequencer/src/app/tests_breaking_changes.rs -assertion_line: 159 +assertion_line: 157 expression: app.app_hash.as_bytes() --- [ - 185, - 5, - 77, - 15, - 122, - 4, - 105, + 65, + 14, + 103, + 92, + 219, + 56, + 251, + 135, + 182, + 207, + 215, + 245, + 234, + 148, + 33, 34, - 166, - 43, - 138, - 143, - 70, - 189, - 121, - 106, - 150, - 128, - 112, + 179, + 40, + 146, + 17, + 236, + 227, + 125, + 197, + 76, 142, - 173, - 98, - 148, - 152, - 2, - 3, - 100, - 153, - 98, - 154, - 77, - 199 + 194, + 180, + 9, + 157, + 73, + 63 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap index 30a896d96f..d1fd6ce989 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap @@ -1,39 +1,39 @@ --- source: crates/astria-sequencer/src/app/tests_breaking_changes.rs -assertion_line: 78 +assertion_line: 77 expression: app.app_hash.as_bytes() --- [ - 216, - 255, - 55, - 240, + 66, + 163, + 122, 253, - 150, - 85, - 195, - 135, - 107, - 242, - 140, - 90, - 79, - 175, - 169, - 15, - 89, - 132, 159, - 196, - 65, - 113, - 174, - 207, - 46, - 107, - 126, - 6, + 247, + 85, + 105, + 254, + 245, + 230, + 91, + 25, + 173, + 179, + 227, + 231, + 2, + 69, + 165, 1, - 239, - 84 + 169, + 100, + 210, + 97, + 33, + 91, + 87, + 140, + 222, + 83, + 57 ] diff --git a/crates/astria-sequencer/src/app/state_ext.rs b/crates/astria-sequencer/src/app/state_ext.rs index cd51110c84..b9a2e3b78e 100644 --- a/crates/astria-sequencer/src/app/state_ext.rs +++ b/crates/astria-sequencer/src/app/state_ext.rs @@ -14,24 +14,18 @@ use cnidarium::{ use tendermint::Time; use tracing::instrument; -use super::storage; +use super::storage::{ + self, + keys, +}; use crate::storage::StoredValue; -const CHAIN_ID_KEY: &str = "chain_id"; -const REVISION_NUMBER_KEY: &str = "revision_number"; -const BLOCK_HEIGHT_KEY: &str = "block_height"; -const BLOCK_TIMESTAMP_KEY: &str = "block_timestamp"; - -fn storage_version_by_height_key(height: u64) -> Vec { - format!("storage_version/{height}").into() -} - #[async_trait] pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_chain_id(&self) -> Result { let Some(bytes) = self - .get_raw(CHAIN_ID_KEY) + .get_raw(keys::CHAIN_ID) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw chain_id from state")? @@ -46,7 +40,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_revision_number(&self) -> Result { let Some(bytes) = self - .get_raw(REVISION_NUMBER_KEY) + .get_raw(keys::REVISION_NUMBER) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw revision number from state")? @@ -61,7 +55,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_block_height(&self) -> Result { let Some(bytes) = self - .get_raw(BLOCK_HEIGHT_KEY) + .get_raw(keys::BLOCK_HEIGHT) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw block_height from state")? @@ -76,7 +70,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_block_timestamp(&self) -> Result