From 4960e99b59ce04c3c58ffaaa6bf855f8fcefe050 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 11 Nov 2024 14:53:17 +0100 Subject: [PATCH] refactor(core): make address, crypto crates --- Cargo.lock | 29 +- Cargo.toml | 6 + .../src/bridge_withdrawer/submitter/mod.rs | 1 + .../helpers/test_bridge_withdrawer.rs | 2 +- crates/astria-cli/src/sequencer/account.rs | 7 +- crates/astria-cli/src/sequencer/address.rs | 4 +- crates/astria-core-address/Cargo.toml | 14 + crates/astria-core-address/src/lib.rs | 457 ++++++++++++++ crates/astria-core-consts/Cargo.toml | 6 + crates/astria-core-consts/src/lib.rs | 3 + crates/astria-core-crypto/Cargo.toml | 22 + .../src/lib.rs} | 41 +- crates/astria-core/Cargo.toml | 13 +- crates/astria-core/src/lib.rs | 3 +- crates/astria-core/src/primitive/v1/mod.rs | 565 +----------------- .../astria-core/src/protocol/bridge/v1/mod.rs | 23 +- crates/astria-core/src/protocol/genesis/v1.rs | 25 +- .../src/protocol/transaction/v1/action/mod.rs | 40 +- .../src/protocol/transaction/v1/mod.rs | 4 +- .../src/sequencerblock/v1/block.rs | 2 +- .../astria-sequencer-client/src/tests/http.rs | 1 + crates/astria-sequencer/src/accounts/mod.rs | 14 +- .../src/app/tests_breaking_changes.rs | 23 +- .../src/app/tests_execute_transaction.rs | 13 +- crates/astria-sequencer/src/authority/mod.rs | 6 +- .../src/authority/state_ext.rs | 10 +- .../src/authority/storage/values.rs | 6 +- .../astria-sequencer/src/bridge/state_ext.rs | 10 +- .../bridge/storage/values/address_bytes.rs | 6 +- crates/astria-sequencer/src/fees/tests.rs | 5 +- crates/astria-sequencer/src/grpc/sequencer.rs | 2 +- .../storage/values/sequencer_block_header.rs | 8 +- crates/astria-sequencer/src/ibc/state_ext.rs | 6 +- .../src/ibc/storage/values.rs | 6 +- .../src/transaction/checks.rs | 6 +- .../src/transaction/state_ext.rs | 6 +- 36 files changed, 724 insertions(+), 671 deletions(-) create mode 100644 crates/astria-core-address/Cargo.toml create mode 100644 crates/astria-core-address/src/lib.rs create mode 100644 crates/astria-core-consts/Cargo.toml create mode 100644 crates/astria-core-consts/src/lib.rs create mode 100644 crates/astria-core-crypto/Cargo.toml rename crates/{astria-core/src/crypto.rs => astria-core-crypto/src/lib.rs} (93%) diff --git a/Cargo.lock b/Cargo.lock index b95f060418..aada0a3187 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -701,15 +701,15 @@ name = "astria-core" version = "0.1.0" dependencies = [ "astria-core", + "astria-core-address", + "astria-core-crypto", "astria-merkle", "base64 0.21.7", "base64-serde", - "bech32 0.11.0", "brotli", "bytes", "celestia-tendermint", "celestia-types", - "ed25519-consensus", "hex", "ibc-types", "indexmap 2.4.0", @@ -728,6 +728,31 @@ dependencies = [ "thiserror", "tonic 0.10.2", "tracing", +] + +[[package]] +name = "astria-core-address" +version = "0.1.0" +dependencies = [ + "astria-core-consts", + "bech32 0.11.0", + "thiserror", +] + +[[package]] +name = "astria-core-consts" +version = "0.1.0" + +[[package]] +name = "astria-core-crypto" +version = "0.1.0" +dependencies = [ + "astria-core-consts", + "base64 0.21.7", + "ed25519-consensus", + "rand 0.8.5", + "sha2 0.10.8", + "thiserror", "zeroize", ] diff --git a/Cargo.toml b/Cargo.toml index d1cc181333..bded5e96a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,9 @@ members = [ "crates/astria-conductor", "crates/astria-config", "crates/astria-core", + "crates/astria-core-address", + "crates/astria-core-consts", + "crates/astria-core-crypto", "crates/astria-eyre", "crates/astria-grpc-mock", "crates/astria-grpc-mock-test", @@ -34,6 +37,9 @@ default-members = [ "crates/astria-conductor", "crates/astria-config", "crates/astria-core", + "crates/astria-core-address", + "crates/astria-core-consts", + "crates/astria-core-crypto", "crates/astria-grpc-mock", "crates/astria-grpc-mock-test", "crates/astria-grpc-mock-test-codegen", diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs index 0bd72886a5..13f6b8c22e 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs @@ -15,6 +15,7 @@ use astria_core::{ Action, TransactionBody, }, + Protobuf as _, }; use astria_eyre::eyre::{ self, diff --git a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs index 154c409e87..d1d371ed5e 100644 --- a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs +++ b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs @@ -566,7 +566,7 @@ pub fn default_sequencer_address() -> Address { /// Constructs an [`Address`] prefixed by `"astria"`. #[must_use] pub(crate) fn astria_address( - array: [u8; astria_core::primitive::v1::ADDRESS_LEN], + array: [u8; astria_core::primitive::v1::ADDRESS_LENGTH], ) -> astria_core::primitive::v1::Address { astria_core::primitive::v1::Address::builder() .array(array) diff --git a/crates/astria-cli/src/sequencer/account.rs b/crates/astria-cli/src/sequencer/account.rs index 269470725f..e6160730f0 100644 --- a/crates/astria-cli/src/sequencer/account.rs +++ b/crates/astria-cli/src/sequencer/account.rs @@ -51,7 +51,12 @@ impl Create { let signing_key = SigningKey::new(OsRng); let pretty_signing_key = hex::encode(signing_key.as_bytes()); let pretty_verifying_key = hex::encode(signing_key.verification_key().as_bytes()); - let pretty_address = SigningKey::try_address(&signing_key, &self.prefix)?; + + let pretty_address: Address = Address::builder() + .array(signing_key.address_bytes()) + .prefix(&self.prefix) + .try_build()?; + println!("Create Sequencer Account"); println!(); // TODO: don't print private keys to CLI, prefer writing to file: diff --git a/crates/astria-cli/src/sequencer/address.rs b/crates/astria-cli/src/sequencer/address.rs index fa7dcee7cb..5f0a559aea 100644 --- a/crates/astria-cli/src/sequencer/address.rs +++ b/crates/astria-cli/src/sequencer/address.rs @@ -1,6 +1,6 @@ use astria_core::primitive::v1::{ Address, - ADDRESS_LEN, + ADDRESS_LENGTH, }; use color_eyre::eyre::{ self, @@ -39,7 +39,7 @@ struct Bech32m { impl Bech32m { fn run(self) -> eyre::Result<()> { use hex::FromHex as _; - let bytes = <[u8; ADDRESS_LEN]>::from_hex(&self.bytes) + let bytes = <[u8; ADDRESS_LENGTH]>::from_hex(&self.bytes) .wrap_err("failed decoding provided hex bytes")?; let address = Address::::builder() .array(bytes) diff --git a/crates/astria-core-address/Cargo.toml b/crates/astria-core-address/Cargo.toml new file mode 100644 index 0000000000..85a2a03eaf --- /dev/null +++ b/crates/astria-core-address/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "astria-core-address" +version = "0.1.0" +edition = "2021" + +[dependencies] +astria-core-consts = { path = "../astria-core-consts" } + +thiserror = { workspace = true } + +bech32 = "0.11.0" + +[features] +unchecked-constructor = [] diff --git a/crates/astria-core-address/src/lib.rs b/crates/astria-core-address/src/lib.rs new file mode 100644 index 0000000000..3b71e02bc0 --- /dev/null +++ b/crates/astria-core-address/src/lib.rs @@ -0,0 +1,457 @@ +use std::{ + fmt::Display, + marker::PhantomData, + str::FromStr, +}; + +pub use astria_core_consts::ADDRESS_LENGTH; + +#[derive(Debug, Hash)] +pub struct Address { + bytes: [u8; ADDRESS_LENGTH], + prefix: bech32::Hrp, + format: PhantomData, +} + +impl Clone for Address { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Address {} + +impl PartialEq for Address { + fn eq(&self, other: &Self) -> bool { + self.bytes.eq(&other.bytes) && self.prefix.eq(&other.prefix) + } +} + +impl Eq for Address {} + +impl Address { + #[must_use = "the builder must be used to construct an address to be useful"] + pub fn builder() -> Builder { + Builder::new() + } + + #[must_use] + pub fn bytes(self) -> [u8; ADDRESS_LENGTH] { + self.bytes + } + + #[must_use] + pub fn as_bytes(&self) -> &[u8; ADDRESS_LENGTH] { + &self.bytes + } + + #[must_use] + pub fn prefix(&self) -> &str { + self.prefix.as_str() + } + + /// Converts to a new address with the given `prefix`. + /// + /// # Errors + /// Returns an error if an address with `prefix` cannot be constructed. + /// The error conditions for this are the same as for [`AddressBuilder::try_build`]. + pub fn to_prefix(&self, prefix: &str) -> Result { + Self::builder() + .array(*self.as_bytes()) + .prefix(prefix) + .try_build() + } + + /// Converts to a new address with the type argument `OtherFormat`. + /// + /// `OtherFormat` is usually [`Bech32`] or [`Bech32m`]. + #[must_use] + pub fn to_format(&self) -> Address { + Address { + bytes: self.bytes, + prefix: self.prefix, + format: PhantomData, + } + } +} + +impl Address { + /// Should only be used where the inputs have been provided by a trusted entity, e.g. read + /// from our own state store. + /// + /// Note that this function is not considered part of the public API and is subject to breaking + /// change at any time. + #[cfg(feature = "unchecked-constructor")] + #[doc(hidden)] + #[must_use] + pub fn unchecked_from_parts(bytes: [u8; ADDRESS_LENGTH], prefix: &str) -> Self { + Self { + bytes, + prefix: bech32::Hrp::parse_unchecked(prefix), + format: PhantomData, + } + } +} + +impl FromStr for Address { + type Err = Error; + + fn from_str(s: &str) -> Result { + let checked = bech32::primitives::decode::CheckedHrpstring::new::(s) + .map_err(Self::Err::decode)?; + let hrp = checked.hrp(); + Self::builder() + .with_iter(checked.byte_iter()) + .prefix(hrp.as_str()) + .try_build() + } +} + +impl std::fmt::Display for Address { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use bech32::EncodeError; + match bech32::encode_lower_to_fmt::(f, self.prefix, self.as_bytes()) { + Ok(()) => Ok(()), + Err(EncodeError::Fmt(err)) => Err(err), + Err(err) => panic!( + "only formatting errors are valid when encoding astria addresses; all other error \ + variants (only TooLong as of bech32-0.11.0) are guaranteed to not happen because \ + `Address` is length checked:\n{err:?}", + ), + } + } +} + +pub struct NoBytes; +pub struct NoPrefix; +pub struct WithBytes<'a, I>(WithBytesInner<'a, I>); +enum WithBytesInner<'a, I> { + Array([u8; ADDRESS_LENGTH]), + Iter(I), + Slice(std::borrow::Cow<'a, [u8]>), +} +pub struct WithPrefix<'a>(std::borrow::Cow<'a, str>); + +pub struct NoBytesIter; + +impl Iterator for NoBytesIter { + type Item = u8; + + fn next(&mut self) -> Option { + None + } +} + +pub struct Builder { + bytes: TBytes, + prefix: TPrefix, + format: PhantomData, +} + +impl Builder { + const fn new() -> Self { + Self { + bytes: NoBytes, + prefix: NoPrefix, + format: PhantomData, + } + } +} + +impl Builder { + #[must_use = "the builder must be built to construct an address to be useful"] + pub fn array( + self, + array: [u8; ADDRESS_LENGTH], + ) -> Builder, TPrefix> { + Builder { + bytes: WithBytes(WithBytesInner::Array(array)), + prefix: self.prefix, + format: self.format, + } + } + + #[must_use = "the builder must be built to construct an address to be useful"] + pub fn slice<'a, T: Into>>( + self, + bytes: T, + ) -> Builder, TPrefix> { + Builder { + bytes: WithBytes(WithBytesInner::Slice(bytes.into())), + prefix: self.prefix, + format: self.format, + } + } + + #[must_use = "the builder must be built to construct an address to be useful"] + pub fn with_iter>( + self, + iter: T, + ) -> Builder, TPrefix> { + Builder { + bytes: WithBytes(WithBytesInner::Iter(iter)), + prefix: self.prefix, + format: self.format, + } + } + + #[must_use = "the builder must be built to construct an address to be useful"] + pub fn prefix<'a, T: Into>>( + self, + prefix: T, + ) -> Builder> { + Builder { + bytes: self.bytes, + prefix: WithPrefix(prefix.into()), + format: self.format, + } + } +} + +impl<'a, 'b, TFormat, TBytesIter> Builder, WithPrefix<'b>> +where + TBytesIter: IntoIterator, +{ + /// Attempts to build an address from the configured prefix and bytes. + /// + /// # Errors + /// Returns an error if one of the following conditions are violated: + /// + if the prefix shorter than 1 or longer than 83 characters, or contains characters outside + /// 33-126 of ASCII characters. + /// + if the provided bytes are not exactly 20 bytes. + pub fn try_build(self) -> Result, Error> { + let Self { + bytes: WithBytes(bytes), + prefix: WithPrefix(prefix), + format, + } = self; + let bytes = match bytes { + WithBytesInner::Array(bytes) => bytes, + WithBytesInner::Iter(bytes) => try_collect_to_array(bytes)?, + WithBytesInner::Slice(bytes) => <[u8; ADDRESS_LENGTH]>::try_from(bytes.as_ref()) + .map_err(|_| Error::incorrect_address_length(bytes.len()))?, + }; + let prefix = bech32::Hrp::parse(&prefix).map_err(Error::invalid_prefix)?; + Ok(Address { + bytes, + prefix, + format, + }) + } +} + +fn try_collect_to_array>( + iter: I, +) -> Result<[u8; ADDRESS_LENGTH], Error> { + let mut arr = [0; ADDRESS_LENGTH]; + let mut iter = iter.into_iter(); + + let mut bytes_set = 0; + for (i, byte) in iter.by_ref().take(ADDRESS_LENGTH).enumerate() { + arr[i] = byte; + bytes_set += 1; + } + if bytes_set < ADDRESS_LENGTH { + return Err(Error::incorrect_address_length(bytes_set)); + } else if iter.next().is_some() { + return Err(Error::too_many_bytes()); + } + Ok(arr) +} + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct Error(ErrorKind); + +impl Error { + fn decode(source: bech32::primitives::decode::CheckedHrpstringError) -> Self { + Self(ErrorKind::Decode { + source, + }) + } + + fn invalid_prefix(source: bech32::primitives::hrp::Error) -> Self { + Self(ErrorKind::InvalidPrefix { + source, + }) + } + + fn incorrect_address_length(received: usize) -> Self { + Self(ErrorKind::IncorrectLength { + received: BytesProvided::Exact(received), + }) + } + + fn too_many_bytes() -> Self { + Self(ErrorKind::IncorrectLength { + received: BytesProvided::MoreThan(ADDRESS_LENGTH), + }) + } +} + +#[derive(Debug, thiserror::Error, PartialEq)] +enum ErrorKind { + #[error("failed decoding provided string")] + Decode { + source: bech32::primitives::decode::CheckedHrpstringError, + }, + #[error("expected an address of 20 bytes, got `{received}`")] + IncorrectLength { received: BytesProvided }, + #[error("the provided prefix was not a valid bech32 human readable prefix")] + InvalidPrefix { + source: bech32::primitives::hrp::Error, + }, +} + +#[derive(Debug, PartialEq, Eq)] +enum BytesProvided { + Exact(usize), + MoreThan(usize), +} + +impl Display for BytesProvided { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BytesProvided::Exact(bytes) => write!(f, "{bytes}"), + BytesProvided::MoreThan(bytes) => write!(f, "more than {bytes}"), + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum Bech32m {} +#[derive(Clone, Copy, Debug)] +pub enum Bech32 {} +#[derive(Clone, Copy, Debug)] +pub enum NoFormat {} + +#[expect( + private_bounds, + reason = "prevent downstream implementation of this trait" +)] +pub trait Format: Sealed { + type Checksum: bech32::Checksum; +} + +impl Format for Bech32m { + type Checksum = bech32::Bech32m; +} + +impl Format for Bech32 { + type Checksum = bech32::Bech32; +} + +impl Format for NoFormat { + type Checksum = bech32::NoChecksum; +} + +trait Sealed {} +impl Sealed for Bech32m {} +impl Sealed for Bech32 {} +impl Sealed for NoFormat {} + +#[cfg(test)] +mod tests { + use super::{ + Address, + Bech32, + Bech32m, + BytesProvided, + Error, + ErrorKind, + }; + + const ASTRIA_ADDRESS_PREFIX: &str = "astria"; + const ASTRIA_COMPAT_ADDRESS_PREFIX: &str = "astriacompat"; + + #[track_caller] + fn assert_wrong_address_bytes(bad_account: &[u8]) { + let error = Address::::builder() + .slice(bad_account) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .expect_err( + "converting from an incorrectly sized byte slice succeeded where it should have \ + failed", + ); + let Error(ErrorKind::IncorrectLength { + received, + }) = error + else { + panic!("expected ErrorKind::IncorrectLength, got {error:?}"); + }; + assert_eq!(BytesProvided::Exact(bad_account.len()), received); + } + + #[test] + fn account_of_incorrect_length_gives_error() { + assert_wrong_address_bytes(&[42; 0]); + assert_wrong_address_bytes(&[42; 19]); + assert_wrong_address_bytes(&[42; 21]); + assert_wrong_address_bytes(&[42; 100]); + } + + #[test] + fn parse_bech32m_address() { + let expected = Address::builder() + .array([42; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let actual = expected.to_string().parse::
().unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn parse_bech32_address() { + let expected = Address::::builder() + .array([42; 20]) + .prefix(ASTRIA_COMPAT_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let actual = expected.to_string().parse::>().unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn parsing_bech32_address_as_bech32m_fails() { + let expected = Address::::builder() + .array([42; 20]) + .prefix(ASTRIA_COMPAT_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let err = expected + .to_string() + .parse::>() + .expect_err("this must not work"); + match err { + Error(ErrorKind::Decode { + .. + }) => {} + other => { + panic!("expected Error(ErrorKind::Decode {{ .. }}), but got {other:?}") + } + } + } + + #[test] + fn parsing_bech32m_address_as_bech32_fails() { + let expected = Address::::builder() + .array([42; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let err = expected + .to_string() + .parse::>() + .expect_err("this must not work"); + match err { + Error(ErrorKind::Decode { + .. + }) => {} + other => { + panic!("expected Error(ErrorKind::Decode {{ .. }}), but got {other:?}") + } + } + } +} diff --git a/crates/astria-core-consts/Cargo.toml b/crates/astria-core-consts/Cargo.toml new file mode 100644 index 0000000000..125b069104 --- /dev/null +++ b/crates/astria-core-consts/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "astria-core-consts" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/crates/astria-core-consts/src/lib.rs b/crates/astria-core-consts/src/lib.rs new file mode 100644 index 0000000000..1b05c26f66 --- /dev/null +++ b/crates/astria-core-consts/src/lib.rs @@ -0,0 +1,3 @@ +//! Public constants that are used throughout the Astria stack. + +pub const ADDRESS_LENGTH: usize = 20; diff --git a/crates/astria-core-crypto/Cargo.toml b/crates/astria-core-crypto/Cargo.toml new file mode 100644 index 0000000000..4e8b94bf7c --- /dev/null +++ b/crates/astria-core-crypto/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "astria-core-crypto" +version = "0.1.0" +edition = "2021" +rust-version = "1.81.0" +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/astriaorg/astria" +homepage = "https://astria.org" + +[dependencies] +astria-core-consts = { path = "../astria-core-consts" } + +base64 = { workspace = true } +rand = { workspace = true } +sha2 = { workspace = true } +thiserror = { workspace = true } + +ed25519-consensus = { version = "2.1.0", default-features = false, features = [ + "std", +] } +zeroize = { version = "1.7.0", features = ["zeroize_derive"] } diff --git a/crates/astria-core/src/crypto.rs b/crates/astria-core-crypto/src/lib.rs similarity index 93% rename from crates/astria-core/src/crypto.rs rename to crates/astria-core-crypto/src/lib.rs index 2ce819d875..ee7179538f 100644 --- a/crates/astria-core/src/crypto.rs +++ b/crates/astria-core-crypto/src/lib.rs @@ -13,6 +13,7 @@ use std::{ sync::OnceLock, }; +use astria_core_consts::ADDRESS_LENGTH; use base64::{ display::Base64Display, prelude::BASE64_STANDARD, @@ -28,21 +29,11 @@ use rand::{ CryptoRng, RngCore, }; -use sha2::{ - Digest as _, - Sha256, -}; use zeroize::{ Zeroize, ZeroizeOnDrop, }; -use crate::primitive::v1::{ - Address, - AddressError, - ADDRESS_LEN, -}; - /// An Ed25519 signing key. // *Implementation note*: this is currently a refinement type around // ed25519_consensus::SigningKey overriding its Debug implementation @@ -85,22 +76,9 @@ impl SigningKey { /// Returns the address bytes of the verification key associated with this signing key. #[must_use] - pub fn address_bytes(&self) -> [u8; ADDRESS_LEN] { + pub fn address_bytes(&self) -> [u8; ADDRESS_LENGTH] { *self.verification_key().address_bytes() } - - /// Attempts to create an Astria bech32m `[Address]` with the given prefix. - /// - /// # Errors - /// Returns an [`AddressError`] if an address could not be constructed - /// with the given prefix. Usually if the prefix was too long or contained - /// characters not allowed by bech32m. - pub fn try_address(&self, prefix: &str) -> Result { - Address::builder() - .prefix(prefix) - .array(self.address_bytes()) - .try_build() - } } impl Debug for SigningKey { @@ -139,7 +117,7 @@ pub struct VerificationKey { // The address-bytes are lazily-initialized. Since it may or may not be initialized for any // given instance of a verification key, it is excluded from `PartialEq`, `Eq`, `PartialOrd`, // `Ord` and `Hash` impls. - address_bytes: OnceLock<[u8; ADDRESS_LEN]>, + address_bytes: OnceLock<[u8; ADDRESS_LENGTH]>, } impl VerificationKey { @@ -167,18 +145,23 @@ impl VerificationKey { /// /// The address is the first 20 bytes of the sha256 hash of the verification key. #[must_use] - pub fn address_bytes(&self) -> &[u8; ADDRESS_LEN] { + pub fn address_bytes(&self) -> &[u8; ADDRESS_LENGTH] { + use sha2::{ + Digest as _, + Sha256, + }; + self.address_bytes.get_or_init(|| { - fn first_20(array: [u8; 32]) -> [u8; ADDRESS_LEN] { + fn first_20(array: [u8; 32]) -> [u8; ADDRESS_LENGTH] { [ array[0], array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8], array[9], array[10], array[11], array[12], array[13], array[14], array[15], array[16], array[17], array[18], array[19], ] } - /// this ensures that `ADDRESS_LEN` is never accidentally changed to a value + /// this ensures that `ADDRESS_LENGTH` is never accidentally changed to a value /// that would violate this assumption. - const _: () = assert!(ADDRESS_LEN <= 32); + const _: () = assert!(ADDRESS_LENGTH <= 32); let bytes: [u8; 32] = Sha256::digest(self).into(); first_20(bytes) }) diff --git a/crates/astria-core/Cargo.toml b/crates/astria-core/Cargo.toml index a7a45d5ba0..4eee313a96 100644 --- a/crates/astria-core/Cargo.toml +++ b/crates/astria-core/Cargo.toml @@ -16,18 +16,16 @@ keywords = ["astria", "grpc", "rpc", "blockchain", "execution", "protobuf"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bech32 = "0.11.0" brotli = { version = "5.0.0", optional = true } celestia-types = { version = "0.1.1", optional = true } pbjson = { version = "0.6.0", optional = true } +astria-core-address = { path = "../astria-core-address" } +astria-core-crypto = { path = "../astria-core-crypto" } merkle = { package = "astria-merkle", path = "../astria-merkle" } bytes = { workspace = true } celestia-tendermint = { workspace = true } -ed25519-consensus = { version = "2.1.0", default-features = false, features = [ - "std", -] } hex = { workspace = true } ibc-types = { workspace = true } indexmap = { workspace = true } @@ -35,7 +33,7 @@ pbjson-types = { workspace = true } penumbra-ibc = { workspace = true } penumbra-proto = { workspace = true } prost = { workspace = true } -rand = { workspace = true } +rand = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"], optional = true } sha2 = { workspace = true } tendermint = { workspace = true } @@ -45,20 +43,19 @@ tonic = { workspace = true, optional = true } tracing = { workspace = true } base64-serde = { workspace = true, optional = true } base64 = { workspace = true } -zeroize = { version = "1.7.0", features = ["zeroize_derive"] } [features] celestia = ["dep:celestia-types"] client = ["dep:tonic"] serde = ["dep:serde", "dep:pbjson", "dep:base64-serde"] server = ["dep:tonic"] -test-utils = [] +test-utils = ["dep:rand"] base64-serde = ["dep:base64-serde"] brotli = ["dep:brotli"] # When enabled, this adds constructors for some types that skip the normal constructor validity # checks. It supports the case where the inputs are already deemed valid, e.g. having read them from # local storage. -unchecked-constructors = [] +unchecked-constructors = ["astria-core-address/unchecked-constructor"] [dev-dependencies] astria-core = { path = ".", features = ["serde"] } diff --git a/crates/astria-core/src/lib.rs b/crates/astria-core/src/lib.rs index 5c97446d17..b6718e136e 100644 --- a/crates/astria-core/src/lib.rs +++ b/crates/astria-core/src/lib.rs @@ -13,12 +13,13 @@ compile_error!( )] pub mod generated; -pub mod crypto; pub mod execution; pub mod primitive; pub mod protocol; pub mod sequencerblock; +pub use astria_core_crypto as crypto; + #[cfg(feature = "brotli")] pub mod brotli; #[cfg(feature = "celestia")] diff --git a/crates/astria-core/src/primitive/v1/mod.rs b/crates/astria-core/src/primitive/v1/mod.rs index 56cb6922b5..dd87a4c824 100644 --- a/crates/astria-core/src/primitive/v1/mod.rs +++ b/crates/astria-core/src/primitive/v1/mod.rs @@ -1,11 +1,15 @@ pub mod asset; pub mod u128; -use std::{ - marker::PhantomData, - str::FromStr, +pub use address::{ + Address, + Bech32, + Bech32m, + Error as AddressError, + Format, + ADDRESS_LENGTH, }; - +pub use astria_core_address as address; use base64::{ display::Base64Display, prelude::BASE64_URL_SAFE, @@ -21,12 +25,28 @@ use crate::{ Protobuf, }; -pub const ADDRESS_LEN: usize = 20; - pub const ROLLUP_ID_LEN: usize = 32; pub const TRANSACTION_ID_LEN: usize = 32; +impl Protobuf for Address { + type Error = AddressError; + type Raw = raw::Address; + + fn try_from_raw_ref(raw: &Self::Raw) -> Result { + let raw::Address { + bech32m, + } = raw; + bech32m.parse() + } + + fn to_raw(&self) -> Self::Raw { + raw::Address { + bech32m: self.to_string(), + } + } +} + impl Protobuf for merkle::Proof { type Error = merkle::audit::InvalidProof; type Raw = raw::Proof; @@ -254,431 +274,6 @@ pub struct IncorrectRollupIdLength { received: usize, } -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct AddressError(AddressErrorKind); - -impl AddressError { - fn decode(source: bech32::primitives::decode::CheckedHrpstringError) -> Self { - Self(AddressErrorKind::Decode { - source, - }) - } - - fn invalid_prefix(source: bech32::primitives::hrp::Error) -> Self { - Self(AddressErrorKind::InvalidPrefix { - source, - }) - } - - fn incorrect_address_length(received: usize) -> Self { - Self(AddressErrorKind::IncorrectAddressLength { - received, - }) - } -} - -#[derive(Debug, thiserror::Error, PartialEq)] -enum AddressErrorKind { - #[error("failed decoding provided string")] - Decode { - source: bech32::primitives::decode::CheckedHrpstringError, - }, - #[error("expected an address of 20 bytes, got `{received}`")] - IncorrectAddressLength { received: usize }, - #[error("the provided prefix was not a valid bech32 human readable prefix")] - InvalidPrefix { - source: bech32::primitives::hrp::Error, - }, -} - -pub struct NoBytes; -pub struct NoPrefix; -pub struct WithBytes<'a, I>(WithBytesInner<'a, I>); -enum WithBytesInner<'a, I> { - Array([u8; ADDRESS_LEN]), - Iter(I), - Slice(std::borrow::Cow<'a, [u8]>), -} -pub struct WithPrefix<'a>(std::borrow::Cow<'a, str>); - -pub struct NoBytesIter; - -impl Iterator for NoBytesIter { - type Item = u8; - - fn next(&mut self) -> Option { - None - } -} - -pub struct AddressBuilder { - bytes: TBytes, - prefix: TPrefix, - format: PhantomData, -} - -impl AddressBuilder { - const fn new() -> Self { - Self { - bytes: NoBytes, - prefix: NoPrefix, - format: PhantomData, - } - } -} - -impl AddressBuilder { - #[must_use = "the builder must be built to construct an address to be useful"] - pub fn array( - self, - array: [u8; ADDRESS_LEN], - ) -> AddressBuilder, TPrefix> { - AddressBuilder { - bytes: WithBytes(WithBytesInner::Array(array)), - prefix: self.prefix, - format: self.format, - } - } - - #[must_use = "the builder must be built to construct an address to be useful"] - pub fn slice<'a, T: Into>>( - self, - bytes: T, - ) -> AddressBuilder, TPrefix> { - AddressBuilder { - bytes: WithBytes(WithBytesInner::Slice(bytes.into())), - prefix: self.prefix, - format: self.format, - } - } - - #[must_use = "the builder must be built to construct an address to be useful"] - pub fn with_iter>( - self, - iter: T, - ) -> AddressBuilder, TPrefix> { - AddressBuilder { - bytes: WithBytes(WithBytesInner::Iter(iter)), - prefix: self.prefix, - format: self.format, - } - } - - /// Use the given verification key for address generation. - /// - /// The verification key is hashed with SHA256 and the first 20 bytes are used as the address - /// bytes. - #[expect(clippy::missing_panics_doc, reason = "the conversion is infallible")] - #[must_use = "the builder must be built to construct an address to be useful"] - pub fn verification_key( - self, - key: &crate::crypto::VerificationKey, - ) -> AddressBuilder, TPrefix> { - let hash = Sha256::digest(key.as_bytes()); - let array: [u8; ADDRESS_LEN] = hash[0..ADDRESS_LEN] - .try_into() - .expect("hash is 32 bytes long, so must always be able to convert to 20 bytes"); - self.array(array) - } - - #[must_use = "the builder must be built to construct an address to be useful"] - pub fn prefix<'a, T: Into>>( - self, - prefix: T, - ) -> AddressBuilder> { - AddressBuilder { - bytes: self.bytes, - prefix: WithPrefix(prefix.into()), - format: self.format, - } - } -} - -impl<'a, 'b, TFormat, TBytesIter> AddressBuilder, WithPrefix<'b>> -where - TBytesIter: IntoIterator, -{ - /// Attempts to build an address from the configured prefix and bytes. - /// - /// # Errors - /// Returns an error if one of the following conditions are violated: - /// + if the prefix shorter than 1 or longer than 83 characters, or contains characters outside - /// 33-126 of ASCII characters. - /// + if the provided bytes are not exactly 20 bytes. - pub fn try_build(self) -> Result, AddressError> { - let Self { - bytes: WithBytes(bytes), - prefix: WithPrefix(prefix), - format, - } = self; - let bytes = match bytes { - WithBytesInner::Array(bytes) => bytes, - WithBytesInner::Iter(bytes) => try_collect_to_array(bytes)?, - WithBytesInner::Slice(bytes) => <[u8; ADDRESS_LEN]>::try_from(bytes.as_ref()) - .map_err(|_| AddressError::incorrect_address_length(bytes.len()))?, - }; - let prefix = bech32::Hrp::parse(&prefix).map_err(AddressError::invalid_prefix)?; - Ok(Address { - bytes, - prefix, - format, - }) - } -} - -fn try_collect_to_array>( - iter: I, -) -> Result<[u8; ADDRESS_LEN], AddressError> { - let mut arr = [0; ADDRESS_LEN]; - let mut iter = iter.into_iter(); - let mut i = 0; - loop { - if i >= ADDRESS_LEN { - break; - } - let Some(byte) = iter.next() else { - break; - }; - arr[i] = byte; - i = i.saturating_add(1); - } - let items_in_iterator = i.saturating_add(iter.count()); - if items_in_iterator != ADDRESS_LEN { - return Err(AddressError::incorrect_address_length(items_in_iterator)); - } - Ok(arr) -} - -#[derive(Clone, Copy, Debug)] -pub enum Bech32m {} -#[derive(Clone, Copy, Debug)] -pub enum Bech32 {} -#[derive(Clone, Copy, Debug)] -pub enum NoFormat {} - -pub trait Format: private::Sealed { - type Checksum: bech32::Checksum; -} - -impl Format for Bech32m { - type Checksum = bech32::Bech32m; -} - -impl Format for Bech32 { - type Checksum = bech32::Bech32; -} - -impl Format for NoFormat { - type Checksum = bech32::NoChecksum; -} - -mod private { - pub trait Sealed {} - impl Sealed for super::Bech32m {} - impl Sealed for super::Bech32 {} - impl Sealed for super::NoFormat {} -} - -#[derive(Debug, Hash)] -pub struct Address { - bytes: [u8; ADDRESS_LEN], - prefix: bech32::Hrp, - format: PhantomData, -} - -// The serde impls need to be manually implemented for Address because they -// only work for Address which cannot be expressed using serde -// attributes. -#[cfg(feature = "serde")] -mod _serde_impls { - use serde::de::Error as _; - impl serde::Serialize for super::Address { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.to_raw().serialize(serializer) - } - } - impl<'de> serde::Deserialize<'de> for super::Address { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - super::raw::Address::deserialize(deserializer) - .and_then(|raw| raw.try_into().map_err(D::Error::custom)) - } - } -} - -impl Clone for Address { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for Address {} - -impl PartialEq for Address { - fn eq(&self, other: &Self) -> bool { - self.bytes.eq(&other.bytes) && self.prefix.eq(&other.prefix) - } -} - -impl Eq for Address {} - -impl Address { - #[must_use = "the builder must be used to construct an address to be useful"] - pub fn builder() -> AddressBuilder { - AddressBuilder::::new() - } - - #[must_use] - pub fn bytes(self) -> [u8; ADDRESS_LEN] { - self.bytes - } - - #[must_use] - pub fn as_bytes(&self) -> &[u8; ADDRESS_LEN] { - &self.bytes - } - - #[must_use] - pub fn prefix(&self) -> &str { - self.prefix.as_str() - } - - /// Converts to a new address with the given `prefix`. - /// - /// # Errors - /// Returns an error if an address with `prefix` cannot be constructed. - /// The error conditions for this are the same as for [`AddressBuilder::try_build`]. - pub fn to_prefix(&self, prefix: &str) -> Result { - Self::builder() - .array(*self.as_bytes()) - .prefix(prefix) - .try_build() - } - - /// Converts to a new address with the type argument `OtherFormat`. - /// - /// `OtherFormat` is usually [`Bech32`] or [`Bech32m`]. - #[must_use] - pub fn to_format(&self) -> Address { - Address { - bytes: self.bytes, - prefix: self.prefix, - format: PhantomData, - } - } -} - -impl Address { - /// Convert [`Address`] to a [`raw::Address`]. - #[expect( - clippy::missing_panics_doc, - reason = "panics are checked to not happen" - )] - #[must_use] - pub fn to_raw(&self) -> raw::Address { - let bech32m = - bech32::encode_lower::<::Checksum>(self.prefix, self.as_bytes()) - .expect( - "should not fail because len(prefix) + len(bytes) <= 63 < BECH32M::CODELENGTH", - ); - raw::Address { - bech32m, - } - } - - #[must_use] - pub fn into_raw(self) -> raw::Address { - self.to_raw() - } - - /// Convert from protobuf to rust type an address. - /// - /// # Errors - /// - /// Returns an error if the account buffer was not 20 bytes long. - pub fn try_from_raw(raw: &raw::Address) -> Result { - let raw::Address { - bech32m, - } = raw; - bech32m.parse() - } - - /// This should only be used where the inputs have been provided by a trusted entity, e.g. read - /// from our own state store. - /// - /// Note that this function is not considered part of the public API and is subject to breaking - /// change at any time. - #[cfg(feature = "unchecked-constructors")] - #[doc(hidden)] - #[must_use] - pub fn unchecked_from_parts(bytes: [u8; ADDRESS_LEN], prefix: &str) -> Self { - Self { - bytes, - prefix: bech32::Hrp::parse_unchecked(prefix), - format: PhantomData, - } - } -} - -impl From> for raw::Address { - fn from(value: Address) -> Self { - value.into_raw() - } -} - -impl FromStr for Address { - type Err = AddressError; - - fn from_str(s: &str) -> Result { - let checked = bech32::primitives::decode::CheckedHrpstring::new::(s) - .map_err(Self::Err::decode)?; - let hrp = checked.hrp(); - Self::builder() - .with_iter(checked.byte_iter()) - .prefix(hrp.as_str()) - .try_build() - } -} - -impl TryFrom for Address { - type Error = AddressError; - - fn try_from(value: raw::Address) -> Result { - Self::try_from_raw(&value) - } -} - -impl std::fmt::Display for Address { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use bech32::EncodeError; - match bech32::encode_lower_to_fmt::(f, self.prefix, self.as_bytes()) { - Ok(()) => Ok(()), - Err(EncodeError::Fmt(err)) => Err(err), - Err(err) => panic!( - "only formatting errors are valid when encoding astria addresses; all other error \ - variants (only TooLong as of bech32-0.11.0) are guaranteed to not happen because \ - `Address` is length checked:\n{err:?}", - ), - } - } -} -/// Constructs a dummy address from a given `prefix`, otherwise fail. -pub(crate) fn try_construct_dummy_address_from_prefix( - prefix: &str, -) -> Result<(), AddressError> { - Address::::builder() - .array([0u8; ADDRESS_LEN]) - .prefix(prefix) - .try_build() - .map(|_| ()) -} - /// Derive a [`merkle::Tree`] from an iterable. /// /// It is the responsibility of the caller to ensure that the iterable is @@ -799,42 +394,12 @@ enum TransactionIdErrorKind { mod tests { use super::{ Address, - AddressError, - AddressErrorKind, - Bech32m, - ADDRESS_LEN, + ADDRESS_LENGTH, }; - use crate::primitive::v1::Bech32; + use crate::Protobuf as _; const ASTRIA_ADDRESS_PREFIX: &str = "astria"; const ASTRIA_COMPAT_ADDRESS_PREFIX: &str = "astriacompat"; - #[track_caller] - fn assert_wrong_address_bytes(bad_account: &[u8]) { - let error = Address::::builder() - .slice(bad_account) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .expect_err( - "converting from an incorrectly sized byte slice succeeded where it should have \ - failed", - ); - let AddressError(AddressErrorKind::IncorrectAddressLength { - received, - }) = error - else { - panic!("expected AddressErrorKind::IncorrectAddressLength, got {error:?}"); - }; - assert_eq!(bad_account.len(), received); - } - - #[test] - fn account_of_incorrect_length_gives_error() { - assert_wrong_address_bytes(&[42; 0]); - assert_wrong_address_bytes(&[42; 19]); - assert_wrong_address_bytes(&[42; 21]); - assert_wrong_address_bytes(&[42; 100]); - } - #[cfg(feature = "serde")] #[test] fn snapshots() { @@ -856,80 +421,12 @@ mod tests { insta::assert_snapshot!(&compat_address); } - #[test] - fn parse_bech32m_address() { - let expected = Address::builder() - .array([42; 20]) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .unwrap(); - let actual = expected.to_string().parse::
().unwrap(); - assert_eq!(expected, actual); - } - - #[test] - fn parse_bech32_address() { - let expected = Address::::builder() - .array([42; 20]) - .prefix(ASTRIA_COMPAT_ADDRESS_PREFIX) - .try_build() - .unwrap(); - let actual = expected.to_string().parse::>().unwrap(); - assert_eq!(expected, actual); - } - - #[test] - fn parsing_bech32_address_as_bech32m_fails() { - let expected = Address::::builder() - .array([42; 20]) - .prefix(ASTRIA_COMPAT_ADDRESS_PREFIX) - .try_build() - .unwrap(); - let err = expected - .to_string() - .parse::>() - .expect_err("this must not work"); - match err { - AddressError(AddressErrorKind::Decode { - .. - }) => {} - other => { - panic!( - "expected AddressError(AddressErrorKind::Decode {{ .. }}), but got {other:?}" - ) - } - } - } - - #[test] - fn parsing_bech32m_address_as_bech32_fails() { - let expected = Address::::builder() - .array([42; 20]) - .prefix(ASTRIA_ADDRESS_PREFIX) - .try_build() - .unwrap(); - let err = expected - .to_string() - .parse::>() - .expect_err("this must not work"); - match err { - AddressError(AddressErrorKind::Decode { - .. - }) => {} - other => { - panic!( - "expected AddressError(AddressErrorKind::Decode {{ .. }}), but got {other:?}" - ) - } - } - } - #[test] fn can_construct_protobuf_from_address_with_maximally_sized_prefix() { // 83 is the maximal length of a hrp let long_prefix = [b'a'; 83]; let address = Address::builder() - .array([42u8; ADDRESS_LEN]) + .array([42u8; ADDRESS_LENGTH]) .prefix(std::str::from_utf8(&long_prefix).unwrap()) .try_build() .unwrap(); @@ -939,14 +436,14 @@ mod tests { #[cfg(feature = "unchecked-constructors")] #[test] fn address_to_unchecked_roundtrip() { - let bytes = [42u8; ADDRESS_LEN]; + let bytes = [42u8; ADDRESS_LENGTH]; let input = Address::builder() .array(bytes) .prefix(ASTRIA_ADDRESS_PREFIX) .try_build() .unwrap(); let unchecked = input.into_raw(); - let roundtripped = Address::try_from_raw(&unchecked).unwrap(); + let roundtripped = Address::try_from_raw(unchecked).unwrap(); assert_eq!(input, roundtripped); assert_eq!(input.as_bytes(), roundtripped.as_bytes()); assert_eq!("astria", input.prefix()); diff --git a/crates/astria-core/src/protocol/bridge/v1/mod.rs b/crates/astria-core/src/protocol/bridge/v1/mod.rs index df30ed9068..49b3e07c90 100644 --- a/crates/astria-core/src/protocol/bridge/v1/mod.rs +++ b/crates/astria-core/src/protocol/bridge/v1/mod.rs @@ -1,13 +1,18 @@ use bytes::Bytes; use super::raw; -use crate::primitive::v1::{ - asset, - asset::denom::ParseDenomError, - Address, - AddressError, - IncorrectRollupIdLength, - RollupId, +use crate::{ + primitive::v1::{ + asset::{ + self, + denom::ParseDenomError, + }, + Address, + AddressError, + IncorrectRollupIdLength, + RollupId, + }, + Protobuf as _, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -159,9 +164,9 @@ impl BridgeAccountInfoResponse { rollup_id: RollupId::try_from_raw(rollup_id) .map_err(BridgeAccountInfoResponseError::invalid_rollup_id)?, asset, - sudo_address: Address::try_from_raw(&sudo_address) + sudo_address: Address::try_from_raw(sudo_address) .map_err(BridgeAccountInfoResponseError::invalid_sudo_address)?, - withdrawer_address: Address::try_from_raw(&withdrawer_address) + withdrawer_address: Address::try_from_raw(withdrawer_address) .map_err(BridgeAccountInfoResponseError::invalid_withdrawer_address)?, }), }) diff --git a/crates/astria-core/src/protocol/genesis/v1.rs b/crates/astria-core/src/protocol/genesis/v1.rs index 69bb2269bd..2ad1619c27 100644 --- a/crates/astria-core/src/protocol/genesis/v1.rs +++ b/crates/astria-core/src/protocol/genesis/v1.rs @@ -10,7 +10,6 @@ use crate::{ denom::ParseTracePrefixedError, ParseDenomError, }, - try_construct_dummy_address_from_prefix, Address, AddressError, Bech32, @@ -183,16 +182,18 @@ impl Protobuf for GenesisAppState { .as_ref() .ok_or_else(|| Self::Error::field_not_set("authority_sudo_address")) .and_then(|addr| { - Address::try_from_raw(addr).map_err(Self::Error::authority_sudo_address) + Address::try_from_raw_ref(addr).map_err(Self::Error::authority_sudo_address) })?; let ibc_sudo_address = ibc_sudo_address .as_ref() .ok_or_else(|| Self::Error::field_not_set("ibc_sudo_address")) - .and_then(|addr| Address::try_from_raw(addr).map_err(Self::Error::ibc_sudo_address))?; + .and_then(|addr| { + Address::try_from_raw_ref(addr).map_err(Self::Error::ibc_sudo_address) + })?; let ibc_relayer_addresses = ibc_relayer_addresses .iter() - .map(Address::try_from_raw) + .map(Address::try_from_raw_ref) .collect::>() .map_err(Self::Error::ibc_relayer_addresses)?; @@ -405,7 +406,7 @@ impl Protobuf for Account { let address = address .as_ref() .ok_or_else(|| AccountError::field_not_set("address")) - .and_then(|addr| Address::try_from_raw(addr).map_err(Self::Error::address))?; + .and_then(|addr| Address::try_from_raw_ref(addr).map_err(Self::Error::address))?; let balance = balance .ok_or_else(|| AccountError::field_not_set("balance")) .map(Into::into)?; @@ -481,12 +482,22 @@ impl Protobuf for AddressPrefixes { type Raw = raw::AddressPrefixes; fn try_from_raw_ref(raw: &Self::Raw) -> Result { + fn dummy_addr(prefix: &str) -> Result<(), AddressError> { + Address::::builder() + .array([0u8; crate::primitive::v1::ADDRESS_LENGTH]) + .prefix(prefix) + .try_build() + .map(|_| ()) + } + let Self::Raw { base, ibc_compat, } = raw; - try_construct_dummy_address_from_prefix::(base).map_err(Self::Error::base)?; - try_construct_dummy_address_from_prefix::(ibc_compat).map_err(Self::Error::base)?; + + dummy_addr::(base).map_err(Self::Error::base)?; + dummy_addr::(ibc_compat).map_err(Self::Error::base)?; + Ok(Self { base: base.to_string(), ibc_compat: ibc_compat.to_string(), diff --git a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs index c4f8320f9c..80ba7d2326 100644 --- a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs @@ -538,7 +538,7 @@ impl Protobuf for Transfer { let Some(to) = to else { return Err(TransferError::field_not_set("to")); }; - let to = Address::try_from_raw(to).map_err(TransferError::address)?; + let to = Address::try_from_raw_ref(to).map_err(TransferError::address)?; let amount = amount.map_or(0, Into::into); let asset = asset.parse().map_err(TransferError::asset)?; let fee_asset = fee_asset.parse().map_err(TransferError::fee_asset)?; @@ -780,7 +780,7 @@ impl Protobuf for SudoAddressChange { return Err(SudoAddressChangeError::field_not_set("new_address")); }; let new_address = - Address::try_from_raw(new_address).map_err(SudoAddressChangeError::address)?; + Address::try_from_raw_ref(new_address).map_err(SudoAddressChangeError::address)?; Ok(Self { new_address, }) @@ -847,7 +847,7 @@ impl Protobuf for IbcSudoChange { return Err(IbcSudoChangeError::field_not_set("new_address")); }; let new_address = - Address::try_from_raw(new_address).map_err(IbcSudoChangeError::address)?; + Address::try_from_raw_ref(new_address).map_err(IbcSudoChangeError::address)?; Ok(Self { new_address, }) @@ -1034,7 +1034,7 @@ impl Protobuf for Ics20Withdrawal { } = proto; let amount = amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?; let return_address = Address::try_from_raw( - &return_address.ok_or(Ics20WithdrawalError::field_not_set("return_address"))?, + return_address.ok_or(Ics20WithdrawalError::field_not_set("return_address"))?, ) .map_err(Ics20WithdrawalError::return_address)?; @@ -1042,7 +1042,6 @@ impl Protobuf for Ics20Withdrawal { .ok_or(Ics20WithdrawalError::field_not_set("timeout_height"))? .into(); let bridge_address = bridge_address - .as_ref() .map(Address::try_from_raw) .transpose() .map_err(Ics20WithdrawalError::invalid_bridge_address)?; @@ -1090,12 +1089,13 @@ impl Protobuf for Ics20Withdrawal { use_compat_address, } = proto; let amount = amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?; - let return_address = Address::try_from_raw( - return_address - .as_ref() - .ok_or(Ics20WithdrawalError::field_not_set("return_address"))?, - ) - .map_err(Ics20WithdrawalError::return_address)?; + let return_address = return_address + .as_ref() + .ok_or_else(|| Ics20WithdrawalError::field_not_set("return_address")) + .and_then(|return_address| { + Address::try_from_raw_ref(return_address) + .map_err(Ics20WithdrawalError::return_address) + })?; let timeout_height = timeout_height .clone() @@ -1103,7 +1103,7 @@ impl Protobuf for Ics20Withdrawal { .into(); let bridge_address = bridge_address .as_ref() - .map(Address::try_from_raw) + .map(Address::try_from_raw_ref) .transpose() .map_err(Ics20WithdrawalError::invalid_bridge_address)?; @@ -1245,14 +1245,14 @@ impl Protobuf for IbcRelayerChange { value: Some(raw::ibc_relayer_change::Value::Addition(address)), } => { let address = - Address::try_from_raw(address).map_err(IbcRelayerChangeError::address)?; + Address::try_from_raw_ref(address).map_err(IbcRelayerChangeError::address)?; Ok(IbcRelayerChange::Addition(address)) } raw::IbcRelayerChange { value: Some(raw::ibc_relayer_change::Value::Removal(address)), } => { let address = - Address::try_from_raw(address).map_err(IbcRelayerChangeError::address)?; + Address::try_from_raw_ref(address).map_err(IbcRelayerChangeError::address)?; Ok(IbcRelayerChange::Removal(address)) } _ => Err(IbcRelayerChangeError::missing_address()), @@ -1423,13 +1423,11 @@ impl Protobuf for InitBridgeAccount { .map_err(InitBridgeAccountError::invalid_fee_asset)?; let sudo_address = proto .sudo_address - .as_ref() .map(Address::try_from_raw) .transpose() .map_err(InitBridgeAccountError::invalid_sudo_address)?; let withdrawer_address = proto .withdrawer_address - .as_ref() .map(Address::try_from_raw) .transpose() .map_err(InitBridgeAccountError::invalid_withdrawer_address)?; @@ -1558,7 +1556,7 @@ impl Protobuf for BridgeLock { let Some(to) = proto.to else { return Err(BridgeLockError::field_not_set("to")); }; - let to = Address::try_from_raw(&to).map_err(BridgeLockError::address)?; + let to = Address::try_from_raw(to).map_err(BridgeLockError::address)?; let amount = proto.amount.ok_or(BridgeLockError::missing_amount())?; let asset = proto .asset @@ -1704,13 +1702,13 @@ impl Protobuf for BridgeUnlock { } = proto; let to = to .ok_or_else(|| BridgeUnlockError::field_not_set("to")) - .and_then(|to| Address::try_from_raw(&to).map_err(BridgeUnlockError::address))?; + .and_then(|to| Address::try_from_raw(to).map_err(BridgeUnlockError::address))?; let amount = amount.ok_or_else(|| BridgeUnlockError::field_not_set("amount"))?; let fee_asset = fee_asset.parse().map_err(BridgeUnlockError::fee_asset)?; let bridge_address = bridge_address .ok_or_else(|| BridgeUnlockError::field_not_set("bridge_address")) - .and_then(|to| Address::try_from_raw(&to).map_err(BridgeUnlockError::bridge_address))?; + .and_then(|to| Address::try_from_raw(to).map_err(BridgeUnlockError::bridge_address))?; Ok(Self { to, amount: amount.into(), @@ -1825,17 +1823,15 @@ impl Protobuf for BridgeSudoChange { let Some(bridge_address) = proto.bridge_address else { return Err(BridgeSudoChangeError::field_not_set("bridge_address")); }; - let bridge_address = Address::try_from_raw(&bridge_address) + let bridge_address = Address::try_from_raw(bridge_address) .map_err(BridgeSudoChangeError::invalid_bridge_address)?; let new_sudo_address = proto .new_sudo_address - .as_ref() .map(Address::try_from_raw) .transpose() .map_err(BridgeSudoChangeError::invalid_new_sudo_address)?; let new_withdrawer_address = proto .new_withdrawer_address - .as_ref() .map(Address::try_from_raw) .transpose() .map_err(BridgeSudoChangeError::invalid_new_withdrawer_address)?; diff --git a/crates/astria-core/src/protocol/transaction/v1/mod.rs b/crates/astria-core/src/protocol/transaction/v1/mod.rs index 6bfbd5b22c..15b7d4200d 100644 --- a/crates/astria-core/src/protocol/transaction/v1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1/mod.rs @@ -14,7 +14,7 @@ use crate::{ generated::protocol::transaction::v1 as raw, primitive::v1::{ TransactionId, - ADDRESS_LEN, + ADDRESS_LENGTH, }, Protobuf, }; @@ -172,7 +172,7 @@ impl Protobuf for Transaction { } impl Transaction { - pub fn address_bytes(&self) -> &[u8; ADDRESS_LEN] { + pub fn address_bytes(&self) -> &[u8; ADDRESS_LENGTH] { self.verification_key.address_bytes() } diff --git a/crates/astria-core/src/sequencerblock/v1/block.rs b/crates/astria-core/src/sequencerblock/v1/block.rs index 48f8907fac..c7013e4d85 100644 --- a/crates/astria-core/src/sequencerblock/v1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1/block.rs @@ -1428,7 +1428,7 @@ impl Deposit { return Err(DepositError::field_not_set("bridge_address")); }; let bridge_address = - Address::try_from_raw(&bridge_address).map_err(DepositError::address)?; + Address::try_from_raw(bridge_address).map_err(DepositError::address)?; let amount = amount.ok_or(DepositError::field_not_set("amount"))?.into(); let Some(rollup_id) = rollup_id else { return Err(DepositError::field_not_set("rollup_id")); diff --git a/crates/astria-sequencer-client/src/tests/http.rs b/crates/astria-sequencer-client/src/tests/http.rs index 23f1ee481c..9fca6a4949 100644 --- a/crates/astria-sequencer-client/src/tests/http.rs +++ b/crates/astria-sequencer-client/src/tests/http.rs @@ -12,6 +12,7 @@ use astria_core::{ Transaction, TransactionBody, }, + Protobuf as _, }; use hex_literal::hex; use prost::bytes::Bytes; diff --git a/crates/astria-sequencer/src/accounts/mod.rs b/crates/astria-sequencer/src/accounts/mod.rs index 436ff211ea..19081cc4f3 100644 --- a/crates/astria-sequencer/src/accounts/mod.rs +++ b/crates/astria-sequencer/src/accounts/mod.rs @@ -8,7 +8,7 @@ use astria_core::{ crypto::VerificationKey, primitive::v1::{ Address, - ADDRESS_LEN, + ADDRESS_LENGTH, }, protocol::transaction::v1::Transaction, }; @@ -19,7 +19,7 @@ pub(crate) use state_ext::{ }; pub(crate) trait AddressBytes: Send + Sync { - fn address_bytes(&self) -> &[u8; ADDRESS_LEN]; + fn address_bytes(&self) -> &[u8; ADDRESS_LENGTH]; fn display_address(&self) -> impl std::fmt::Display { telemetry::display::base64(self.address_bytes()) @@ -27,7 +27,7 @@ pub(crate) trait AddressBytes: Send + Sync { } impl AddressBytes for Address { - fn address_bytes(&self) -> &[u8; ADDRESS_LEN] { + fn address_bytes(&self) -> &[u8; ADDRESS_LENGTH] { self.as_bytes() } @@ -36,20 +36,20 @@ impl AddressBytes for Address { } } -impl AddressBytes for [u8; ADDRESS_LEN] { - fn address_bytes(&self) -> &[u8; ADDRESS_LEN] { +impl AddressBytes for [u8; ADDRESS_LENGTH] { + fn address_bytes(&self) -> &[u8; ADDRESS_LENGTH] { self } } impl<'a> AddressBytes for &'a Transaction { - fn address_bytes(&self) -> &'a [u8; ADDRESS_LEN] { + fn address_bytes(&self) -> &'a [u8; ADDRESS_LENGTH] { Transaction::address_bytes(self) } } impl AddressBytes for VerificationKey { - fn address_bytes(&self) -> &[u8; ADDRESS_LEN] { + fn address_bytes(&self) -> &[u8; ADDRESS_LENGTH] { self.address_bytes() } } diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 031538a057..b70ee03d8f 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -15,7 +15,10 @@ use std::{ }; use astria_core::{ - primitive::v1::RollupId, + primitive::v1::{ + Address, + RollupId, + }, protocol::{ genesis::v1::Account, transaction::v1::{ @@ -190,8 +193,22 @@ async fn app_execute_transaction_with_every_action_snapshot() { }; let genesis_state = astria_core::generated::protocol::genesis::v1::GenesisAppState { accounts, - authority_sudo_address: Some(alice.try_address(ASTRIA_PREFIX).unwrap().to_raw()), - ibc_sudo_address: Some(alice.try_address(ASTRIA_PREFIX).unwrap().to_raw()), + authority_sudo_address: Some( + Address::builder() + .prefix(ASTRIA_PREFIX) + .array(alice.address_bytes()) + .try_build() + .unwrap() + .to_raw(), + ), + ibc_sudo_address: Some( + Address::builder() + .prefix(ASTRIA_PREFIX) + .array(alice.address_bytes()) + .try_build() + .unwrap() + .to_raw(), + ), ..proto_genesis_state() } .try_into() diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 7f4d99d658..6159a89e46 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -4,6 +4,7 @@ use astria_core::{ crypto::SigningKey, primitive::v1::{ asset, + Address, RollupId, }, protocol::{ @@ -78,14 +79,18 @@ use crate::{ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::GenesisAppState { astria_core::generated::protocol::genesis::v1::GenesisAppState { authority_sudo_address: Some( - get_alice_signing_key() - .try_address(ASTRIA_PREFIX) + Address::builder() + .prefix(ASTRIA_PREFIX) + .array(get_alice_signing_key().address_bytes()) + .try_build() .unwrap() .to_raw(), ), ibc_sudo_address: Some( - get_alice_signing_key() - .try_address(ASTRIA_PREFIX) + Address::builder() + .prefix(ASTRIA_PREFIX) + .array(get_alice_signing_key().address_bytes()) + .try_build() .unwrap() .to_raw(), ), diff --git a/crates/astria-sequencer/src/authority/mod.rs b/crates/astria-sequencer/src/authority/mod.rs index 22d8a00784..16f5fb68c2 100644 --- a/crates/astria-sequencer/src/authority/mod.rs +++ b/crates/astria-sequencer/src/authority/mod.rs @@ -6,7 +6,7 @@ pub(crate) mod storage; use std::collections::BTreeMap; use astria_core::{ - primitive::v1::ADDRESS_LEN, + primitive::v1::ADDRESS_LENGTH, protocol::transaction::v1::action::ValidatorUpdate, }; use astria_eyre::eyre::{ @@ -23,10 +23,10 @@ use crate::accounts::AddressBytes; /// A map of public keys to validator updates. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(test, derive(Clone))] -pub(crate) struct ValidatorSet(BTreeMap<[u8; ADDRESS_LEN], ValidatorUpdate>); +pub(crate) struct ValidatorSet(BTreeMap<[u8; ADDRESS_LENGTH], ValidatorUpdate>); impl ValidatorSet { - pub(crate) fn new(inner: BTreeMap<[u8; ADDRESS_LEN], ValidatorUpdate>) -> Self { + pub(crate) fn new(inner: BTreeMap<[u8; ADDRESS_LENGTH], ValidatorUpdate>) -> Self { Self(inner) } diff --git a/crates/astria-sequencer/src/authority/state_ext.rs b/crates/astria-sequencer/src/authority/state_ext.rs index e55b81fcbf..a0b1c242cd 100644 --- a/crates/astria-sequencer/src/authority/state_ext.rs +++ b/crates/astria-sequencer/src/authority/state_ext.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use astria_core::primitive::v1::ADDRESS_LEN; +use astria_core::primitive::v1::ADDRESS_LENGTH; use astria_eyre::{ anyhow_to_eyre, eyre::{ @@ -31,7 +31,7 @@ use crate::{ #[async_trait] pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] - async fn get_sudo_address(&self) -> Result<[u8; ADDRESS_LEN]> { + async fn get_sudo_address(&self) -> Result<[u8; ADDRESS_LENGTH]> { let Some(bytes) = self .get_raw(keys::SUDO) .await @@ -42,7 +42,7 @@ pub(crate) trait StateReadExt: StateRead { bail!("sudo key not found"); }; StoredValue::deserialize(&bytes) - .and_then(|value| storage::AddressBytes::try_from(value).map(<[u8; ADDRESS_LEN]>::from)) + .and_then(|value| storage::AddressBytes::try_from(value).map(<[u8; ADDRESS_LENGTH]>::from)) .wrap_err("invalid sudo key bytes") } @@ -151,7 +151,7 @@ mod tests { .expect_err("no sudo address should exist at first"); // can write new - let mut address_expected = [42u8; ADDRESS_LEN]; + let mut address_expected = [42u8; ADDRESS_LENGTH]; state .put_sudo_address(address_expected) .expect("writing sudo address should not fail"); @@ -165,7 +165,7 @@ mod tests { ); // can rewrite with new value - address_expected = [41u8; ADDRESS_LEN]; + address_expected = [41u8; ADDRESS_LENGTH]; state .put_sudo_address(address_expected) .expect("writing sudo address should not fail"); diff --git a/crates/astria-sequencer/src/authority/storage/values.rs b/crates/astria-sequencer/src/authority/storage/values.rs index 1c2aaaa4a1..da4934ea10 100644 --- a/crates/astria-sequencer/src/authority/storage/values.rs +++ b/crates/astria-sequencer/src/authority/storage/values.rs @@ -9,7 +9,7 @@ use std::{ use astria_core::{ crypto::VerificationKey as DomainVerificationKey, - primitive::v1::ADDRESS_LEN, + primitive::v1::ADDRESS_LENGTH, protocol::transaction::v1::action::ValidatorUpdate as DomainValidatorUpdate, }; use astria_eyre::eyre::bail; @@ -34,7 +34,7 @@ enum ValueImpl<'a> { } #[derive(BorshSerialize, BorshDeserialize)] -pub(in crate::authority) struct AddressBytes<'a>(Cow<'a, [u8; ADDRESS_LEN]>); +pub(in crate::authority) struct AddressBytes<'a>(Cow<'a, [u8; ADDRESS_LENGTH]>); impl<'a> Debug for AddressBytes<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -48,7 +48,7 @@ impl<'a, T: DomainAddressBytes> From<&'a T> for AddressBytes<'a> { } } -impl<'a> From> for [u8; ADDRESS_LEN] { +impl<'a> From> for [u8; ADDRESS_LENGTH] { fn from(address_bytes: AddressBytes<'a>) -> Self { address_bytes.0.into_owned() } diff --git a/crates/astria-sequencer/src/bridge/state_ext.rs b/crates/astria-sequencer/src/bridge/state_ext.rs index 68bd6c2925..1911619b28 100644 --- a/crates/astria-sequencer/src/bridge/state_ext.rs +++ b/crates/astria-sequencer/src/bridge/state_ext.rs @@ -5,7 +5,7 @@ use astria_core::{ asset, RollupId, TransactionId, - ADDRESS_LEN, + ADDRESS_LENGTH, }, sequencerblock::v1::block::Deposit, }; @@ -90,7 +90,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { async fn get_bridge_account_sudo_address( &self, bridge_address: &T, - ) -> Result> { + ) -> Result> { let Some(bytes) = self .get_raw(&keys::bridge_account_sudo_address(bridge_address)) .await @@ -103,7 +103,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { StoredValue::deserialize(&bytes) .and_then(|value| { storage::AddressBytes::try_from(value).map(|stored_address_bytes| { - Some(<[u8; ADDRESS_LEN]>::from(stored_address_bytes)) + Some(<[u8; ADDRESS_LENGTH]>::from(stored_address_bytes)) }) }) .wrap_err("invalid bridge account sudo address bytes") @@ -113,7 +113,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { async fn get_bridge_account_withdrawer_address( &self, bridge_address: &T, - ) -> Result> { + ) -> Result> { let Some(bytes) = self .get_raw(&keys::bridge_account_withdrawer_address(bridge_address)) .await @@ -126,7 +126,7 @@ pub(crate) trait StateReadExt: StateRead + address::StateReadExt { StoredValue::deserialize(&bytes) .and_then(|value| { storage::AddressBytes::try_from(value).map(|stored_address_bytes| { - Some(<[u8; ADDRESS_LEN]>::from(stored_address_bytes)) + Some(<[u8; ADDRESS_LENGTH]>::from(stored_address_bytes)) }) }) .wrap_err("invalid bridge account withdrawer address bytes") diff --git a/crates/astria-sequencer/src/bridge/storage/values/address_bytes.rs b/crates/astria-sequencer/src/bridge/storage/values/address_bytes.rs index 73d1d016f0..a98fff2da5 100644 --- a/crates/astria-sequencer/src/bridge/storage/values/address_bytes.rs +++ b/crates/astria-sequencer/src/bridge/storage/values/address_bytes.rs @@ -7,7 +7,7 @@ use std::{ }, }; -use astria_core::primitive::v1::ADDRESS_LEN; +use astria_core::primitive::v1::ADDRESS_LENGTH; use astria_eyre::eyre::bail; use borsh::{ BorshDeserialize, @@ -22,7 +22,7 @@ use super::{ use crate::accounts::AddressBytes as DomainAddressBytes; #[derive(BorshSerialize, BorshDeserialize)] -pub(in crate::bridge) struct AddressBytes<'a>(Cow<'a, [u8; ADDRESS_LEN]>); +pub(in crate::bridge) struct AddressBytes<'a>(Cow<'a, [u8; ADDRESS_LENGTH]>); impl<'a> Debug for AddressBytes<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -36,7 +36,7 @@ impl<'a, T: DomainAddressBytes> From<&'a T> for AddressBytes<'a> { } } -impl<'a> From> for [u8; ADDRESS_LEN] { +impl<'a> From> for [u8; ADDRESS_LENGTH] { fn from(address_bytes: AddressBytes<'a>) -> Self { address_bytes.0.into_owned() } diff --git a/crates/astria-sequencer/src/fees/tests.rs b/crates/astria-sequencer/src/fees/tests.rs index f486683a9e..c7d7894bf9 100644 --- a/crates/astria-sequencer/src/fees/tests.rs +++ b/crates/astria-sequencer/src/fees/tests.rs @@ -6,7 +6,7 @@ use astria_core::{ Address, RollupId, TransactionId, - ADDRESS_LEN, + ADDRESS_LENGTH, ROLLUP_ID_LEN, TRANSACTION_ID_LEN, }, @@ -30,6 +30,7 @@ use astria_core::{ }, }, sequencerblock::v1::block::Deposit, + Protobuf as _, }; use cnidarium::StateDelta; @@ -441,7 +442,7 @@ fn get_base_deposit_fee() { use prost::Message as _; let bridge_address = Address::builder() .prefix("astria-bridge") - .slice(&[0u8; ADDRESS_LEN][..]) + .slice(&[0u8; ADDRESS_LENGTH][..]) .try_build() .unwrap(); let raw_deposit = astria_core::generated::sequencerblock::v1::Deposit { diff --git a/crates/astria-sequencer/src/grpc/sequencer.rs b/crates/astria-sequencer/src/grpc/sequencer.rs index 7d74f077f6..2a87c73182 100644 --- a/crates/astria-sequencer/src/grpc/sequencer.rs +++ b/crates/astria-sequencer/src/grpc/sequencer.rs @@ -188,7 +188,7 @@ impl SequencerService for SequencerServer { )); }; - let address = Address::try_from_raw(&address).map_err(|e| { + let address = Address::try_from_raw(address).map_err(|e| { info!( error = %e, "failed to parse address from request", diff --git a/crates/astria-sequencer/src/grpc/storage/values/sequencer_block_header.rs b/crates/astria-sequencer/src/grpc/storage/values/sequencer_block_header.rs index 0c53040cca..d523bf63f1 100644 --- a/crates/astria-sequencer/src/grpc/storage/values/sequencer_block_header.rs +++ b/crates/astria-sequencer/src/grpc/storage/values/sequencer_block_header.rs @@ -8,7 +8,7 @@ use std::{ }; use astria_core::{ - primitive::v1::ADDRESS_LEN, + primitive::v1::ADDRESS_LENGTH, sequencerblock::v1::block::{ SequencerBlockHeader as DomainSequencerBlockHeader, SequencerBlockHeaderParts, @@ -103,7 +103,7 @@ pub(in crate::grpc) struct SequencerBlockHeader<'a> { time: BlockTimestamp, rollup_transactions_root: Cow<'a, [u8; 32]>, data_hash: Cow<'a, [u8; 32]>, - proposer_address: [u8; ADDRESS_LEN], + proposer_address: [u8; ADDRESS_LENGTH], } impl<'a> Debug for SequencerBlockHeader<'a> { @@ -127,8 +127,8 @@ impl<'a> Debug for SequencerBlockHeader<'a> { impl<'a> From<&'a DomainSequencerBlockHeader> for SequencerBlockHeader<'a> { fn from(header: &'a DomainSequencerBlockHeader) -> Self { - const _: () = assert!(ADDRESS_LEN == tendermint::account::LENGTH); - let mut proposer_address = [0; ADDRESS_LEN]; + const _: () = assert!(ADDRESS_LENGTH == tendermint::account::LENGTH); + let mut proposer_address = [0; ADDRESS_LENGTH]; proposer_address.copy_from_slice(header.proposer_address().as_bytes()); SequencerBlockHeader { diff --git a/crates/astria-sequencer/src/ibc/state_ext.rs b/crates/astria-sequencer/src/ibc/state_ext.rs index 3307e5b2e8..8c9a207c27 100644 --- a/crates/astria-sequencer/src/ibc/state_ext.rs +++ b/crates/astria-sequencer/src/ibc/state_ext.rs @@ -5,7 +5,7 @@ use std::{ use astria_core::primitive::v1::{ asset, - ADDRESS_LEN, + ADDRESS_LENGTH, }; use astria_eyre::{ anyhow_to_eyre, @@ -63,7 +63,7 @@ pub(crate) trait StateReadExt: StateRead { } #[instrument(skip_all)] - async fn get_ibc_sudo_address(&self) -> Result<[u8; ADDRESS_LEN]> { + async fn get_ibc_sudo_address(&self) -> Result<[u8; ADDRESS_LENGTH]> { let Some(bytes) = self .get_raw(keys::IBC_SUDO) .await @@ -74,7 +74,7 @@ pub(crate) trait StateReadExt: StateRead { bail!("ibc sudo address not found"); }; StoredValue::deserialize(&bytes) - .and_then(|value| storage::AddressBytes::try_from(value).map(<[u8; ADDRESS_LEN]>::from)) + .and_then(|value| storage::AddressBytes::try_from(value).map(<[u8; ADDRESS_LENGTH]>::from)) .wrap_err("invalid ibc sudo address bytes") } diff --git a/crates/astria-sequencer/src/ibc/storage/values.rs b/crates/astria-sequencer/src/ibc/storage/values.rs index f1a0783f89..cb776a3449 100644 --- a/crates/astria-sequencer/src/ibc/storage/values.rs +++ b/crates/astria-sequencer/src/ibc/storage/values.rs @@ -7,7 +7,7 @@ use std::{ }, }; -use astria_core::primitive::v1::ADDRESS_LEN; +use astria_core::primitive::v1::ADDRESS_LENGTH; use astria_eyre::eyre::bail; use borsh::{ BorshDeserialize, @@ -59,7 +59,7 @@ impl<'a> TryFrom> for Balance { } #[derive(BorshSerialize, BorshDeserialize)] -pub(in crate::ibc) struct AddressBytes<'a>(Cow<'a, [u8; ADDRESS_LEN]>); +pub(in crate::ibc) struct AddressBytes<'a>(Cow<'a, [u8; ADDRESS_LENGTH]>); impl<'a> Debug for AddressBytes<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -73,7 +73,7 @@ impl<'a, T: DomainAddressBytes> From<&'a T> for AddressBytes<'a> { } } -impl<'a> From> for [u8; ADDRESS_LEN] { +impl<'a> From> for [u8; ADDRESS_LENGTH] { fn from(address_bytes: AddressBytes<'a>) -> Self { address_bytes.0.into_owned() } diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index 3e00c7b0f4..8ab713deb9 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -129,7 +129,7 @@ mod tests { primitive::v1::{ asset::Denom, RollupId, - ADDRESS_LEN, + ADDRESS_LENGTH, }, protocol::{ fees::v1::{ @@ -285,7 +285,7 @@ mod tests { asset: other_asset.clone(), amount, fee_asset: nria().into(), - to: state_tx.try_base_prefixed(&[0; ADDRESS_LEN]).await.unwrap(), + to: state_tx.try_base_prefixed(&[0; ADDRESS_LENGTH]).await.unwrap(), }), Action::RollupDataSubmission(RollupDataSubmission { rollup_id: RollupId::from_unhashed_bytes([0; 32]), @@ -407,7 +407,7 @@ mod tests { asset: other_asset.clone(), amount, fee_asset: nria().into(), - to: state_tx.try_base_prefixed(&[0; ADDRESS_LEN]).await.unwrap(), + to: state_tx.try_base_prefixed(&[0; ADDRESS_LENGTH]).await.unwrap(), }), Action::RollupDataSubmission(RollupDataSubmission { rollup_id: RollupId::from_unhashed_bytes([0; 32]), diff --git a/crates/astria-sequencer/src/transaction/state_ext.rs b/crates/astria-sequencer/src/transaction/state_ext.rs index 2dbfed621c..807516f6ea 100644 --- a/crates/astria-sequencer/src/transaction/state_ext.rs +++ b/crates/astria-sequencer/src/transaction/state_ext.rs @@ -1,7 +1,7 @@ use astria_core::{ primitive::v1::{ TransactionId, - ADDRESS_LEN, + ADDRESS_LENGTH, }, protocol::transaction::v1::Transaction, }; @@ -16,13 +16,13 @@ fn transaction_context() -> &'static str { #[derive(Clone, Copy)] pub(crate) struct TransactionContext { - pub(crate) address_bytes: [u8; ADDRESS_LEN], + pub(crate) address_bytes: [u8; ADDRESS_LENGTH], pub(crate) transaction_id: TransactionId, pub(crate) source_action_index: u64, } impl TransactionContext { - pub(crate) fn address_bytes(self) -> [u8; ADDRESS_LEN] { + pub(crate) fn address_bytes(self) -> [u8; ADDRESS_LENGTH] { self.address_bytes } }