Skip to content

Commit

Permalink
chore(sequencer)!: update storage keys locations and values (ENG-898) (
Browse files Browse the repository at this point in the history
…#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 `<COMPONENT>/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.
  • Loading branch information
Fraser999 authored Oct 9, 2024
1 parent 099b237 commit bb6c435
Show file tree
Hide file tree
Showing 95 changed files with 1,277 additions and 773 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 28 additions & 4 deletions crates/astria-core/src/primitive/v1/asset/denom.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
borrow::Cow,
collections::VecDeque,
str::FromStr,
};
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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,
})
Expand Down
4 changes: 2 additions & 2 deletions crates/astria-core/src/primitive/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{

use base64::{
display::Base64Display,
prelude::BASE64_STANDARD,
prelude::BASE64_URL_SAFE,
};
use bytes::Bytes;
use sha2::{
Expand Down Expand Up @@ -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)
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/astria-sequencer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
7 changes: 2 additions & 5 deletions crates/astria-sequencer/src/accounts/action.rs
Original file line number Diff line number Diff line change
@@ -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 _,
Expand Down Expand Up @@ -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::<Transfer, _>(&action.fee_asset, fee)
.await
.wrap_err("failed to add to block fees")?;

Expand Down

This file was deleted.

This file was deleted.

106 changes: 25 additions & 81 deletions crates/astria-sequencer/src/accounts/state_ext.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
borrow::Cow,
fmt::Display,
pin::Pin,
task::{
Expand Down Expand Up @@ -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<T: AddressBytes>(address: &T) -> String {
format!("{}/nonce", StorageKey(address))
}

pin_project! {
/// A stream of IBC prefixed assets for a given account.
pub(crate) struct AccountAssetsStream<St> {
Expand Down Expand Up @@ -141,23 +116,14 @@ where
}
}

fn extract_asset_from_key(s: &str) -> Result<asset::IbcPrefixed> {
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::<crate::storage_keys::hunks::Asset>()
.context("failed to parse storage key suffix as address hunk")?
.get())
}

#[async_trait]
pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt {
#[instrument(skip_all)]
fn account_asset_keys<T: AddressBytes>(
&self,
address: &T,
) -> AccountAssetsStream<Self::PrefixKeysStream> {
let prefix = format!("{}/balance/", StorageKey(address));
let prefix = keys::balance_prefix(address);
AccountAssetsStream {
underlying: self.prefix_keys(&prefix),
}
Expand All @@ -168,7 +134,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt {
&self,
address: &T,
) -> AccountAssetBalancesStream<Self::PrefixRawStream> {
let prefix = format!("{}/balance/", StorageKey(address));
let prefix = keys::balance_prefix(address);
AccountAssetBalancesStream {
underlying: self.prefix_raw(&prefix),
}
Expand All @@ -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<Cow<'a, asset::IbcPrefixed>>,
{
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")?
Expand All @@ -201,7 +167,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt {
#[instrument(skip_all)]
async fn get_account_nonce<T: AddressBytes>(&self, address: &T) -> Result<u32> {
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")?;
Expand All @@ -217,7 +183,7 @@ pub(crate) trait StateReadExt: StateRead + crate::assets::StateReadExt {
#[instrument(skip_all)]
async fn get_transfer_base_fee(&self) -> Result<u128> {
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")?;
Expand All @@ -244,12 +210,12 @@ pub(crate) trait StateWriteExt: StateWrite {
where
TAddress: AddressBytes,
TAsset: Display,
asset::IbcPrefixed: From<&'a TAsset> + Send,
&'a TAsset: Into<Cow<'a, asset::IbcPrefixed>>,
{
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(())
}

Expand All @@ -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(())
}

Expand All @@ -272,7 +238,7 @@ pub(crate) trait StateWriteExt: StateWrite {
where
TAddress: AddressBytes,
TAsset: Sync + Display,
asset::IbcPrefixed: From<&'a TAsset> + Send,
&'a TAsset: Into<Cow<'a, asset::IbcPrefixed>>,
{
let balance = self
.get_account_balance(address, asset)
Expand All @@ -299,7 +265,7 @@ pub(crate) trait StateWriteExt: StateWrite {
where
TAddress: AddressBytes,
TAsset: Sync + Display,
asset::IbcPrefixed: From<&'a TAsset> + Send,
&'a TAsset: Into<Cow<'a, asset::IbcPrefixed>>,
{
let balance = self
.get_account_balance(address, asset)
Expand All @@ -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(())
}
}
Expand All @@ -330,17 +296,10 @@ impl<T: StateWrite> 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 _,
Expand All @@ -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()
}

Expand Down Expand Up @@ -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::<astria_core::primitive::v1::asset::Denom>()
.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));
}
}
Loading

0 comments on commit bb6c435

Please sign in to comment.