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 df1b0bf0c8..0000000000 --- a/crates/astria-sequencer/src/accounts/snapshots/astria_sequencer__accounts__state_ext__tests__storage_keys_have_not_changed-2.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/accounts/state_ext.rs -expression: nonce_storage_key(address) ---- -accounts/1c0c490f1b5528d8173c5de46d131160e4b2c0c3/nonce diff --git a/crates/astria-sequencer/src/accounts/state_ext.rs b/crates/astria-sequencer/src/accounts/state_ext.rs index 49596f92e9..6ca7d9f5a8 100644 --- a/crates/astria-sequencer/src/accounts/state_ext.rs +++ b/crates/astria-sequencer/src/accounts/state_ext.rs @@ -31,6 +31,13 @@ use pin_project_lite::pin_project; use tracing::instrument; use super::AddressBytes; +use crate::storage::verifiable_keys::accounts::{ + balance_key, + balance_prefix, + extract_asset_from_key, + nonce_key, + TRANSFER_BASE_FEE_KEY, +}; /// Newtype wrapper to read and write a u32 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] @@ -44,36 +51,6 @@ struct Balance(u128); #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Fee(u128); -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>( - address: TAddress, - asset: TAsset, -) -> String { - 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 { @@ -153,15 +130,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)] @@ -169,7 +137,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { &self, address: impl AddressBytes, ) -> AccountAssetsStream { - let prefix = format!("{}/balance/", StorageKey(&address)); + let prefix = balance_prefix(&address); AccountAssetsStream { underlying: self.prefix_keys(&prefix), } @@ -180,7 +148,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { &self, address: impl AddressBytes, ) -> AccountAssetBalancesStream { - let prefix = format!("{}/balance/", StorageKey(&address)); + let prefix = balance_prefix(&address); AccountAssetBalancesStream { underlying: self.prefix_raw(&prefix), } @@ -199,7 +167,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt { TAsset: Into + std::fmt::Display + Send, { let Some(bytes) = self - .get_raw(&balance_storage_key(address, asset)) + .get_raw(&balance_key(address, asset)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw account balance from state")? @@ -213,7 +181,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(&nonce_key(&address)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw account nonce from state")?; @@ -229,7 +197,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(TRANSFER_BASE_FEE_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw transfer base fee from state")?; @@ -257,15 +225,15 @@ pub(crate) trait StateWriteExt: StateWrite { TAddress: AddressBytes, TAsset: Into + std::fmt::Display + Send, { - let bytes = borsh::to_vec(&Balance(balance)).wrap_err("failed to serialize balance")?; - self.put_raw(balance_storage_key(address, asset), bytes); + let bytes = borsh::to_vec(&Balance(balance)).context("failed to serialize balance")?; + self.put_raw(balance_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)).wrap_err("failed to serialize nonce")?; - self.put_raw(nonce_storage_key(address), bytes); + let bytes = borsh::to_vec(&Nonce(nonce)).context("failed to serialize nonce")?; + self.put_raw(nonce_key(&address), bytes); Ok(()) } @@ -327,8 +295,8 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_transfer_base_fee(&mut self, fee: u128) -> Result<()> { - let bytes = borsh::to_vec(&Fee(fee)).wrap_err("failed to serialize fee")?; - self.put_raw(TRANSFER_BASE_FEE_STORAGE_KEY.to_string(), bytes); + let bytes = borsh::to_vec(&Fee(fee)).context("failed to serialize fee")?; + self.put_raw(TRANSFER_BASE_FEE_KEY.to_string(), bytes); Ok(()) } } @@ -337,20 +305,11 @@ 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::{ - StateReadExt as _, - StateWriteExt as _, - }; + use super::*; use crate::{ - accounts::state_ext::{ - balance_storage_key, - nonce_storage_key, - }, assets::{ StateReadExt as _, StateWriteExt as _, @@ -361,14 +320,14 @@ 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() } @@ -832,20 +791,4 @@ mod tests { .await .expect_err("should not be able to subtract larger balance than what existed"); } - - #[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/address/state_ext.rs b/crates/astria-sequencer/src/address/state_ext.rs index 74a0a776aa..4e432fc793 100644 --- a/crates/astria-sequencer/src/address/state_ext.rs +++ b/crates/astria-sequencer/src/address/state_ext.rs @@ -18,13 +18,10 @@ use cnidarium::{ }; use tracing::instrument; -fn base_prefix_key() -> &'static str { - "prefixes/base" -} - -fn ibc_compat_prefix_key() -> &'static str { - "prefixes/ibc-compat" -} +use crate::storage::verifiable_keys::address::{ + BASE_PREFIX_KEY, + IBC_COMPAT_PREFIX_KEY, +}; #[async_trait] pub(crate) trait StateReadExt: StateRead { @@ -58,7 +55,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(BASE_PREFIX_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading address base prefix from state")? @@ -73,7 +70,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(IBC_COMPAT_PREFIX_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading address ibc compat prefix from state")? @@ -90,12 +87,12 @@ impl StateReadExt for T {} pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_base_prefix(&mut self, prefix: &str) { - self.put_raw(base_prefix_key().into(), prefix.into()); + self.put_raw(BASE_PREFIX_KEY.to_string(), prefix.into()); } #[instrument(skip_all)] fn put_ibc_compat_prefix(&mut self, prefix: &str) { - self.put_raw(ibc_compat_prefix_key().into(), prefix.into()); + self.put_raw(IBC_COMPAT_PREFIX_KEY.to_string(), prefix.into()); } } diff --git a/crates/astria-sequencer/src/api_state_ext.rs b/crates/astria-sequencer/src/api_state_ext.rs index c42510b9cb..065aeedc3b 100644 --- a/crates/astria-sequencer/src/api_state_ext.rs +++ b/crates/astria-sequencer/src/api_state_ext.rs @@ -34,29 +34,14 @@ use cnidarium::{ use prost::Message; use tracing::instrument; -fn block_hash_by_height_key(height: u64) -> String { - format!("blockhash/{height}") -} - -fn sequencer_block_header_by_hash_key(hash: &[u8]) -> String { - format!("blockheader/{}", crate::utils::Hex(hash)) -} - -fn rollup_data_by_hash_and_rollup_id_key(hash: &[u8], rollup_id: &RollupId) -> String { - format!("rollupdata/{}/{}", crate::utils::Hex(hash), rollup_id) -} - -fn rollup_ids_by_hash_key(hash: &[u8]) -> String { - format!("rollupids/{}", crate::utils::Hex(hash)) -} - -fn rollup_transactions_proof_by_hash_key(hash: &[u8]) -> String { - format!("rolluptxsproof/{}", crate::utils::Hex(hash)) -} - -fn rollup_ids_proof_by_hash_key(hash: &[u8]) -> String { - format!("rollupidsproof/{}", crate::utils::Hex(hash)) -} +use crate::storage::nonverifiable_keys::api::{ + block_hash_by_height_key, + rollup_data_by_hash_and_rollup_id_key, + rollup_ids_by_hash_key, + rollup_ids_proof_by_hash_key, + rollup_transactions_proof_by_hash_key, + sequencer_block_header_by_hash_key, +}; #[derive(BorshSerialize, BorshDeserialize)] struct RollupIdSeq( @@ -139,7 +124,7 @@ pub(crate) trait StateReadExt: StateRead { async fn get_block_hash_by_height(&self, height: u64) -> Result<[u8; 32]> { let key = block_hash_by_height_key(height); let Some(hash) = self - .get_raw(&key) + .nonverifiable_get_raw(&key) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read block hash by height from state")? @@ -160,7 +145,7 @@ pub(crate) trait StateReadExt: StateRead { ) -> Result { let key = sequencer_block_header_by_hash_key(hash); let Some(header_bytes) = self - .get_raw(&key) + .nonverifiable_get_raw(&key) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw sequencer block from state")? @@ -179,7 +164,7 @@ pub(crate) trait StateReadExt: StateRead { async fn get_rollup_ids_by_block_hash(&self, hash: &[u8]) -> Result> { let key = rollup_ids_by_hash_key(hash); let Some(rollup_ids_bytes) = self - .get_raw(&key) + .nonverifiable_get_raw(&key) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read rollup IDs by block hash from state")? @@ -195,7 +180,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_sequencer_block_by_hash(&self, hash: &[u8]) -> Result { let Some(header_bytes) = self - .get_raw(&sequencer_block_header_by_hash_key(hash)) + .nonverifiable_get_raw(&sequencer_block_header_by_hash_key(hash)) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw sequencer block from state")? @@ -214,10 +199,11 @@ pub(crate) trait StateReadExt: StateRead { 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.map_err(anyhow_to_eyre).wrap_err( - "failed to read rollup data by block hash and rollup ID from state", - )?; + let raw = self + .nonverifiable_get_raw(&key) + .await + .map_err(anyhow_to_eyre) + .context("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) @@ -227,7 +213,7 @@ pub(crate) trait StateReadExt: StateRead { } let Some(rollup_transactions_proof) = self - .get_raw(&rollup_transactions_proof_by_hash_key(hash)) + .nonverifiable_get_raw(&rollup_transactions_proof_by_hash_key(hash)) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read rollup transactions proof by block hash from state")? @@ -240,7 +226,7 @@ pub(crate) trait StateReadExt: StateRead { .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)) + .nonverifiable_get_raw(&rollup_ids_proof_by_hash_key(hash)) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read rollup IDs proof by block hash from state")? @@ -284,7 +270,7 @@ pub(crate) trait StateReadExt: StateRead { ) -> Result { let key = rollup_data_by_hash_and_rollup_id_key(hash, rollup_id); let Some(bytes) = self - .get_raw(&key) + .nonverifiable_get_raw(&key) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read rollup data by block hash and rollup ID from state")? @@ -306,7 +292,7 @@ pub(crate) trait StateReadExt: StateRead { hash: &[u8], ) -> Result<(primitiveRaw::Proof, primitiveRaw::Proof)> { let Some(rollup_transactions_proof) = self - .get_raw(&rollup_transactions_proof_by_hash_key(hash)) + .nonverifiable_get_raw(&rollup_transactions_proof_by_hash_key(hash)) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read rollup transactions proof by block hash from state")? @@ -319,7 +305,7 @@ pub(crate) trait StateReadExt: StateRead { .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)) + .nonverifiable_get_raw(&rollup_ids_proof_by_hash_key(hash)) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read rollup IDs proof by block hash from state")? @@ -348,7 +334,7 @@ pub(crate) trait StateWriteExt: StateWrite { // 6. block hash to rollup IDs proof let key = block_hash_by_height_key(block.height().into()); - self.put_raw(key, block.block_hash().to_vec()); + self.nonverifiable_put_raw(key, block.block_hash().to_vec()); let rollup_ids = block .rollup_transactions() @@ -359,7 +345,7 @@ pub(crate) trait StateWriteExt: StateWrite { let key = rollup_ids_by_hash_key(&block.block_hash()); - self.put_raw( + self.nonverifiable_put_raw( key, borsh::to_vec(&RollupIdSeq(rollup_ids)) .wrap_err("failed to serialize rollup IDs list")?, @@ -374,18 +360,18 @@ pub(crate) trait StateWriteExt: StateWrite { rollup_ids_proof, } = block.into_parts(); let header = header.into_raw(); - self.put_raw(key, header.encode_to_vec()); + self.nonverifiable_put_raw(key, header.encode_to_vec()); for (id, rollup_data) in rollup_transactions { let key = rollup_data_by_hash_and_rollup_id_key(&block_hash, &id); - self.put_raw(key, rollup_data.into_raw().encode_to_vec()); + self.nonverifiable_put_raw(key, rollup_data.into_raw().encode_to_vec()); } let key = rollup_transactions_proof_by_hash_key(&block_hash); - self.put_raw(key, rollup_transactions_proof.into_raw().encode_to_vec()); + self.nonverifiable_put_raw(key, rollup_transactions_proof.into_raw().encode_to_vec()); let key = rollup_ids_proof_by_hash_key(&block_hash); - self.put_raw(key, rollup_ids_proof.into_raw().encode_to_vec()); + self.nonverifiable_put_raw(key, rollup_ids_proof.into_raw().encode_to_vec()); Ok(()) } diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 66a9e2c5a8..d92cd06e84 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -879,7 +879,7 @@ impl App { .await .wrap_err("failed to get block deposits in end_block")?; state_tx - .clear_block_deposits() + .clear_block_deposit_nonces() .await .wrap_err("failed to clear block deposits")?; debug!( diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index 3e900c2423..579c57dc51 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 18, - 206, - 200, - 101, - 160, - 129, - 124, - 188, - 249, - 181, - 44, - 218, - 209, - 251, - 208, + 98, 119, - 122, - 211, - 105, - 201, - 75, - 214, - 56, - 145, + 255, + 37, + 50, + 148, + 173, + 225, + 73, + 8, + 85, + 223, + 87, 239, - 132, - 48, + 208, + 77, + 23, + 229, + 208, 0, - 79, - 13, - 53, - 156 + 108, + 163, + 67, + 97, + 222, + 83, + 63, + 46, + 116, + 31, + 142, + 68 ] 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 2c96d3d3ba..b806bb45fe 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 @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 113, - 62, - 40, - 174, - 69, - 178, + 233, + 224, + 39, + 210, + 192, + 217, 27, - 49, - 166, - 136, - 209, + 172, + 147, + 223, + 184, + 187, + 228, + 8, + 178, + 5, + 178, + 0, + 236, + 111, + 231, + 207, + 235, + 81, 163, - 103, - 135, - 254, - 99, - 63, - 197, - 35, - 165, - 202, - 177, - 78, - 238, - 106, - 144, - 250, - 69, - 90, - 192, - 129, - 187 + 161, + 77, + 92, + 49, + 147, + 119, + 69 ] 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 6df05ffada..f5f80e8998 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 @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 230, - 203, - 68, - 175, - 166, - 230, - 219, - 247, - 217, - 191, - 121, - 236, - 155, - 53, - 148, - 22, - 12, - 121, - 60, - 22, - 214, - 93, + 7, + 34, + 84, + 104, + 180, + 74, + 207, + 31, + 198, + 255, + 107, + 249, + 25, + 37, + 103, + 202, + 199, + 132, + 141, + 201, + 64, + 172, 26, - 177, - 216, - 135, - 217, - 212, - 93, - 40, - 173, - 94 + 24, + 80, + 182, + 114, + 182, + 189, + 220, + 109, + 211 ] diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 1c0253308e..bd43bb62e1 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -54,10 +54,7 @@ use crate::{ StateWriteExt as _, ValidatorSet, }, - bridge::{ - StateReadExt as _, - StateWriteExt as _, - }, + bridge::StateWriteExt as _, proposal::commitment::generate_rollup_datas_commitment, state_ext::StateReadExt as _, test_utils::{ @@ -361,9 +358,8 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { .unwrap(); app.commit(storage).await; - // ensure deposits are cleared at the end of the block - let deposit_events = app.state.get_deposit_events(&rollup_id).await.unwrap(); - assert_eq!(deposit_events.len(), 0); + // ensure deposit nonces are cleared at the end of the block + crate::bridge::assert_deposit_nonce_cleared(&app.state, &rollup_id).await; let block = app.state.get_sequencer_block_by_height(1).await.unwrap(); let mut deposits = vec![]; diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index e2cd67b1e4..50080519b3 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -772,7 +772,8 @@ async fn app_execute_transaction_bridge_lock_action_ok() { bridge_before_balance + amount ); - let deposits = app.state.get_deposit_events(&rollup_id).await.unwrap(); + let all_deposits = app.state.get_block_deposits().await.unwrap(); + let deposits = all_deposits.get(&rollup_id).unwrap(); assert_eq!(deposits.len(), 1); assert_eq!(deposits[0], expected_deposit); } @@ -1115,7 +1116,8 @@ async fn app_execute_transaction_action_index_correctly_increments() { app.execute_transaction(signed_tx.clone()).await.unwrap(); assert_eq!(app.state.get_account_nonce(alice_address).await.unwrap(), 1); - let deposits = app.state.get_deposit_events(&rollup_id).await.unwrap(); + let all_deposits = app.state.get_block_deposits().await.unwrap(); + let deposits = all_deposits.get(&rollup_id).unwrap(); assert_eq!(deposits.len(), 2); assert_eq!(deposits[0].source_action_index, starting_index_of_action); assert_eq!( diff --git a/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-2.snap b/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-2.snap deleted file mode 100644 index 8c31993c79..0000000000 --- a/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-2.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/assets/state_ext.rs -expression: block_fees_key(&trace_prefixed) ---- -block_fees/ce072174ebc356c6ead681d61ab417ee72fdedd8155eb76478ece374bb04dc1d diff --git a/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-3.snap b/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-3.snap deleted file mode 100644 index a3e273ee76..0000000000 --- a/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged-3.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/assets/state_ext.rs -expression: fee_asset_key(trace_prefixed) ---- -fee_asset/ce072174ebc356c6ead681d61ab417ee72fdedd8155eb76478ece374bb04dc1d diff --git a/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged.snap b/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged.snap deleted file mode 100644 index 3ad210d6a2..0000000000 --- a/crates/astria-sequencer/src/assets/snapshots/astria_sequencer__assets__state_ext__tests__storage_keys_are_unchanged.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/assets/state_ext.rs -expression: asset_storage_key(asset) ---- -asset/be429a02d00837245167a2616674a979a2ac6f9806468b48a975b156ad711320 diff --git a/crates/astria-sequencer/src/assets/state_ext.rs b/crates/astria-sequencer/src/assets/state_ext.rs index 0b281544e5..066352052b 100644 --- a/crates/astria-sequencer/src/assets/state_ext.rs +++ b/crates/astria-sequencer/src/assets/state_ext.rs @@ -24,32 +24,25 @@ use tendermint::abci::{ }; use tracing::instrument; +use crate::storage::{ + nonverifiable_keys::assets::{ + block_fees_key, + extract_asset_from_block_fees_key, + extract_asset_from_fee_asset_key, + fee_asset_key, + BLOCK_FEES_PREFIX, + FEE_ASSET_PREFIX, + }, + verifiable_keys::assets::{ + asset_key, + NATIVE_ASSET_KEY, + }, +}; + /// Newtype wrapper to read and write a denomination trace from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] struct DenominationTrace(String); -const BLOCK_FEES_PREFIX: &str = "block_fees/"; -const FEE_ASSET_PREFIX: &str = "fee_asset/"; -const NATIVE_ASSET_KEY: &[u8] = b"nativeasset"; - -fn asset_storage_key>(asset: TAsset) -> String { - format!("asset/{}", crate::storage_keys::hunks::Asset::from(asset)) -} - -fn block_fees_key>(asset: TAsset) -> String { - format!( - "{BLOCK_FEES_PREFIX}{}", - crate::storage_keys::hunks::Asset::from(asset) - ) -} - -fn fee_asset_key>(asset: TAsset) -> String { - format!( - "{FEE_ASSET_PREFIX}{}", - crate::storage_keys::hunks::Asset::from(asset) - ) -} - /// Creates `abci::Event` of kind `tx.fees` for sequencer fee reporting fn construct_tx_fee_event( asset: &T, @@ -71,7 +64,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_native_asset(&self) -> Result { let Some(bytes) = self - .nonverifiable_get_raw(NATIVE_ASSET_KEY) + .get_raw(NATIVE_ASSET_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw native asset from state")? @@ -92,7 +85,7 @@ pub(crate) trait StateReadExt: StateRead { TAsset: Into + std::fmt::Display + Send, { Ok(self - .get_raw(&asset_storage_key(asset)) + .get_raw(&asset_key(asset)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw asset from state")? @@ -107,7 +100,7 @@ pub(crate) trait StateReadExt: StateRead { asset: asset::IbcPrefixed, ) -> Result> { let Some(bytes) = self - .get_raw(&asset_storage_key(asset)) + .get_raw(&asset_key(asset)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw asset from state")? @@ -127,19 +120,11 @@ pub(crate) trait StateReadExt: StateRead { async fn get_block_fees(&self) -> Result> { let mut fees = Vec::new(); - let mut stream = - std::pin::pin!(self.nonverifiable_prefix_raw(BLOCK_FEES_PREFIX.as_bytes())); + let mut stream = std::pin::pin!(self.nonverifiable_prefix_raw(BLOCK_FEES_PREFIX)); while let Some(Ok((key, value))) = stream.next().await { // if the key isn't of the form `block_fees/{asset_id}`, then we have a bug // in `put_block_fees` - let suffix = key - .strip_prefix(BLOCK_FEES_PREFIX.as_bytes()) - .expect("prefix must always be present"); - let asset = std::str::from_utf8(suffix) - .wrap_err("key suffix was not utf8 encoded; this should not happen")? - .parse::() - .wrap_err("failed to parse storage key suffix as address hunk")? - .get(); + let asset = extract_asset_from_block_fees_key(&key)?; let Ok(bytes): Result<[u8; 16], _> = value.try_into() else { bail!("failed turning raw block fees bytes into u128; not 16 bytes?"); @@ -157,7 +142,7 @@ pub(crate) trait StateReadExt: StateRead { TAsset: Into + std::fmt::Display + Send, { Ok(self - .nonverifiable_get_raw(fee_asset_key(asset).as_bytes()) + .nonverifiable_get_raw(&fee_asset_key(asset)) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw fee asset from state")? @@ -168,19 +153,11 @@ pub(crate) trait StateReadExt: StateRead { async fn get_allowed_fee_assets(&self) -> Result> { let mut assets = Vec::new(); - let mut stream = std::pin::pin!(self.nonverifiable_prefix_raw(FEE_ASSET_PREFIX.as_bytes())); + let mut stream = std::pin::pin!(self.nonverifiable_prefix_raw(FEE_ASSET_PREFIX)); while let Some(Ok((key, _))) = stream.next().await { // if the key isn't of the form `fee_asset/{asset_id}`, then we have a bug // in `put_allowed_fee_asset` - let suffix = key - .strip_prefix(FEE_ASSET_PREFIX.as_bytes()) - .expect("prefix must always be present"); - let asset = std::str::from_utf8(suffix) - .wrap_err("key suffix was not utf8 encoded; this should not happen")? - .parse::() - .wrap_err("failed to parse storage key suffix as address hunk")? - .get(); - assets.push(asset); + assets.push(extract_asset_from_fee_asset_key(&key)?); } Ok(assets) @@ -193,14 +170,14 @@ impl StateReadExt for T {} pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_native_asset(&mut self, asset: &asset::TracePrefixed) { - self.nonverifiable_put_raw(NATIVE_ASSET_KEY.to_vec(), asset.to_string().into_bytes()); + self.put_raw(NATIVE_ASSET_KEY.to_string(), asset.to_string().into_bytes()); } #[instrument(skip_all)] fn put_ibc_asset(&mut self, asset: &asset::TracePrefixed) -> Result<()> { let bytes = borsh::to_vec(&DenominationTrace(asset.to_string())) .wrap_err("failed to serialize asset")?; - self.put_raw(asset_storage_key(asset), bytes); + self.put_raw(asset_key(asset), bytes); Ok(()) } @@ -219,7 +196,7 @@ pub(crate) trait StateWriteExt: StateWrite { let block_fees_key = block_fees_key(asset); let current_amount = self - .nonverifiable_get_raw(block_fees_key.as_bytes()) + .nonverifiable_get_raw(&block_fees_key) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw block fees from state")? @@ -237,7 +214,7 @@ pub(crate) trait StateWriteExt: StateWrite { .checked_add(amount) .ok_or_eyre("block fees overflowed u128")?; - self.nonverifiable_put_raw(block_fees_key.into(), new_amount.to_be_bytes().to_vec()); + self.nonverifiable_put_raw(block_fees_key, new_amount.to_be_bytes().to_vec()); self.record(tx_fee_event); @@ -246,8 +223,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] async fn clear_block_fees(&mut self) { - let mut stream = - std::pin::pin!(self.nonverifiable_prefix_raw(BLOCK_FEES_PREFIX.as_bytes())); + let mut stream = std::pin::pin!(self.nonverifiable_prefix_raw(BLOCK_FEES_PREFIX)); while let Some(Ok((key, _))) = stream.next().await { self.nonverifiable_delete(key); } @@ -258,7 +234,7 @@ pub(crate) trait StateWriteExt: StateWrite { where TAsset: Into + std::fmt::Display, { - self.nonverifiable_delete(fee_asset_key(asset).into()); + self.nonverifiable_delete(fee_asset_key(asset)); } #[instrument(skip_all)] @@ -266,7 +242,7 @@ pub(crate) trait StateWriteExt: StateWrite { where TAsset: Into + std::fmt::Display + Send, { - self.nonverifiable_put_raw(fee_asset_key(asset).into(), vec![]); + self.nonverifiable_put_raw(fee_asset_key(asset), vec![]); } } @@ -279,13 +255,7 @@ mod tests { use astria_core::primitive::v1::asset; use cnidarium::StateDelta; - use super::{ - asset_storage_key, - block_fees_key, - fee_asset_key, - StateReadExt as _, - StateWriteExt as _, - }; + use super::*; fn asset() -> asset::Denom { "asset".parse().unwrap() @@ -630,31 +600,4 @@ mod tests { "delete for allowed fee asset did not behave as expected" ); } - - #[test] - fn storage_keys_are_unchanged() { - let asset = "an/asset/with/a/prefix" - .parse::() - .unwrap(); - assert_eq!( - asset_storage_key(&asset), - asset_storage_key(asset.to_ibc_prefixed()), - ); - insta::assert_snapshot!(asset_storage_key(asset)); - - let trace_prefixed = "a/denom/with/a/prefix" - .parse::() - .unwrap(); - assert_eq!( - block_fees_key(&trace_prefixed), - block_fees_key(trace_prefixed.to_ibc_prefixed()), - ); - insta::assert_snapshot!(block_fees_key(&trace_prefixed)); - - assert_eq!( - fee_asset_key(&trace_prefixed), - fee_asset_key(trace_prefixed.to_ibc_prefixed()), - ); - insta::assert_snapshot!(fee_asset_key(trace_prefixed)); - } } diff --git a/crates/astria-sequencer/src/authority/state_ext.rs b/crates/astria-sequencer/src/authority/state_ext.rs index 3196cd212e..4c525fe16c 100644 --- a/crates/astria-sequencer/src/authority/state_ext.rs +++ b/crates/astria-sequencer/src/authority/state_ext.rs @@ -21,16 +21,21 @@ use cnidarium::{ use tracing::instrument; use super::ValidatorSet; -use crate::accounts::AddressBytes; +use crate::{ + accounts::AddressBytes, + storage::{ + nonverifiable_keys::authority::VALIDATOR_UPDATES_KEY, + verifiable_keys::authority::{ + SUDO_STORAGE_KEY, + VALIDATOR_SET_STORAGE_KEY, + }, + }, +}; /// Newtype wrapper to read and write an address from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] struct SudoAddress([u8; ADDRESS_LEN]); -const SUDO_STORAGE_KEY: &str = "sudo"; -const VALIDATOR_SET_STORAGE_KEY: &str = "valset"; -const VALIDATOR_UPDATES_KEY: &[u8] = b"valupdates"; - #[async_trait] pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] diff --git a/crates/astria-sequencer/src/bridge/mod.rs b/crates/astria-sequencer/src/bridge/mod.rs index f322762bc4..9719352672 100644 --- a/crates/astria-sequencer/src/bridge/mod.rs +++ b/crates/astria-sequencer/src/bridge/mod.rs @@ -7,6 +7,8 @@ pub(crate) mod query; mod state_ext; pub(crate) use bridge_lock_action::get_deposit_byte_len; +#[cfg(test)] +pub(crate) use state_ext::assert_deposit_nonce_cleared; pub(crate) use state_ext::{ StateReadExt, StateWriteExt, diff --git a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-2.snap b/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-2.snap deleted file mode 100644 index fc67103b2e..0000000000 --- a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-2.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/bridge/state_ext.rs -expression: asset_id_storage_key(&address) ---- -bridgeacc/1c0c490f1b5528d8173c5de46d131160e4b2c0c3/assetid diff --git a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-3.snap b/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-3.snap deleted file mode 100644 index 00400c037c..0000000000 --- a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-3.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/bridge/state_ext.rs -expression: bridge_account_sudo_address_storage_key(&address) ---- -bsudo/1c0c490f1b5528d8173c5de46d131160e4b2c0c3 diff --git a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-4.snap b/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-4.snap deleted file mode 100644 index 27f89f002d..0000000000 --- a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed-4.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/bridge/state_ext.rs -expression: bridge_account_withdrawer_address_storage_key(&address) ---- -bwithdrawer/1c0c490f1b5528d8173c5de46d131160e4b2c0c3 diff --git a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed.snap b/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed.snap deleted file mode 100644 index d531eb4c5e..0000000000 --- a/crates/astria-sequencer/src/bridge/snapshots/astria_sequencer__bridge__state_ext__test__storage_keys_have_not_changed.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/astria-sequencer/src/bridge/state_ext.rs -expression: rollup_id_storage_key(&address) ---- -bridgeacc/1c0c490f1b5528d8173c5de46d131160e4b2c0c3/rollupid diff --git a/crates/astria-sequencer/src/bridge/state_ext.rs b/crates/astria-sequencer/src/bridge/state_ext.rs index 1a3b15c763..76cf9294aa 100644 --- a/crates/astria-sequencer/src/bridge/state_ext.rs +++ b/crates/astria-sequencer/src/bridge/state_ext.rs @@ -34,16 +34,35 @@ use cnidarium::{ StateWrite, }; use futures::StreamExt as _; -use hex::ToHex as _; use prost::Message as _; use tracing::{ debug, + error, instrument, }; use crate::{ accounts::AddressBytes, address, + storage::{ + nonverifiable_keys::bridge::{ + deposit_key, + deposit_key_prefix, + deposit_nonce_key, + last_transaction_id_for_bridge_account_key, + DEPOSIT_PREFIX, + }, + verifiable_keys::bridge::{ + asset_id_key, + bridge_account_sudo_address_key, + bridge_account_withdrawal_event_key, + bridge_account_withdrawer_address_key, + rollup_id_key, + BRIDGE_LOCK_BYTE_COST_MULTIPLIER_KEY, + BRIDGE_SUDO_CHANGE_FEE_KEY, + INIT_BRIDGE_ACCOUNT_BASE_FEE_KEY, + }, + }, }; /// Newtype wrapper to read and write a u128 from rocksdb. @@ -62,111 +81,6 @@ struct AssetId([u8; 32]); #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Fee(u128); -const BRIDGE_ACCOUNT_PREFIX: &str = "bridgeacc"; -const BRIDGE_ACCOUNT_SUDO_PREFIX: &str = "bsudo"; -const BRIDGE_ACCOUNT_WITHDRAWER_PREFIX: &str = "bwithdrawer"; -const DEPOSIT_PREFIX: &str = "deposit"; -const INIT_BRIDGE_ACCOUNT_BASE_FEE_STORAGE_KEY: &str = "initbridgeaccfee"; -const BRIDGE_LOCK_BYTE_COST_MULTIPLIER_STORAGE_KEY: &str = "bridgelockmultiplier"; -const BRIDGE_SUDO_CHANGE_FEE_STORAGE_KEY: &str = "bridgesudofee"; - -struct BridgeAccountKey<'a, T> { - prefix: &'static str, - address: &'a T, -} - -impl<'a, T> std::fmt::Display for BridgeAccountKey<'a, T> -where - T: AddressBytes, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.prefix)?; - f.write_str("/")?; - for byte in self.address.address_bytes() { - f.write_fmt(format_args!("{byte:02x}"))?; - } - Ok(()) - } -} - -fn rollup_id_storage_key(address: &T) -> String { - format!( - "{}/rollupid", - BridgeAccountKey { - prefix: BRIDGE_ACCOUNT_PREFIX, - address - } - ) -} - -fn asset_id_storage_key(address: &T) -> String { - format!( - "{}/assetid", - BridgeAccountKey { - prefix: BRIDGE_ACCOUNT_PREFIX, - address - } - ) -} - -fn deposit_storage_key_prefix(rollup_id: &RollupId) -> String { - format!("{DEPOSIT_PREFIX}/{}", rollup_id.encode_hex::()) -} - -fn deposit_storage_key(rollup_id: &RollupId, nonce: u32) -> Vec { - format!("{}/{}", deposit_storage_key_prefix(rollup_id), nonce).into() -} - -fn deposit_nonce_storage_key(rollup_id: &RollupId) -> Vec { - format!("depositnonce/{}", rollup_id.encode_hex::()).into() -} - -fn bridge_account_sudo_address_storage_key(address: &T) -> String { - format!( - "{}", - BridgeAccountKey { - prefix: BRIDGE_ACCOUNT_SUDO_PREFIX, - address - } - ) -} - -fn bridge_account_withdrawer_address_storage_key(address: &T) -> String { - format!( - "{}", - BridgeAccountKey { - prefix: BRIDGE_ACCOUNT_WITHDRAWER_PREFIX, - address - } - ) -} - -fn bridge_account_withdrawal_event_storage_key( - address: &T, - withdrawal_event_id: &str, -) -> String { - format!( - "{}/withdrawalevent/{}", - BridgeAccountKey { - prefix: BRIDGE_ACCOUNT_PREFIX, - address - }, - withdrawal_event_id - ) -} - -fn last_transaction_id_for_bridge_account_storage_key(address: &T) -> Vec { - format!( - "{}/lasttx", - BridgeAccountKey { - prefix: BRIDGE_ACCOUNT_PREFIX, - address - } - ) - .as_bytes() - .to_vec() -} - #[async_trait] pub(crate) trait StateReadExt: StateRead + address::StateReadExt { #[instrument(skip_all)] @@ -183,7 +97,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { address: T, ) -> Result> { let Some(rollup_id_bytes) = self - .get_raw(&rollup_id_storage_key(&address)) + .get_raw(&rollup_id_key(&address)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw account rollup ID from state")? @@ -205,7 +119,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { address: T, ) -> Result { let bytes = self - .get_raw(&asset_id_storage_key(&address)) + .get_raw(&asset_id_key(&address)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw asset ID from state")? @@ -221,7 +135,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { bridge_address: T, ) -> Result> { let Some(sudo_address_bytes) = self - .get_raw(&bridge_account_sudo_address_storage_key(&bridge_address)) + .get_raw(&bridge_account_sudo_address_key(&bridge_address)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw bridge account sudo address from state")? @@ -244,9 +158,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { bridge_address: T, ) -> Result> { let Some(withdrawer_address_bytes) = self - .get_raw(&bridge_account_withdrawer_address_storage_key( - &bridge_address, - )) + .get_raw(&bridge_account_withdrawer_address_key(&bridge_address)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw bridge account withdrawer address from state")? @@ -265,60 +177,26 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { Ok(Some(addr)) } - #[instrument(skip_all)] - async fn get_deposit_nonce(&self, rollup_id: &RollupId) -> Result { - let bytes = self - .nonverifiable_get_raw(&deposit_nonce_storage_key(rollup_id)) - .await - .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); - }; - - let Nonce(nonce) = - Nonce(u32::from_be_bytes(bytes.try_into().expect( - "all deposit nonces stored should be 4 bytes; this is a bug", - ))); - Ok(nonce) - } - #[instrument(skip_all)] async fn get_deposit_rollup_ids(&self) -> Result> { - let mut stream = std::pin::pin!(self.nonverifiable_prefix_raw(DEPOSIT_PREFIX.as_bytes())); + let mut stream = std::pin::pin!(self.nonverifiable_prefix_raw(DEPOSIT_PREFIX)); let mut rollup_ids = HashSet::new(); 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).wrap_err("failed to convert deposit key to string")?; - let key_parts = key_str.split('/').collect::>(); - if key_parts.len() != 3 { + // the deposit key is of the form b"deposit/{32 bytes of rollup_id}{4 bytes of nonce}" + let Some(rollup_id_and_nonce) = key.strip_prefix(DEPOSIT_PREFIX) else { + error!("got wrong prefix in prefix stream"); continue; - } - let rollup_id_bytes = - hex::decode(key_parts[1]).wrap_err("invalid rollup ID hex string")?; + }; + let Some(rollup_id_slice) = rollup_id_and_nonce.get(0..32) else { + bail!("failed to get rollup id bytes from deposit key {:?}", key); + }; let rollup_id = - RollupId::try_from_slice(&rollup_id_bytes).wrap_err("invalid rollup ID bytes")?; + RollupId::try_from_slice(rollup_id_slice).wrap_err("invalid rollup ID bytes")?; rollup_ids.insert(rollup_id); } Ok(rollup_ids) } - #[instrument(skip_all)] - async fn get_deposit_events(&self, rollup_id: &RollupId) -> Result> { - let mut stream = std::pin::pin!( - self.nonverifiable_prefix_raw(deposit_storage_key_prefix(rollup_id).as_bytes()) - ); - let mut deposits = Vec::new(); - while let Some(Ok((_, value))) = stream.next().await { - 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) - } - #[instrument(skip_all)] async fn get_block_deposits(&self) -> Result>> { let deposit_rollup_ids = self @@ -327,8 +205,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { .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) + let rollup_deposit_events = get_deposit_events(self, &rollup_id) .await .wrap_err("failed to get deposit events")?; deposit_events.insert(rollup_id, rollup_deposit_events); @@ -339,7 +216,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { #[instrument(skip_all)] async fn get_init_bridge_account_base_fee(&self) -> Result { let bytes = self - .get_raw(INIT_BRIDGE_ACCOUNT_BASE_FEE_STORAGE_KEY) + .get_raw(INIT_BRIDGE_ACCOUNT_BASE_FEE_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw init bridge account base fee from state")? @@ -351,7 +228,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { #[instrument(skip_all)] async fn get_bridge_lock_byte_cost_multiplier(&self) -> Result { let bytes = self - .get_raw(BRIDGE_LOCK_BYTE_COST_MULTIPLIER_STORAGE_KEY) + .get_raw(BRIDGE_LOCK_BYTE_COST_MULTIPLIER_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw bridge lock byte cost multiplier from state")? @@ -363,7 +240,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { #[instrument(skip_all)] async fn get_bridge_sudo_change_base_fee(&self) -> Result { let bytes = self - .get_raw(BRIDGE_SUDO_CHANGE_FEE_STORAGE_KEY) + .get_raw(BRIDGE_SUDO_CHANGE_FEE_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw bridge sudo change fee from state")? @@ -378,7 +255,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { address: &Address, ) -> Result> { let Some(tx_hash_bytes) = self - .nonverifiable_get_raw(&last_transaction_id_for_bridge_account_storage_key(address)) + .nonverifiable_get_raw(&last_transaction_id_for_bridge_account_key(address)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw last transaction hash for bridge account from state")? @@ -400,7 +277,7 @@ impl StateReadExt for T {} pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_bridge_account_rollup_id(&mut self, address: T, rollup_id: &RollupId) { - self.put_raw(rollup_id_storage_key(&address), rollup_id.to_vec()); + self.put_raw(rollup_id_key(&address), rollup_id.to_vec()); } #[instrument(skip_all)] @@ -415,8 +292,8 @@ pub(crate) trait StateWriteExt: StateWrite { { let ibc = asset.into(); self.put_raw( - asset_id_storage_key(&address), - borsh::to_vec(&AssetId(ibc.get())).wrap_err("failed to serialize asset IDs")?, + asset_id_key(&address), + borsh::to_vec(&AssetId(ibc.get())).context("failed to serialize asset IDs")?, ); Ok(()) } @@ -431,7 +308,7 @@ pub(crate) trait StateWriteExt: StateWrite { TSudoAddress: AddressBytes, { self.put_raw( - bridge_account_sudo_address_storage_key(&bridge_address), + bridge_account_sudo_address_key(&bridge_address), sudo_address.address_bytes().to_vec(), ); } @@ -446,7 +323,7 @@ pub(crate) trait StateWriteExt: StateWrite { TWithdrawerAddress: AddressBytes, { self.put_raw( - bridge_account_withdrawer_address_storage_key(&bridge_address), + bridge_account_withdrawer_address_key(&bridge_address), withdrawer_address.address_bytes().to_vec(), ); } @@ -458,7 +335,7 @@ pub(crate) trait StateWriteExt: StateWrite { withdrawal_event_id: &str, block_num: u64, ) -> Result<()> { - let key = bridge_account_withdrawal_event_storage_key(&address, withdrawal_event_id); + let key = bridge_account_withdrawal_event_key(&address, withdrawal_event_id); // Check if the withdrawal ID has already been used, if so return an error. let bytes = self @@ -483,52 +360,30 @@ pub(crate) trait StateWriteExt: StateWrite { Ok(()) } - // the deposit "nonce" for a given rollup ID during a given block. - // this is only used to generate storage keys for each of the deposits within a block, - // and is reset to 0 at the beginning of each block. - #[instrument(skip_all)] - fn put_deposit_nonce(&mut self, rollup_id: &RollupId, nonce: u32) { - self.nonverifiable_put_raw( - deposit_nonce_storage_key(rollup_id), - nonce.to_be_bytes().to_vec(), - ); - } - // allow: false positive due to proc macro; fixed with rust/clippy 1.81 #[allow(clippy::blocks_in_conditions)] #[instrument(skip_all, err)] async fn put_deposit_event(&mut self, deposit: Deposit) -> Result<()> { - let nonce = self.get_deposit_nonce(&deposit.rollup_id).await?; - self.put_deposit_nonce( + let nonce = get_deposit_nonce(self, &deposit.rollup_id).await?; + put_deposit_nonce( + self, &deposit.rollup_id, nonce.checked_add(1).ok_or_eyre("nonce overflowed")?, ); - let key = deposit_storage_key(&deposit.rollup_id, nonce); + let key = deposit_key(&deposit.rollup_id, nonce); self.nonverifiable_put_raw(key, deposit.into_raw().encode_to_vec()); Ok(()) } - // clears the deposit nonce and all deposits for for a given rollup ID. #[instrument(skip_all)] - async fn clear_deposit_info(&mut self, rollup_id: &RollupId) { - self.nonverifiable_delete(deposit_nonce_storage_key(rollup_id)); - let mut stream = std::pin::pin!( - self.nonverifiable_prefix_raw(deposit_storage_key_prefix(rollup_id).as_bytes()) - ); - while let Some(Ok((key, _))) = stream.next().await { - self.nonverifiable_delete(key); - } - } - - #[instrument(skip_all)] - async fn clear_block_deposits(&mut self) -> Result<()> { + async fn clear_block_deposit_nonces(&mut self) -> Result<()> { let deposit_rollup_ids = self .get_deposit_rollup_ids() .await .wrap_err("failed to get deposit rollup ids")?; for rollup_id in deposit_rollup_ids { - self.clear_deposit_info(&rollup_id).await; + self.nonverifiable_delete(deposit_nonce_key(&rollup_id)); } Ok(()) } @@ -536,7 +391,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_init_bridge_account_base_fee(&mut self, fee: u128) { self.put_raw( - INIT_BRIDGE_ACCOUNT_BASE_FEE_STORAGE_KEY.to_string(), + INIT_BRIDGE_ACCOUNT_BASE_FEE_KEY.to_string(), borsh::to_vec(&Fee(fee)).expect("failed to serialize fee"), ); } @@ -544,7 +399,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_bridge_lock_byte_cost_multiplier(&mut self, fee: u128) { self.put_raw( - BRIDGE_LOCK_BYTE_COST_MULTIPLIER_STORAGE_KEY.to_string(), + BRIDGE_LOCK_BYTE_COST_MULTIPLIER_KEY.to_string(), borsh::to_vec(&Fee(fee)).expect("failed to serialize fee"), ); } @@ -552,7 +407,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_bridge_sudo_change_base_fee(&mut self, fee: u128) { self.put_raw( - BRIDGE_SUDO_CHANGE_FEE_STORAGE_KEY.to_string(), + BRIDGE_SUDO_CHANGE_FEE_KEY.to_string(), borsh::to_vec(&Fee(fee)).expect("failed to serialize fee"), ); } @@ -564,7 +419,7 @@ pub(crate) trait StateWriteExt: StateWrite { tx_id: &TransactionId, ) { self.nonverifiable_put_raw( - last_transaction_id_for_bridge_account_storage_key(&address), + last_transaction_id_for_bridge_account_key(&address), tx_id.get().to_vec(), ); } @@ -572,28 +427,76 @@ pub(crate) trait StateWriteExt: StateWrite { impl StateWriteExt for T {} +#[instrument(skip_all)] +async fn get_deposit_events( + state: &T, + rollup_id: &RollupId, +) -> Result> { + let mut stream = std::pin::pin!(state.nonverifiable_prefix_raw(&deposit_key_prefix(rollup_id))); + let mut deposits = Vec::new(); + while let Some(Ok((_, value))) = stream.next().await { + 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) +} + +#[instrument(skip_all)] +async fn get_deposit_nonce(state: &T, rollup_id: &RollupId) -> Result { + let bytes = state + .nonverifiable_get_raw(&deposit_nonce_key(rollup_id)) + .await + .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); + }; + + let Nonce(nonce) = + Nonce(u32::from_be_bytes(bytes.try_into().expect( + "all deposit nonces stored should be 4 bytes; this is a bug", + ))); + Ok(nonce) +} + +// the deposit "nonce" for a given rollup ID during a given block. +// this is only used to generate storage keys for each of the deposits within a block, +// and is reset to 0 at the beginning of each block. +#[instrument(skip_all)] +fn put_deposit_nonce(state: &mut T, rollup_id: &RollupId, nonce: u32) { + state.nonverifiable_put_raw(deposit_nonce_key(rollup_id), nonce.to_be_bytes().to_vec()); +} + +#[cfg(test)] +pub(crate) async fn assert_deposit_nonce_cleared( + state: &T, + rollup_id: &RollupId, +) { + assert!( + state + .nonverifiable_get_raw(&deposit_nonce_key(rollup_id)) + .await + .expect("failed reading deposit nonce") + .is_none(), + "deposit nonce not cleared from state" + ); +} + #[cfg(test)] mod test { use astria_core::{ primitive::v1::{ asset, - Address, RollupId, TransactionId, }, sequencerblock::v1alpha1::block::Deposit, }; use cnidarium::StateDelta; - use insta::assert_snapshot; - - use super::{ - asset_id_storage_key, - bridge_account_sudo_address_storage_key, - bridge_account_withdrawer_address_storage_key, - rollup_id_storage_key, - StateReadExt as _, - StateWriteExt as _, - }; + + use super::*; use crate::test_utils::astria_address; fn asset_0() -> asset::Denom { @@ -768,8 +671,7 @@ mod test { // uninitialized ok assert_eq!( - state - .get_deposit_nonce(&rollup_id) + get_deposit_nonce(&state, &rollup_id) .await .expect("call to get deposit nonce should not fail on uninitialized rollup ids"), 0u32, @@ -787,10 +689,9 @@ mod test { let mut nonce = 1u32; // can write - state.put_deposit_nonce(&rollup_id, nonce); + super::put_deposit_nonce(&mut state, &rollup_id, nonce); assert_eq!( - state - .get_deposit_nonce(&rollup_id) + get_deposit_nonce(&state, &rollup_id) .await .expect("a rollup id nonce was written and must exist inside the database"), nonce, @@ -799,10 +700,9 @@ mod test { // can update nonce = 2u32; - state.put_deposit_nonce(&rollup_id, nonce); + super::put_deposit_nonce(&mut state, &rollup_id, nonce); assert_eq!( - state - .get_deposit_nonce(&rollup_id) + get_deposit_nonce(&state, &rollup_id) .await .expect("a rollup id nonce was written and must exist inside the database"), nonce, @@ -812,18 +712,16 @@ mod test { // writing to different account is ok let rollup_id_1 = RollupId::new([3u8; 32]); let nonce_1 = 3u32; - state.put_deposit_nonce(&rollup_id_1, nonce_1); + super::put_deposit_nonce(&mut state, &rollup_id_1, nonce_1); assert_eq!( - state - .get_deposit_nonce(&rollup_id_1) + get_deposit_nonce(&state, &rollup_id_1) .await .expect("a rollup id nonce was written and must exist inside the database"), nonce_1, "additional stored nonce did not match expected" ); assert_eq!( - state - .get_deposit_nonce(&rollup_id) + get_deposit_nonce(&state, &rollup_id) .await .expect("a rollup id nonce was written and must exist inside the database"), nonce, @@ -841,8 +739,7 @@ mod test { // no events ok assert_eq!( - state - .get_deposit_events(&rollup_id) + super::get_deposit_events(&state, &rollup_id) .await .expect("call for rollup id with no deposit events should not fail"), vec![], @@ -881,8 +778,7 @@ mod test { .await .expect("writing deposit events should be ok"); assert_eq!( - state - .get_deposit_events(&rollup_id) + super::get_deposit_events(&state, &rollup_id) .await .expect("deposit info was written to the database and must exist"), deposits, @@ -890,8 +786,7 @@ mod test { ); // nonce is correct assert_eq!( - state - .get_deposit_nonce(&rollup_id) + super::get_deposit_nonce(&state, &rollup_id) .await .expect("calls to get nonce should not fail"), 1u32, @@ -909,8 +804,7 @@ mod test { .put_deposit_event(deposit.clone()) .await .expect("writing deposit events should be ok"); - let mut returned_deposits = state - .get_deposit_events(&rollup_id) + let mut returned_deposits = super::get_deposit_events(&state, &rollup_id) .await .expect("deposit info was written to the database and must exist"); returned_deposits.sort_by_key(|d| d.amount); @@ -921,8 +815,7 @@ mod test { ); // nonce is correct assert_eq!( - state - .get_deposit_nonce(&rollup_id) + super::get_deposit_nonce(&state, &rollup_id) .await .expect("calls to get nonce should not fail"), 2u32, @@ -942,16 +835,14 @@ mod test { .await .expect("writing deposit events should be ok"); assert_eq!( - state - .get_deposit_events(&rollup_id_1) + super::get_deposit_events(&state, &rollup_id_1) .await .expect("deposit info was written to the database and must exist"), deposits_1, "stored deposits do not match what was expected" ); // verify original still ok - returned_deposits = state - .get_deposit_events(&rollup_id) + returned_deposits = super::get_deposit_events(&state, &rollup_id) .await .expect("deposit info was written to the database and must exist"); returned_deposits.sort_by_key(|d| d.amount); @@ -1022,158 +913,6 @@ mod test { ); } - #[tokio::test] - async fn clear_deposit_info_uninitialized_ok() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let rollup_id = RollupId::new([1u8; 32]); - // uninitialized delete ok - state.clear_deposit_info(&rollup_id).await; - } - - #[tokio::test] - async fn clear_deposit_info() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let rollup_id = RollupId::new([1u8; 32]); - let bridge_address = astria_address(&[42u8; 20]); - let amount = 10u128; - let asset = asset_0(); - let destination_chain_address = "0xdeadbeef"; - - let deposit = Deposit { - bridge_address, - rollup_id, - amount, - asset: asset.clone(), - destination_chain_address: destination_chain_address.to_string(), - source_transaction_id: TransactionId::new([0; 32]), - source_action_index: 0, - }; - - let deposits = vec![deposit.clone()]; - - // can write - state - .put_deposit_event(deposit) - .await - .expect("writing deposit events should be ok"); - assert_eq!( - state - .get_deposit_events(&rollup_id) - .await - .expect("deposit info was written to the database and must exist"), - deposits, - "stored deposits do not match what was expected" - ); - - // can delete - state.clear_deposit_info(&rollup_id).await; - assert_eq!( - state - .get_deposit_events(&rollup_id) - .await - .expect("deposit should return empty when none exists"), - vec![], - "deposits were cleared and should return empty vector" - ); - assert_eq!( - state - .get_deposit_nonce(&rollup_id) - .await - .expect("calls to get nonce should not fail"), - 0u32, - "nonce should have been deleted also" - ); - } - - #[tokio::test] - async fn clear_deposit_info_multiple_accounts() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let rollup_id = RollupId::new([1u8; 32]); - let bridge_address = astria_address(&[42u8; 20]); - let amount = 10u128; - let asset = asset_0(); - let destination_chain_address = "0xdeadbeef"; - let mut deposit = Deposit { - bridge_address, - rollup_id, - amount, - asset: asset.clone(), - destination_chain_address: destination_chain_address.to_string(), - source_transaction_id: TransactionId::new([0; 32]), - source_action_index: 0, - }; - - // write to first - state - .put_deposit_event(deposit) - .await - .expect("writing deposit events should be ok"); - - // write to second - let rollup_id_1 = RollupId::new([2u8; 32]); - deposit = Deposit { - bridge_address, - rollup_id: rollup_id_1, - amount, - asset: asset.clone(), - destination_chain_address: destination_chain_address.to_string(), - source_transaction_id: TransactionId::new([0; 32]), - source_action_index: 1, - }; - let deposits_1 = vec![deposit.clone()]; - - state - .put_deposit_event(deposit) - .await - .expect("writing deposit events for rollup 2 should be ok"); - - // delete first rollup's info - state.clear_deposit_info(&rollup_id).await; - assert_eq!( - state - .get_deposit_events(&rollup_id) - .await - .expect("deposit should return empty when none exists"), - vec![], - "deposits were cleared and should return empty vector" - ); - assert_eq!( - state - .get_deposit_nonce(&rollup_id) - .await - .expect("calls to get nonce should not fail"), - 0u32, - "nonce should have been deleted also" - ); - - // second rollup's info should be intact - assert_eq!( - state - .get_deposit_events(&rollup_id_1) - .await - .expect("deposit should return empty when none exists"), - deposits_1, - "deposits were written to the database and should exist" - ); - assert_eq!( - state - .get_deposit_nonce(&rollup_id_1) - .await - .expect("calls to get nonce should not fail"), - 1u32, - "nonce was written to the database and should exist" - ); - } - #[tokio::test] async fn clear_block_info_uninitialized_ok() { let storage = cnidarium::TempStorage::new().await.unwrap(); @@ -1182,7 +921,7 @@ mod test { // uninitialized delete ok state - .clear_block_deposits() + .clear_block_deposit_nonces() .await .expect("calls to clear block deposit should succeed"); } @@ -1198,7 +937,7 @@ mod test { let amount = 10u128; let asset = asset_0(); let destination_chain_address = "0xdeadbeef"; - let mut deposit = Deposit { + let deposit = Deposit { bridge_address, rollup_id, amount, @@ -1216,65 +955,50 @@ mod test { // write to second let rollup_id_1 = RollupId::new([2u8; 32]); - deposit = Deposit { + let deposit_1 = Deposit { rollup_id: rollup_id_1, source_action_index: 1, - ..deposit + ..deposit.clone() }; state - .put_deposit_event(deposit.clone()) + .put_deposit_event(deposit_1.clone()) .await .expect("writing deposit events for rollup 2 should be ok"); - // delete all info + // delete nonce info state - .clear_block_deposits() + .clear_block_deposit_nonces() .await .expect("clearing deposits call should not fail"); + // check that nonces were deleted assert_eq!( - state - .get_deposit_events(&rollup_id) + get_deposit_nonce(&state, &rollup_id) .await .expect("deposit should return empty when none exists"), - vec![], - "deposits were cleared and should return empty vector" + 0u32, + "nonce should have been deleted also" ); - // check that all info was deleted assert_eq!( - state - .get_deposit_events(&rollup_id_1) + get_deposit_nonce(&state, &rollup_id_1) .await .expect("deposit should return empty when none exists"), - vec![], - "deposits were cleared and should return empty vector" + 0u32, + "nonce should have been deleted also" ); + // check that deposits were not deleted assert_eq!( - state - .get_deposit_nonce(&rollup_id) + super::get_deposit_events(&state, &rollup_id) .await .expect("deposit should return empty when none exists"), - 0u32, - "nonce should have been deleted also" + vec![deposit], + "deposits were cleared and should return empty vector" ); assert_eq!( - state - .get_deposit_nonce(&rollup_id_1) + super::get_deposit_events(&state, &rollup_id_1) .await .expect("deposit should return empty when none exists"), - 0u32, - "nonce should have been deleted also" + vec![deposit_1], + "deposits were cleared and should return empty vector" ); } - - #[test] - fn storage_keys_have_not_changed() { - let address: Address = "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - .parse() - .unwrap(); - - assert_snapshot!(rollup_id_storage_key(&address)); - assert_snapshot!(asset_id_storage_key(&address)); - assert_snapshot!(bridge_account_sudo_address_storage_key(&address)); - assert_snapshot!(bridge_account_withdrawer_address_storage_key(&address)); - } } diff --git a/crates/astria-sequencer/src/ibc/state_ext.rs b/crates/astria-sequencer/src/ibc/state_ext.rs index 09b3cb3db5..05f3bb95ea 100644 --- a/crates/astria-sequencer/src/ibc/state_ext.rs +++ b/crates/astria-sequencer/src/ibc/state_ext.rs @@ -26,7 +26,15 @@ use tracing::{ instrument, }; -use crate::accounts::AddressBytes; +use crate::{ + accounts::AddressBytes, + storage::verifiable_keys::ibc::{ + channel_balance_key, + ibc_relayer_key, + IBC_SUDO_KEY, + ICS20_WITHDRAWAL_BASE_FEE_KEY, + }, +}; /// Newtype wrapper to read and write a u128 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] @@ -40,36 +48,6 @@ struct SudoAddress([u8; ADDRESS_LEN]); #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Fee(u128); -const IBC_SUDO_STORAGE_KEY: &str = "ibcsudo"; -const ICS20_WITHDRAWAL_BASE_FEE_STORAGE_KEY: &str = "ics20withdrawalfee"; - -struct IbcRelayerKey<'a, T>(&'a T); - -impl<'a, T: AddressBytes> std::fmt::Display for IbcRelayerKey<'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("ibc-relayer")?; - f.write_str("/")?; - for byte in self.0.address_bytes() { - f.write_fmt(format_args!("{byte:02x}"))?; - } - Ok(()) - } -} - -fn channel_balance_storage_key>( - channel: &ChannelId, - asset: TAsset, -) -> String { - format!( - "ibc-data/{channel}/balance/{}", - crate::storage_keys::hunks::Asset::from(asset), - ) -} - -fn ibc_relayer_key(address: &T) -> String { - IbcRelayerKey(address).to_string() -} - #[async_trait] pub(crate) trait StateReadExt: StateRead { // allow: false positive due to proc macro; fixed with rust/clippy 1.81 @@ -84,7 +62,7 @@ pub(crate) trait StateReadExt: StateRead { TAsset: Into + std::fmt::Display + Send, { let Some(bytes) = self - .get_raw(&channel_balance_storage_key(channel, asset)) + .get_raw(&channel_balance_key(channel, asset)) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading ibc channel balance from state")? @@ -100,7 +78,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_ibc_sudo_address(&self) -> Result<[u8; ADDRESS_LEN]> { let Some(bytes) = self - .get_raw(IBC_SUDO_STORAGE_KEY) + .get_raw(IBC_SUDO_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw ibc sudo key from state")? @@ -126,7 +104,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_ics20_withdrawal_base_fee(&self) -> Result { let Some(bytes) = self - .get_raw(ICS20_WITHDRAWAL_BASE_FEE_STORAGE_KEY) + .get_raw(ICS20_WITHDRAWAL_BASE_FEE_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading ics20 withdrawal fee from state")? @@ -180,15 +158,15 @@ pub(crate) trait StateWriteExt: StateWrite { where TAsset: Into + std::fmt::Display + Send, { - let bytes = borsh::to_vec(&Balance(balance)).wrap_err("failed to serialize balance")?; - self.put_raw(channel_balance_storage_key(channel, asset), bytes); + let bytes = borsh::to_vec(&Balance(balance)).context("failed to serialize balance")?; + self.put_raw(channel_balance_key(channel, asset), bytes); Ok(()) } #[instrument(skip_all)] fn put_ibc_sudo_address(&mut self, address: T) -> Result<()> { self.put_raw( - IBC_SUDO_STORAGE_KEY.to_string(), + IBC_SUDO_KEY.to_string(), borsh::to_vec(&SudoAddress(address.address_bytes())) .wrap_err("failed to convert sudo address to vec")?, ); @@ -208,8 +186,8 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] 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)).wrap_err("failed to serialize fee")?, + ICS20_WITHDRAWAL_BASE_FEE_KEY.to_string(), + borsh::to_vec(&Fee(fee)).context("failed to serialize fee")?, ); Ok(()) } @@ -233,7 +211,7 @@ mod tests { }; use crate::{ address::StateWriteExt, - ibc::state_ext::channel_balance_storage_key, + ibc::state_ext::channel_balance_key, test_utils::{ astria_address, ASTRIA_PREFIX, @@ -528,9 +506,9 @@ mod tests { .parse::() .unwrap(); assert_eq!( - channel_balance_storage_key(&channel, &asset), - channel_balance_storage_key(&channel, asset.to_ibc_prefixed()), + channel_balance_key(&channel, &asset), + channel_balance_key(&channel, asset.to_ibc_prefixed()), ); - assert_snapshot!(channel_balance_storage_key(&channel, &asset)); + assert_snapshot!(channel_balance_key(&channel, &asset)); } } diff --git a/crates/astria-sequencer/src/lib.rs b/crates/astria-sequencer/src/lib.rs index d35e9e0782..6817464cd5 100644 --- a/crates/astria-sequencer/src/lib.rs +++ b/crates/astria-sequencer/src/lib.rs @@ -20,7 +20,7 @@ pub(crate) mod sequence; mod sequencer; pub(crate) mod service; pub(crate) mod state_ext; -pub(crate) mod storage_keys; +pub(crate) mod storage; #[cfg(any(test, feature = "benchmark"))] pub(crate) mod test_utils; pub(crate) mod transaction; diff --git a/crates/astria-sequencer/src/sequence/state_ext.rs b/crates/astria-sequencer/src/sequence/state_ext.rs index 9ea6ec1dba..98c09590e8 100644 --- a/crates/astria-sequencer/src/sequence/state_ext.rs +++ b/crates/astria-sequencer/src/sequence/state_ext.rs @@ -17,8 +17,10 @@ use cnidarium::{ }; use tracing::instrument; -const SEQUENCE_ACTION_BASE_FEE_STORAGE_KEY: &str = "seqbasefee"; -const SEQUENCE_ACTION_BYTE_COST_MULTIPLIER_STORAGE_KEY: &str = "seqmultiplier"; +use crate::storage::verifiable_keys::sequence::{ + SEQUENCE_ACTION_BASE_FEE_KEY, + SEQUENCE_ACTION_BYTE_COST_MULTIPLIER_KEY, +}; /// Newtype wrapper to read and write a u128 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] @@ -29,7 +31,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_sequence_action_base_fee(&self) -> Result { let bytes = self - .get_raw(SEQUENCE_ACTION_BASE_FEE_STORAGE_KEY) + .get_raw(SEQUENCE_ACTION_BASE_FEE_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw sequence action base fee from state")? @@ -41,7 +43,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_sequence_action_byte_cost_multiplier(&self) -> Result { let bytes = self - .get_raw(SEQUENCE_ACTION_BYTE_COST_MULTIPLIER_STORAGE_KEY) + .get_raw(SEQUENCE_ACTION_BYTE_COST_MULTIPLIER_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed reading raw sequence action byte cost multiplier from state")? @@ -58,7 +60,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_sequence_action_base_fee(&mut self, fee: u128) { self.put_raw( - SEQUENCE_ACTION_BASE_FEE_STORAGE_KEY.to_string(), + SEQUENCE_ACTION_BASE_FEE_KEY.to_string(), borsh::to_vec(&Fee(fee)).expect("failed to serialize fee"), ); } @@ -66,7 +68,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_sequence_action_byte_cost_multiplier(&mut self, fee: u128) { self.put_raw( - SEQUENCE_ACTION_BYTE_COST_MULTIPLIER_STORAGE_KEY.to_string(), + SEQUENCE_ACTION_BYTE_COST_MULTIPLIER_KEY.to_string(), borsh::to_vec(&Fee(fee)).expect("failed to serialize fee"), ); } diff --git a/crates/astria-sequencer/src/state_ext.rs b/crates/astria-sequencer/src/state_ext.rs index 1e5f4c69da..a6ff2d965a 100644 --- a/crates/astria-sequencer/src/state_ext.rs +++ b/crates/astria-sequencer/src/state_ext.rs @@ -15,18 +15,22 @@ use cnidarium::{ use tendermint::Time; use tracing::instrument; -const REVISION_NUMBER_KEY: &str = "revision_number"; - -fn storage_version_by_height_key(height: u64) -> Vec { - format!("storage_version/{height}").into() -} +use crate::storage::{ + nonverifiable_keys::app::storage_version_by_height_key, + verifiable_keys::app::{ + BLOCK_HEIGHT_KEY, + BLOCK_TIMESTAMP_KEY, + CHAIN_ID_KEY, + REVISION_NUMBER_KEY, + }, +}; #[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") + .get_raw(CHAIN_ID_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw chain_id from state")? @@ -64,7 +68,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_block_height(&self) -> Result { let Some(bytes) = self - .get_raw("block_height") + .get_raw(BLOCK_HEIGHT_KEY) .await .map_err(anyhow_to_eyre) .wrap_err("failed to read raw block_height from state")? @@ -80,7 +84,7 @@ pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] async fn get_block_timestamp(&self) -> Result