diff --git a/charts/sequencer/Chart.yaml b/charts/sequencer/Chart.yaml
index bb8d4fc527..577becef95 100644
--- a/charts/sequencer/Chart.yaml
+++ b/charts/sequencer/Chart.yaml
@@ -15,7 +15,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 0.19.1
+version: 0.20.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json
index 4e8e7a4ff4..9e266902c2 100644
--- a/charts/sequencer/files/cometbft/config/genesis.json
+++ b/charts/sequencer/files/cometbft/config/genesis.json
@@ -3,13 +3,13 @@
"app_state": {
"native_asset_base_denomination": "{{ .Values.genesis.nativeAssetBaseDenomination }}",
"fees": {
- "transfer_base_fee": {{ .Values.genesis.fees.transferBaseFee }},
- "sequence_base_fee": {{ .Values.genesis.fees.sequenceBaseFee }},
- "sequence_byte_cost_multiplier": {{ .Values.genesis.fees.sequenceByteCostMultiplier }},
- "init_bridge_account_base_fee": {{ .Values.genesis.fees.initBridgeAccountBaseFee }},
- "bridge_lock_byte_cost_multiplier": {{ .Values.genesis.fees.bridgeLockByteCostMultiplier }},
- "bridge_sudo_change_fee": {{ .Values.genesis.fees.bridgeSudoChangeFee }},
- "ics20_withdrawal_base_fee": {{ .Values.genesis.fees.ics20WithdrawalBaseFee }}
+ "transfer_base_fee": {{ include "sequencer.toUint128Proto" .Values.genesis.fees.transferBaseFee }},
+ "sequence_base_fee": {{ include "sequencer.toUint128Proto" .Values.genesis.fees.sequenceBaseFee }},
+ "sequence_byte_cost_multiplier": {{ include "sequencer.toUint128Proto" .Values.genesis.fees.sequenceByteCostMultiplier }},
+ "init_bridge_account_base_fee": {{ include "sequencer.toUint128Proto" .Values.genesis.fees.initBridgeAccountBaseFee }},
+ "bridge_lock_byte_cost_multiplier": {{ include "sequencer.toUint128Proto" .Values.genesis.fees.bridgeLockByteCostMultiplier }},
+ "bridge_sudo_change_fee": {{ include "sequencer.toUint128Proto" .Values.genesis.fees.bridgeSudoChangeFee }},
+ "ics20_withdrawal_base_fee": {{ include "sequencer.toUint128Proto" .Values.genesis.fees.ics20WithdrawalBaseFee }}
},
"allowed_fee_assets": [
{{- range $index, $value := .Values.genesis.allowedFeeAssets }}
@@ -17,7 +17,7 @@
"{{ $value }}"
{{- end }}
],
- "ibc_params": {
+ "ibc_parameters": {
"ibc_enabled": {{ .Values.genesis.ibc.enabled }},
"inbound_ics20_transfers_enabled": {{ .Values.genesis.ibc.inboundEnabled }},
"outbound_ics20_transfers_enabled": {{ .Values.genesis.ibc.outboundEnabled }}
@@ -30,7 +30,7 @@
{{- if $index }},{{- end }}
{
"address": {{ include "sequencer.address" $value.address }},
- "balance": {{ toString $value.balance | replace "\"" "" }}
+ "balance": {{ include "sequencer.toUint128Proto" ( toString $value.balance | replace "\"" "" ) }}
}
{{- end }}
],
diff --git a/charts/sequencer/templates/_helpers.tpl b/charts/sequencer/templates/_helpers.tpl
index bab0a2380a..2dc1da25ab 100644
--- a/charts/sequencer/templates/_helpers.tpl
+++ b/charts/sequencer/templates/_helpers.tpl
@@ -71,3 +71,7 @@ name: {{ .Values.moniker }}-sequencer-metrics
{{/* New sequencer address */}}
{{- define "sequencer.address"}}{ "bech32m": "{{ . }}" }
{{- end }}
+
+{{/* uint64 fee converted to a astria proto Uint128 with only lo set */}}
+{{- define "sequencer.toUint128Proto"}}{ "lo": {{ . }} }
+{{- end }}
diff --git a/crates/astria-core/Cargo.toml b/crates/astria-core/Cargo.toml
index 049919b07a..3c40345d58 100644
--- a/crates/astria-core/Cargo.toml
+++ b/crates/astria-core/Cargo.toml
@@ -57,7 +57,7 @@ base64-serde = ["dep:base64-serde"]
brotli = ["dep:brotli"]
[dev-dependencies]
+astria-core = { path = ".", features = ["serde"] }
insta = { workspace = true, features = ["json"] }
rand = { workspace = true }
tempfile = { workspace = true }
-astria-core = { path = ".", features = ["serde"] }
diff --git a/crates/astria-core/src/crypto.rs b/crates/astria-core/src/crypto.rs
index 69f017fb78..77da8f7b80 100644
--- a/crates/astria-core/src/crypto.rs
+++ b/crates/astria-core/src/crypto.rs
@@ -36,7 +36,11 @@ use zeroize::{
ZeroizeOnDrop,
};
-use crate::primitive::v1::ADDRESS_LEN;
+use crate::primitive::v1::{
+ Address,
+ AddressError,
+ ADDRESS_LEN,
+};
/// An Ed25519 signing key.
// *Implementation note*: this is currently a refinement type around
@@ -82,6 +86,19 @@ impl SigningKey {
pub fn address_bytes(&self) -> [u8; ADDRESS_LEN] {
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 {
diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs
new file mode 100644
index 0000000000..17ccce5d82
--- /dev/null
+++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs
@@ -0,0 +1,126 @@
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct GenesisAppState {
+ #[prost(string, tag = "1")]
+ pub chain_id: ::prost::alloc::string::String,
+ #[prost(message, optional, tag = "2")]
+ pub address_prefixes: ::core::option::Option,
+ #[prost(message, repeated, tag = "3")]
+ pub accounts: ::prost::alloc::vec::Vec,
+ #[prost(message, optional, tag = "4")]
+ pub authority_sudo_address: ::core::option::Option<
+ super::super::super::primitive::v1::Address,
+ >,
+ #[prost(message, optional, tag = "5")]
+ pub ibc_sudo_address: ::core::option::Option<
+ super::super::super::primitive::v1::Address,
+ >,
+ #[prost(message, repeated, tag = "6")]
+ pub ibc_relayer_addresses: ::prost::alloc::vec::Vec<
+ super::super::super::primitive::v1::Address,
+ >,
+ #[prost(string, tag = "7")]
+ pub native_asset_base_denomination: ::prost::alloc::string::String,
+ #[prost(message, optional, tag = "8")]
+ pub ibc_parameters: ::core::option::Option,
+ #[prost(string, repeated, tag = "9")]
+ pub allowed_fee_assets: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
+ #[prost(message, optional, tag = "10")]
+ pub fees: ::core::option::Option,
+}
+impl ::prost::Name for GenesisAppState {
+ const NAME: &'static str = "GenesisAppState";
+ const PACKAGE: &'static str = "astria.protocol.genesis.v1alpha1";
+ fn full_name() -> ::prost::alloc::string::String {
+ ::prost::alloc::format!("astria.protocol.genesis.v1alpha1.{}", Self::NAME)
+ }
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct Account {
+ #[prost(message, optional, tag = "1")]
+ pub address: ::core::option::Option,
+ #[prost(message, optional, tag = "2")]
+ pub balance: ::core::option::Option,
+}
+impl ::prost::Name for Account {
+ const NAME: &'static str = "Account";
+ const PACKAGE: &'static str = "astria.protocol.genesis.v1alpha1";
+ fn full_name() -> ::prost::alloc::string::String {
+ ::prost::alloc::format!("astria.protocol.genesis.v1alpha1.{}", Self::NAME)
+ }
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct AddressPrefixes {
+ #[prost(string, tag = "1")]
+ pub base: ::prost::alloc::string::String,
+}
+impl ::prost::Name for AddressPrefixes {
+ const NAME: &'static str = "AddressPrefixes";
+ const PACKAGE: &'static str = "astria.protocol.genesis.v1alpha1";
+ fn full_name() -> ::prost::alloc::string::String {
+ ::prost::alloc::format!("astria.protocol.genesis.v1alpha1.{}", Self::NAME)
+ }
+}
+/// IBC configuration data.
+#[derive(Copy)]
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct IbcParameters {
+ /// Whether IBC (forming connections, processing IBC packets) is enabled.
+ #[prost(bool, tag = "1")]
+ pub ibc_enabled: bool,
+ /// Whether inbound ICS-20 transfers are enabled
+ #[prost(bool, tag = "2")]
+ pub inbound_ics20_transfers_enabled: bool,
+ /// Whether outbound ICS-20 transfers are enabled
+ #[prost(bool, tag = "3")]
+ pub outbound_ics20_transfers_enabled: bool,
+}
+impl ::prost::Name for IbcParameters {
+ const NAME: &'static str = "IbcParameters";
+ const PACKAGE: &'static str = "astria.protocol.genesis.v1alpha1";
+ fn full_name() -> ::prost::alloc::string::String {
+ ::prost::alloc::format!("astria.protocol.genesis.v1alpha1.{}", Self::NAME)
+ }
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct Fees {
+ #[prost(message, optional, tag = "1")]
+ pub transfer_base_fee: ::core::option::Option<
+ super::super::super::primitive::v1::Uint128,
+ >,
+ #[prost(message, optional, tag = "2")]
+ pub sequence_base_fee: ::core::option::Option<
+ super::super::super::primitive::v1::Uint128,
+ >,
+ #[prost(message, optional, tag = "3")]
+ pub sequence_byte_cost_multiplier: ::core::option::Option<
+ super::super::super::primitive::v1::Uint128,
+ >,
+ #[prost(message, optional, tag = "4")]
+ pub init_bridge_account_base_fee: ::core::option::Option<
+ super::super::super::primitive::v1::Uint128,
+ >,
+ #[prost(message, optional, tag = "5")]
+ pub bridge_lock_byte_cost_multiplier: ::core::option::Option<
+ super::super::super::primitive::v1::Uint128,
+ >,
+ #[prost(message, optional, tag = "6")]
+ pub bridge_sudo_change_fee: ::core::option::Option<
+ super::super::super::primitive::v1::Uint128,
+ >,
+ #[prost(message, optional, tag = "7")]
+ pub ics20_withdrawal_base_fee: ::core::option::Option<
+ super::super::super::primitive::v1::Uint128,
+ >,
+}
+impl ::prost::Name for Fees {
+ const NAME: &'static str = "Fees";
+ const PACKAGE: &'static str = "astria.protocol.genesis.v1alpha1";
+ fn full_name() -> ::prost::alloc::string::String {
+ ::prost::alloc::format!("astria.protocol.genesis.v1alpha1.{}", Self::NAME)
+ }
+}
diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs
new file mode 100644
index 0000000000..4636c57237
--- /dev/null
+++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs
@@ -0,0 +1,779 @@
+impl serde::Serialize for Account {
+ #[allow(deprecated)]
+ fn serialize(&self, serializer: S) -> std::result::Result
+ where
+ S: serde::Serializer,
+ {
+ use serde::ser::SerializeStruct;
+ let mut len = 0;
+ if self.address.is_some() {
+ len += 1;
+ }
+ if self.balance.is_some() {
+ len += 1;
+ }
+ let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.Account", len)?;
+ if let Some(v) = self.address.as_ref() {
+ struct_ser.serialize_field("address", v)?;
+ }
+ if let Some(v) = self.balance.as_ref() {
+ struct_ser.serialize_field("balance", v)?;
+ }
+ struct_ser.end()
+ }
+}
+impl<'de> serde::Deserialize<'de> for Account {
+ #[allow(deprecated)]
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ const FIELDS: &[&str] = &[
+ "address",
+ "balance",
+ ];
+
+ #[allow(clippy::enum_variant_names)]
+ enum GeneratedField {
+ Address,
+ Balance,
+ }
+ impl<'de> serde::Deserialize<'de> for GeneratedField {
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct GeneratedVisitor;
+
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = GeneratedField;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(formatter, "expected one of: {:?}", &FIELDS)
+ }
+
+ #[allow(unused_variables)]
+ fn visit_str(self, value: &str) -> std::result::Result
+ where
+ E: serde::de::Error,
+ {
+ match value {
+ "address" => Ok(GeneratedField::Address),
+ "balance" => Ok(GeneratedField::Balance),
+ _ => Err(serde::de::Error::unknown_field(value, FIELDS)),
+ }
+ }
+ }
+ deserializer.deserialize_identifier(GeneratedVisitor)
+ }
+ }
+ struct GeneratedVisitor;
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = Account;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ formatter.write_str("struct astria.protocol.genesis.v1alpha1.Account")
+ }
+
+ fn visit_map(self, mut map_: V) -> std::result::Result
+ where
+ V: serde::de::MapAccess<'de>,
+ {
+ let mut address__ = None;
+ let mut balance__ = None;
+ while let Some(k) = map_.next_key()? {
+ match k {
+ GeneratedField::Address => {
+ if address__.is_some() {
+ return Err(serde::de::Error::duplicate_field("address"));
+ }
+ address__ = map_.next_value()?;
+ }
+ GeneratedField::Balance => {
+ if balance__.is_some() {
+ return Err(serde::de::Error::duplicate_field("balance"));
+ }
+ balance__ = map_.next_value()?;
+ }
+ }
+ }
+ Ok(Account {
+ address: address__,
+ balance: balance__,
+ })
+ }
+ }
+ deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.Account", FIELDS, GeneratedVisitor)
+ }
+}
+impl serde::Serialize for AddressPrefixes {
+ #[allow(deprecated)]
+ fn serialize(&self, serializer: S) -> std::result::Result
+ where
+ S: serde::Serializer,
+ {
+ use serde::ser::SerializeStruct;
+ let mut len = 0;
+ if !self.base.is_empty() {
+ len += 1;
+ }
+ let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.AddressPrefixes", len)?;
+ if !self.base.is_empty() {
+ struct_ser.serialize_field("base", &self.base)?;
+ }
+ struct_ser.end()
+ }
+}
+impl<'de> serde::Deserialize<'de> for AddressPrefixes {
+ #[allow(deprecated)]
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ const FIELDS: &[&str] = &[
+ "base",
+ ];
+
+ #[allow(clippy::enum_variant_names)]
+ enum GeneratedField {
+ Base,
+ }
+ impl<'de> serde::Deserialize<'de> for GeneratedField {
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct GeneratedVisitor;
+
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = GeneratedField;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(formatter, "expected one of: {:?}", &FIELDS)
+ }
+
+ #[allow(unused_variables)]
+ fn visit_str(self, value: &str) -> std::result::Result
+ where
+ E: serde::de::Error,
+ {
+ match value {
+ "base" => Ok(GeneratedField::Base),
+ _ => Err(serde::de::Error::unknown_field(value, FIELDS)),
+ }
+ }
+ }
+ deserializer.deserialize_identifier(GeneratedVisitor)
+ }
+ }
+ struct GeneratedVisitor;
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = AddressPrefixes;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ formatter.write_str("struct astria.protocol.genesis.v1alpha1.AddressPrefixes")
+ }
+
+ fn visit_map(self, mut map_: V) -> std::result::Result
+ where
+ V: serde::de::MapAccess<'de>,
+ {
+ let mut base__ = None;
+ while let Some(k) = map_.next_key()? {
+ match k {
+ GeneratedField::Base => {
+ if base__.is_some() {
+ return Err(serde::de::Error::duplicate_field("base"));
+ }
+ base__ = Some(map_.next_value()?);
+ }
+ }
+ }
+ Ok(AddressPrefixes {
+ base: base__.unwrap_or_default(),
+ })
+ }
+ }
+ deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.AddressPrefixes", FIELDS, GeneratedVisitor)
+ }
+}
+impl serde::Serialize for Fees {
+ #[allow(deprecated)]
+ fn serialize(&self, serializer: S) -> std::result::Result
+ where
+ S: serde::Serializer,
+ {
+ use serde::ser::SerializeStruct;
+ let mut len = 0;
+ if self.transfer_base_fee.is_some() {
+ len += 1;
+ }
+ if self.sequence_base_fee.is_some() {
+ len += 1;
+ }
+ if self.sequence_byte_cost_multiplier.is_some() {
+ len += 1;
+ }
+ if self.init_bridge_account_base_fee.is_some() {
+ len += 1;
+ }
+ if self.bridge_lock_byte_cost_multiplier.is_some() {
+ len += 1;
+ }
+ if self.bridge_sudo_change_fee.is_some() {
+ len += 1;
+ }
+ if self.ics20_withdrawal_base_fee.is_some() {
+ len += 1;
+ }
+ let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.Fees", len)?;
+ if let Some(v) = self.transfer_base_fee.as_ref() {
+ struct_ser.serialize_field("transferBaseFee", v)?;
+ }
+ if let Some(v) = self.sequence_base_fee.as_ref() {
+ struct_ser.serialize_field("sequenceBaseFee", v)?;
+ }
+ if let Some(v) = self.sequence_byte_cost_multiplier.as_ref() {
+ struct_ser.serialize_field("sequenceByteCostMultiplier", v)?;
+ }
+ if let Some(v) = self.init_bridge_account_base_fee.as_ref() {
+ struct_ser.serialize_field("initBridgeAccountBaseFee", v)?;
+ }
+ if let Some(v) = self.bridge_lock_byte_cost_multiplier.as_ref() {
+ struct_ser.serialize_field("bridgeLockByteCostMultiplier", v)?;
+ }
+ if let Some(v) = self.bridge_sudo_change_fee.as_ref() {
+ struct_ser.serialize_field("bridgeSudoChangeFee", v)?;
+ }
+ if let Some(v) = self.ics20_withdrawal_base_fee.as_ref() {
+ struct_ser.serialize_field("ics20WithdrawalBaseFee", v)?;
+ }
+ struct_ser.end()
+ }
+}
+impl<'de> serde::Deserialize<'de> for Fees {
+ #[allow(deprecated)]
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ const FIELDS: &[&str] = &[
+ "transfer_base_fee",
+ "transferBaseFee",
+ "sequence_base_fee",
+ "sequenceBaseFee",
+ "sequence_byte_cost_multiplier",
+ "sequenceByteCostMultiplier",
+ "init_bridge_account_base_fee",
+ "initBridgeAccountBaseFee",
+ "bridge_lock_byte_cost_multiplier",
+ "bridgeLockByteCostMultiplier",
+ "bridge_sudo_change_fee",
+ "bridgeSudoChangeFee",
+ "ics20_withdrawal_base_fee",
+ "ics20WithdrawalBaseFee",
+ ];
+
+ #[allow(clippy::enum_variant_names)]
+ enum GeneratedField {
+ TransferBaseFee,
+ SequenceBaseFee,
+ SequenceByteCostMultiplier,
+ InitBridgeAccountBaseFee,
+ BridgeLockByteCostMultiplier,
+ BridgeSudoChangeFee,
+ Ics20WithdrawalBaseFee,
+ }
+ impl<'de> serde::Deserialize<'de> for GeneratedField {
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct GeneratedVisitor;
+
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = GeneratedField;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(formatter, "expected one of: {:?}", &FIELDS)
+ }
+
+ #[allow(unused_variables)]
+ fn visit_str(self, value: &str) -> std::result::Result
+ where
+ E: serde::de::Error,
+ {
+ match value {
+ "transferBaseFee" | "transfer_base_fee" => Ok(GeneratedField::TransferBaseFee),
+ "sequenceBaseFee" | "sequence_base_fee" => Ok(GeneratedField::SequenceBaseFee),
+ "sequenceByteCostMultiplier" | "sequence_byte_cost_multiplier" => Ok(GeneratedField::SequenceByteCostMultiplier),
+ "initBridgeAccountBaseFee" | "init_bridge_account_base_fee" => Ok(GeneratedField::InitBridgeAccountBaseFee),
+ "bridgeLockByteCostMultiplier" | "bridge_lock_byte_cost_multiplier" => Ok(GeneratedField::BridgeLockByteCostMultiplier),
+ "bridgeSudoChangeFee" | "bridge_sudo_change_fee" => Ok(GeneratedField::BridgeSudoChangeFee),
+ "ics20WithdrawalBaseFee" | "ics20_withdrawal_base_fee" => Ok(GeneratedField::Ics20WithdrawalBaseFee),
+ _ => Err(serde::de::Error::unknown_field(value, FIELDS)),
+ }
+ }
+ }
+ deserializer.deserialize_identifier(GeneratedVisitor)
+ }
+ }
+ struct GeneratedVisitor;
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = Fees;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ formatter.write_str("struct astria.protocol.genesis.v1alpha1.Fees")
+ }
+
+ fn visit_map(self, mut map_: V) -> std::result::Result
+ where
+ V: serde::de::MapAccess<'de>,
+ {
+ let mut transfer_base_fee__ = None;
+ let mut sequence_base_fee__ = None;
+ let mut sequence_byte_cost_multiplier__ = None;
+ let mut init_bridge_account_base_fee__ = None;
+ let mut bridge_lock_byte_cost_multiplier__ = None;
+ let mut bridge_sudo_change_fee__ = None;
+ let mut ics20_withdrawal_base_fee__ = None;
+ while let Some(k) = map_.next_key()? {
+ match k {
+ GeneratedField::TransferBaseFee => {
+ if transfer_base_fee__.is_some() {
+ return Err(serde::de::Error::duplicate_field("transferBaseFee"));
+ }
+ transfer_base_fee__ = map_.next_value()?;
+ }
+ GeneratedField::SequenceBaseFee => {
+ if sequence_base_fee__.is_some() {
+ return Err(serde::de::Error::duplicate_field("sequenceBaseFee"));
+ }
+ sequence_base_fee__ = map_.next_value()?;
+ }
+ GeneratedField::SequenceByteCostMultiplier => {
+ if sequence_byte_cost_multiplier__.is_some() {
+ return Err(serde::de::Error::duplicate_field("sequenceByteCostMultiplier"));
+ }
+ sequence_byte_cost_multiplier__ = map_.next_value()?;
+ }
+ GeneratedField::InitBridgeAccountBaseFee => {
+ if init_bridge_account_base_fee__.is_some() {
+ return Err(serde::de::Error::duplicate_field("initBridgeAccountBaseFee"));
+ }
+ init_bridge_account_base_fee__ = map_.next_value()?;
+ }
+ GeneratedField::BridgeLockByteCostMultiplier => {
+ if bridge_lock_byte_cost_multiplier__.is_some() {
+ return Err(serde::de::Error::duplicate_field("bridgeLockByteCostMultiplier"));
+ }
+ bridge_lock_byte_cost_multiplier__ = map_.next_value()?;
+ }
+ GeneratedField::BridgeSudoChangeFee => {
+ if bridge_sudo_change_fee__.is_some() {
+ return Err(serde::de::Error::duplicate_field("bridgeSudoChangeFee"));
+ }
+ bridge_sudo_change_fee__ = map_.next_value()?;
+ }
+ GeneratedField::Ics20WithdrawalBaseFee => {
+ if ics20_withdrawal_base_fee__.is_some() {
+ return Err(serde::de::Error::duplicate_field("ics20WithdrawalBaseFee"));
+ }
+ ics20_withdrawal_base_fee__ = map_.next_value()?;
+ }
+ }
+ }
+ Ok(Fees {
+ transfer_base_fee: transfer_base_fee__,
+ sequence_base_fee: sequence_base_fee__,
+ sequence_byte_cost_multiplier: sequence_byte_cost_multiplier__,
+ init_bridge_account_base_fee: init_bridge_account_base_fee__,
+ bridge_lock_byte_cost_multiplier: bridge_lock_byte_cost_multiplier__,
+ bridge_sudo_change_fee: bridge_sudo_change_fee__,
+ ics20_withdrawal_base_fee: ics20_withdrawal_base_fee__,
+ })
+ }
+ }
+ deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.Fees", FIELDS, GeneratedVisitor)
+ }
+}
+impl serde::Serialize for GenesisAppState {
+ #[allow(deprecated)]
+ fn serialize(&self, serializer: S) -> std::result::Result
+ where
+ S: serde::Serializer,
+ {
+ use serde::ser::SerializeStruct;
+ let mut len = 0;
+ if !self.chain_id.is_empty() {
+ len += 1;
+ }
+ if self.address_prefixes.is_some() {
+ len += 1;
+ }
+ if !self.accounts.is_empty() {
+ len += 1;
+ }
+ if self.authority_sudo_address.is_some() {
+ len += 1;
+ }
+ if self.ibc_sudo_address.is_some() {
+ len += 1;
+ }
+ if !self.ibc_relayer_addresses.is_empty() {
+ len += 1;
+ }
+ if !self.native_asset_base_denomination.is_empty() {
+ len += 1;
+ }
+ if self.ibc_parameters.is_some() {
+ len += 1;
+ }
+ if !self.allowed_fee_assets.is_empty() {
+ len += 1;
+ }
+ if self.fees.is_some() {
+ len += 1;
+ }
+ let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.GenesisAppState", len)?;
+ if !self.chain_id.is_empty() {
+ struct_ser.serialize_field("chainId", &self.chain_id)?;
+ }
+ if let Some(v) = self.address_prefixes.as_ref() {
+ struct_ser.serialize_field("addressPrefixes", v)?;
+ }
+ if !self.accounts.is_empty() {
+ struct_ser.serialize_field("accounts", &self.accounts)?;
+ }
+ if let Some(v) = self.authority_sudo_address.as_ref() {
+ struct_ser.serialize_field("authoritySudoAddress", v)?;
+ }
+ if let Some(v) = self.ibc_sudo_address.as_ref() {
+ struct_ser.serialize_field("ibcSudoAddress", v)?;
+ }
+ if !self.ibc_relayer_addresses.is_empty() {
+ struct_ser.serialize_field("ibcRelayerAddresses", &self.ibc_relayer_addresses)?;
+ }
+ if !self.native_asset_base_denomination.is_empty() {
+ struct_ser.serialize_field("nativeAssetBaseDenomination", &self.native_asset_base_denomination)?;
+ }
+ if let Some(v) = self.ibc_parameters.as_ref() {
+ struct_ser.serialize_field("ibcParameters", v)?;
+ }
+ if !self.allowed_fee_assets.is_empty() {
+ struct_ser.serialize_field("allowedFeeAssets", &self.allowed_fee_assets)?;
+ }
+ if let Some(v) = self.fees.as_ref() {
+ struct_ser.serialize_field("fees", v)?;
+ }
+ struct_ser.end()
+ }
+}
+impl<'de> serde::Deserialize<'de> for GenesisAppState {
+ #[allow(deprecated)]
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ const FIELDS: &[&str] = &[
+ "chain_id",
+ "chainId",
+ "address_prefixes",
+ "addressPrefixes",
+ "accounts",
+ "authority_sudo_address",
+ "authoritySudoAddress",
+ "ibc_sudo_address",
+ "ibcSudoAddress",
+ "ibc_relayer_addresses",
+ "ibcRelayerAddresses",
+ "native_asset_base_denomination",
+ "nativeAssetBaseDenomination",
+ "ibc_parameters",
+ "ibcParameters",
+ "allowed_fee_assets",
+ "allowedFeeAssets",
+ "fees",
+ ];
+
+ #[allow(clippy::enum_variant_names)]
+ enum GeneratedField {
+ ChainId,
+ AddressPrefixes,
+ Accounts,
+ AuthoritySudoAddress,
+ IbcSudoAddress,
+ IbcRelayerAddresses,
+ NativeAssetBaseDenomination,
+ IbcParameters,
+ AllowedFeeAssets,
+ Fees,
+ }
+ impl<'de> serde::Deserialize<'de> for GeneratedField {
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct GeneratedVisitor;
+
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = GeneratedField;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(formatter, "expected one of: {:?}", &FIELDS)
+ }
+
+ #[allow(unused_variables)]
+ fn visit_str(self, value: &str) -> std::result::Result
+ where
+ E: serde::de::Error,
+ {
+ match value {
+ "chainId" | "chain_id" => Ok(GeneratedField::ChainId),
+ "addressPrefixes" | "address_prefixes" => Ok(GeneratedField::AddressPrefixes),
+ "accounts" => Ok(GeneratedField::Accounts),
+ "authoritySudoAddress" | "authority_sudo_address" => Ok(GeneratedField::AuthoritySudoAddress),
+ "ibcSudoAddress" | "ibc_sudo_address" => Ok(GeneratedField::IbcSudoAddress),
+ "ibcRelayerAddresses" | "ibc_relayer_addresses" => Ok(GeneratedField::IbcRelayerAddresses),
+ "nativeAssetBaseDenomination" | "native_asset_base_denomination" => Ok(GeneratedField::NativeAssetBaseDenomination),
+ "ibcParameters" | "ibc_parameters" => Ok(GeneratedField::IbcParameters),
+ "allowedFeeAssets" | "allowed_fee_assets" => Ok(GeneratedField::AllowedFeeAssets),
+ "fees" => Ok(GeneratedField::Fees),
+ _ => Err(serde::de::Error::unknown_field(value, FIELDS)),
+ }
+ }
+ }
+ deserializer.deserialize_identifier(GeneratedVisitor)
+ }
+ }
+ struct GeneratedVisitor;
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = GenesisAppState;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ formatter.write_str("struct astria.protocol.genesis.v1alpha1.GenesisAppState")
+ }
+
+ fn visit_map(self, mut map_: V) -> std::result::Result
+ where
+ V: serde::de::MapAccess<'de>,
+ {
+ let mut chain_id__ = None;
+ let mut address_prefixes__ = None;
+ let mut accounts__ = None;
+ let mut authority_sudo_address__ = None;
+ let mut ibc_sudo_address__ = None;
+ let mut ibc_relayer_addresses__ = None;
+ let mut native_asset_base_denomination__ = None;
+ let mut ibc_parameters__ = None;
+ let mut allowed_fee_assets__ = None;
+ let mut fees__ = None;
+ while let Some(k) = map_.next_key()? {
+ match k {
+ GeneratedField::ChainId => {
+ if chain_id__.is_some() {
+ return Err(serde::de::Error::duplicate_field("chainId"));
+ }
+ chain_id__ = Some(map_.next_value()?);
+ }
+ GeneratedField::AddressPrefixes => {
+ if address_prefixes__.is_some() {
+ return Err(serde::de::Error::duplicate_field("addressPrefixes"));
+ }
+ address_prefixes__ = map_.next_value()?;
+ }
+ GeneratedField::Accounts => {
+ if accounts__.is_some() {
+ return Err(serde::de::Error::duplicate_field("accounts"));
+ }
+ accounts__ = Some(map_.next_value()?);
+ }
+ GeneratedField::AuthoritySudoAddress => {
+ if authority_sudo_address__.is_some() {
+ return Err(serde::de::Error::duplicate_field("authoritySudoAddress"));
+ }
+ authority_sudo_address__ = map_.next_value()?;
+ }
+ GeneratedField::IbcSudoAddress => {
+ if ibc_sudo_address__.is_some() {
+ return Err(serde::de::Error::duplicate_field("ibcSudoAddress"));
+ }
+ ibc_sudo_address__ = map_.next_value()?;
+ }
+ GeneratedField::IbcRelayerAddresses => {
+ if ibc_relayer_addresses__.is_some() {
+ return Err(serde::de::Error::duplicate_field("ibcRelayerAddresses"));
+ }
+ ibc_relayer_addresses__ = Some(map_.next_value()?);
+ }
+ GeneratedField::NativeAssetBaseDenomination => {
+ if native_asset_base_denomination__.is_some() {
+ return Err(serde::de::Error::duplicate_field("nativeAssetBaseDenomination"));
+ }
+ native_asset_base_denomination__ = Some(map_.next_value()?);
+ }
+ GeneratedField::IbcParameters => {
+ if ibc_parameters__.is_some() {
+ return Err(serde::de::Error::duplicate_field("ibcParameters"));
+ }
+ ibc_parameters__ = map_.next_value()?;
+ }
+ GeneratedField::AllowedFeeAssets => {
+ if allowed_fee_assets__.is_some() {
+ return Err(serde::de::Error::duplicate_field("allowedFeeAssets"));
+ }
+ allowed_fee_assets__ = Some(map_.next_value()?);
+ }
+ GeneratedField::Fees => {
+ if fees__.is_some() {
+ return Err(serde::de::Error::duplicate_field("fees"));
+ }
+ fees__ = map_.next_value()?;
+ }
+ }
+ }
+ Ok(GenesisAppState {
+ chain_id: chain_id__.unwrap_or_default(),
+ address_prefixes: address_prefixes__,
+ accounts: accounts__.unwrap_or_default(),
+ authority_sudo_address: authority_sudo_address__,
+ ibc_sudo_address: ibc_sudo_address__,
+ ibc_relayer_addresses: ibc_relayer_addresses__.unwrap_or_default(),
+ native_asset_base_denomination: native_asset_base_denomination__.unwrap_or_default(),
+ ibc_parameters: ibc_parameters__,
+ allowed_fee_assets: allowed_fee_assets__.unwrap_or_default(),
+ fees: fees__,
+ })
+ }
+ }
+ deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.GenesisAppState", FIELDS, GeneratedVisitor)
+ }
+}
+impl serde::Serialize for IbcParameters {
+ #[allow(deprecated)]
+ fn serialize(&self, serializer: S) -> std::result::Result
+ where
+ S: serde::Serializer,
+ {
+ use serde::ser::SerializeStruct;
+ let mut len = 0;
+ if self.ibc_enabled {
+ len += 1;
+ }
+ if self.inbound_ics20_transfers_enabled {
+ len += 1;
+ }
+ if self.outbound_ics20_transfers_enabled {
+ len += 1;
+ }
+ let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.IbcParameters", len)?;
+ if self.ibc_enabled {
+ struct_ser.serialize_field("ibcEnabled", &self.ibc_enabled)?;
+ }
+ if self.inbound_ics20_transfers_enabled {
+ struct_ser.serialize_field("inboundIcs20TransfersEnabled", &self.inbound_ics20_transfers_enabled)?;
+ }
+ if self.outbound_ics20_transfers_enabled {
+ struct_ser.serialize_field("outboundIcs20TransfersEnabled", &self.outbound_ics20_transfers_enabled)?;
+ }
+ struct_ser.end()
+ }
+}
+impl<'de> serde::Deserialize<'de> for IbcParameters {
+ #[allow(deprecated)]
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ const FIELDS: &[&str] = &[
+ "ibc_enabled",
+ "ibcEnabled",
+ "inbound_ics20_transfers_enabled",
+ "inboundIcs20TransfersEnabled",
+ "outbound_ics20_transfers_enabled",
+ "outboundIcs20TransfersEnabled",
+ ];
+
+ #[allow(clippy::enum_variant_names)]
+ enum GeneratedField {
+ IbcEnabled,
+ InboundIcs20TransfersEnabled,
+ OutboundIcs20TransfersEnabled,
+ }
+ impl<'de> serde::Deserialize<'de> for GeneratedField {
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct GeneratedVisitor;
+
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = GeneratedField;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(formatter, "expected one of: {:?}", &FIELDS)
+ }
+
+ #[allow(unused_variables)]
+ fn visit_str(self, value: &str) -> std::result::Result
+ where
+ E: serde::de::Error,
+ {
+ match value {
+ "ibcEnabled" | "ibc_enabled" => Ok(GeneratedField::IbcEnabled),
+ "inboundIcs20TransfersEnabled" | "inbound_ics20_transfers_enabled" => Ok(GeneratedField::InboundIcs20TransfersEnabled),
+ "outboundIcs20TransfersEnabled" | "outbound_ics20_transfers_enabled" => Ok(GeneratedField::OutboundIcs20TransfersEnabled),
+ _ => Err(serde::de::Error::unknown_field(value, FIELDS)),
+ }
+ }
+ }
+ deserializer.deserialize_identifier(GeneratedVisitor)
+ }
+ }
+ struct GeneratedVisitor;
+ impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+ type Value = IbcParameters;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ formatter.write_str("struct astria.protocol.genesis.v1alpha1.IbcParameters")
+ }
+
+ fn visit_map(self, mut map_: V) -> std::result::Result
+ where
+ V: serde::de::MapAccess<'de>,
+ {
+ let mut ibc_enabled__ = None;
+ let mut inbound_ics20_transfers_enabled__ = None;
+ let mut outbound_ics20_transfers_enabled__ = None;
+ while let Some(k) = map_.next_key()? {
+ match k {
+ GeneratedField::IbcEnabled => {
+ if ibc_enabled__.is_some() {
+ return Err(serde::de::Error::duplicate_field("ibcEnabled"));
+ }
+ ibc_enabled__ = Some(map_.next_value()?);
+ }
+ GeneratedField::InboundIcs20TransfersEnabled => {
+ if inbound_ics20_transfers_enabled__.is_some() {
+ return Err(serde::de::Error::duplicate_field("inboundIcs20TransfersEnabled"));
+ }
+ inbound_ics20_transfers_enabled__ = Some(map_.next_value()?);
+ }
+ GeneratedField::OutboundIcs20TransfersEnabled => {
+ if outbound_ics20_transfers_enabled__.is_some() {
+ return Err(serde::de::Error::duplicate_field("outboundIcs20TransfersEnabled"));
+ }
+ outbound_ics20_transfers_enabled__ = Some(map_.next_value()?);
+ }
+ }
+ }
+ Ok(IbcParameters {
+ ibc_enabled: ibc_enabled__.unwrap_or_default(),
+ inbound_ics20_transfers_enabled: inbound_ics20_transfers_enabled__.unwrap_or_default(),
+ outbound_ics20_transfers_enabled: outbound_ics20_transfers_enabled__.unwrap_or_default(),
+ })
+ }
+ }
+ deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.IbcParameters", FIELDS, GeneratedVisitor)
+ }
+}
diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs
index d476b83f21..4b78c07fe0 100644
--- a/crates/astria-core/src/generated/mod.rs
+++ b/crates/astria-core/src/generated/mod.rs
@@ -84,6 +84,18 @@ pub mod protocol {
pub mod v1alpha1;
}
#[path = ""]
+ pub mod genesis {
+ pub mod v1alpha1 {
+ include!("astria.protocol.genesis.v1alpha1.rs");
+
+ #[cfg(feature = "serde")]
+ mod _serde_impls {
+ use super::*;
+ include!("astria.protocol.genesis.v1alpha1.serde.rs");
+ }
+ }
+ }
+ #[path = ""]
pub mod memos {
pub mod v1alpha1 {
include!("astria.protocol.memos.v1alpha1.rs");
diff --git a/crates/astria-core/src/lib.rs b/crates/astria-core/src/lib.rs
index 29c87603ef..4fecf8dddd 100644
--- a/crates/astria-core/src/lib.rs
+++ b/crates/astria-core/src/lib.rs
@@ -12,7 +12,6 @@ pub mod crypto;
pub mod execution;
pub mod primitive;
pub mod protocol;
-pub mod sequencer;
pub mod sequencerblock;
#[cfg(feature = "brotli")]
diff --git a/crates/astria-core/src/primitive/v1/u128.rs b/crates/astria-core/src/primitive/v1/u128.rs
index fbfb4208b1..ee77646a27 100644
--- a/crates/astria-core/src/primitive/v1/u128.rs
+++ b/crates/astria-core/src/primitive/v1/u128.rs
@@ -40,6 +40,12 @@ impl From for u128 {
}
}
+impl<'a> From<&'a u128> for Uint128 {
+ fn from(primitive: &'a u128) -> Self {
+ (*primitive).into()
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::generated::primitive::v1::Uint128;
diff --git a/crates/astria-core/src/protocol/genesis/mod.rs b/crates/astria-core/src/protocol/genesis/mod.rs
new file mode 100644
index 0000000000..32a5a9d4fd
--- /dev/null
+++ b/crates/astria-core/src/protocol/genesis/mod.rs
@@ -0,0 +1 @@
+pub mod v1alpha1;
diff --git a/crates/astria-core/src/snapshots/astria_core__sequencer__tests__genesis_state_is_unchanged.snap b/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__tests__genesis_state_is_unchanged.snap
similarity index 50%
rename from crates/astria-core/src/snapshots/astria_core__sequencer__tests__genesis_state_is_unchanged.snap
rename to crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__tests__genesis_state_is_unchanged.snap
index fde7a2b1e4..faa12162a8 100644
--- a/crates/astria-core/src/snapshots/astria_core__sequencer__tests__genesis_state_is_unchanged.snap
+++ b/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__tests__genesis_state_is_unchanged.snap
@@ -1,9 +1,10 @@
---
-source: crates/astria-core/src/sequencer.rs
+source: crates/astria-core/src/protocol/genesis/v1alpha1.rs
expression: genesis_state()
---
{
- "address_prefixes": {
+ "chainId": "astria-1",
+ "addressPrefixes": {
"base": "astria"
},
"accounts": [
@@ -11,28 +12,34 @@ expression: genesis_state()
"address": {
"bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm"
},
- "balance": 1000000000000000000
+ "balance": {
+ "lo": "1000000000000000000"
+ }
},
{
"address": {
"bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z"
},
- "balance": 1000000000000000000
+ "balance": {
+ "lo": "1000000000000000000"
+ }
},
{
"address": {
"bech32m": "astria1vpcfutferpjtwv457r63uwr6hdm8gwr3pxt5ny"
},
- "balance": 1000000000000000000
+ "balance": {
+ "lo": "1000000000000000000"
+ }
}
],
- "authority_sudo_address": {
+ "authoritySudoAddress": {
"bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm"
},
- "ibc_sudo_address": {
+ "ibcSudoAddress": {
"bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm"
},
- "ibc_relayer_addresses": [
+ "ibcRelayerAddresses": [
{
"bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm"
},
@@ -40,22 +47,36 @@ expression: genesis_state()
"bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z"
}
],
- "native_asset_base_denomination": "nria",
- "ibc_params": {
+ "nativeAssetBaseDenomination": "nria",
+ "ibcParameters": {
"ibcEnabled": true,
"inboundIcs20TransfersEnabled": true,
"outboundIcs20TransfersEnabled": true
},
- "allowed_fee_assets": [
+ "allowedFeeAssets": [
"nria"
],
"fees": {
- "transfer_base_fee": 12,
- "sequence_base_fee": 32,
- "sequence_byte_cost_multiplier": 1,
- "init_bridge_account_base_fee": 48,
- "bridge_lock_byte_cost_multiplier": 1,
- "bridge_sudo_change_fee": 24,
- "ics20_withdrawal_base_fee": 24
+ "transferBaseFee": {
+ "lo": "12"
+ },
+ "sequenceBaseFee": {
+ "lo": "32"
+ },
+ "sequenceByteCostMultiplier": {
+ "lo": "1"
+ },
+ "initBridgeAccountBaseFee": {
+ "lo": "48"
+ },
+ "bridgeLockByteCostMultiplier": {
+ "lo": "1"
+ },
+ "bridgeSudoChangeFee": {
+ "lo": "24"
+ },
+ "ics20WithdrawalBaseFee": {
+ "lo": "24"
+ }
}
}
diff --git a/crates/astria-core/src/protocol/genesis/v1alpha1.rs b/crates/astria-core/src/protocol/genesis/v1alpha1.rs
new file mode 100644
index 0000000000..2131d1fccc
--- /dev/null
+++ b/crates/astria-core/src/protocol/genesis/v1alpha1.rs
@@ -0,0 +1,778 @@
+use std::convert::Infallible;
+
+pub use penumbra_ibc::params::IBCParameters;
+
+use crate::{
+ generated::protocol::genesis::v1alpha1 as raw,
+ primitive::v1::{
+ asset::{
+ self,
+ denom::ParseTracePrefixedError,
+ ParseDenomError,
+ },
+ Address,
+ AddressError,
+ ADDRESS_LEN,
+ },
+ Protobuf,
+};
+
+/// The genesis state of Astria's Sequencer.
+///
+/// Verified to only contain valid fields (right now, addresses that have the same base prefix
+/// as set in `GenesisState::address_prefixes::base`).
+#[derive(Clone, Debug)]
+#[cfg_attr(
+ feature = "serde",
+ derive(serde::Deserialize, serde::Serialize),
+ serde(try_from = "raw::GenesisAppState", into = "raw::GenesisAppState")
+)]
+pub struct GenesisAppState {
+ chain_id: String,
+ address_prefixes: AddressPrefixes,
+ accounts: Vec,
+ authority_sudo_address: crate::primitive::v1::Address,
+ ibc_sudo_address: crate::primitive::v1::Address,
+ ibc_relayer_addresses: Vec,
+ native_asset_base_denomination: asset::TracePrefixed,
+ ibc_parameters: IBCParameters,
+ allowed_fee_assets: Vec,
+ fees: Fees,
+}
+
+impl GenesisAppState {
+ #[must_use]
+ pub fn address_prefixes(&self) -> &AddressPrefixes {
+ &self.address_prefixes
+ }
+
+ #[must_use]
+ pub fn accounts(&self) -> &[Account] {
+ &self.accounts
+ }
+
+ #[must_use]
+ pub fn authority_sudo_address(&self) -> &Address {
+ &self.authority_sudo_address
+ }
+
+ #[must_use]
+ pub fn chain_id(&self) -> &str {
+ &self.chain_id
+ }
+
+ #[must_use]
+ pub fn ibc_sudo_address(&self) -> &Address {
+ &self.ibc_sudo_address
+ }
+
+ #[must_use]
+ pub fn ibc_relayer_addresses(&self) -> &[Address] {
+ &self.ibc_relayer_addresses
+ }
+
+ #[must_use]
+ pub fn native_asset_base_denomination(&self) -> &asset::TracePrefixed {
+ &self.native_asset_base_denomination
+ }
+
+ #[must_use]
+ pub fn ibc_parameters(&self) -> &IBCParameters {
+ &self.ibc_parameters
+ }
+
+ #[must_use]
+ pub fn allowed_fee_assets(&self) -> &[asset::Denom] {
+ &self.allowed_fee_assets
+ }
+
+ #[must_use]
+ pub fn fees(&self) -> &Fees {
+ &self.fees
+ }
+
+ fn ensure_address_has_base_prefix(
+ &self,
+ address: &Address,
+ field: &str,
+ ) -> Result<(), Box> {
+ if self.address_prefixes.base != address.prefix() {
+ return Err(Box::new(AddressDoesNotMatchBase {
+ base_prefix: self.address_prefixes.base.clone(),
+ address: *address,
+ field: field.to_string(),
+ }));
+ }
+ Ok(())
+ }
+
+ fn ensure_all_addresses_have_base_prefix(&self) -> Result<(), Box> {
+ for (i, account) in self.accounts.iter().enumerate() {
+ self.ensure_address_has_base_prefix(
+ &account.address,
+ &format!(".accounts[{i}].address"),
+ )?;
+ }
+ self.ensure_address_has_base_prefix(
+ &self.authority_sudo_address,
+ ".authority_sudo_address",
+ )?;
+ self.ensure_address_has_base_prefix(&self.ibc_sudo_address, ".ibc_sudo_address")?;
+ for (i, address) in self.ibc_relayer_addresses.iter().enumerate() {
+ self.ensure_address_has_base_prefix(address, &format!(".ibc_relayer_addresses[{i}]"))?;
+ }
+ Ok(())
+ }
+}
+
+impl Protobuf for GenesisAppState {
+ type Error = GenesisAppStateError;
+ type Raw = raw::GenesisAppState;
+
+ fn try_from_raw_ref(raw: &Self::Raw) -> Result {
+ let Self::Raw {
+ address_prefixes,
+ accounts,
+ authority_sudo_address,
+ chain_id,
+ ibc_sudo_address,
+ ibc_relayer_addresses,
+ native_asset_base_denomination,
+ ibc_parameters,
+ allowed_fee_assets,
+ fees,
+ } = raw;
+ let address_prefixes = address_prefixes
+ .as_ref()
+ .ok_or_else(|| Self::Error::field_not_set("address_prefixes"))
+ .and_then(|aps| {
+ AddressPrefixes::try_from_raw_ref(aps).map_err(Self::Error::address_prefixes)
+ })?;
+ let accounts = accounts
+ .iter()
+ .map(Account::try_from_raw_ref)
+ .collect::, _>>()
+ .map_err(Self::Error::accounts)?;
+
+ let authority_sudo_address = authority_sudo_address
+ .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)
+ })?;
+ 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))?;
+
+ let ibc_relayer_addresses = ibc_relayer_addresses
+ .iter()
+ .map(Address::try_from_raw)
+ .collect::>()
+ .map_err(Self::Error::ibc_relayer_addresses)?;
+
+ let native_asset_base_denomination = native_asset_base_denomination
+ .parse()
+ .map_err(Self::Error::native_asset_base_denomination)?;
+
+ let ibc_parameters = {
+ let params = ibc_parameters
+ .as_ref()
+ .ok_or_else(|| Self::Error::field_not_set("ibc_parameters"))?;
+ IBCParameters::try_from_raw_ref(params).expect("conversion is infallible")
+ };
+
+ let allowed_fee_assets = allowed_fee_assets
+ .iter()
+ .map(|asset| asset.parse())
+ .collect::>()
+ .map_err(Self::Error::allowed_fee_assets)?;
+
+ let fees = fees
+ .as_ref()
+ .ok_or_else(|| Self::Error::field_not_set("fees"))
+ .and_then(|fees| Fees::try_from_raw_ref(fees).map_err(Self::Error::fees))?;
+
+ let this = Self {
+ address_prefixes,
+ accounts,
+ authority_sudo_address,
+ chain_id: chain_id.clone(),
+ ibc_sudo_address,
+ ibc_relayer_addresses,
+ native_asset_base_denomination,
+ ibc_parameters,
+ allowed_fee_assets,
+ fees,
+ };
+ this.ensure_all_addresses_have_base_prefix()
+ .map_err(Self::Error::address_does_not_match_base)?;
+ Ok(this)
+ }
+
+ fn to_raw(&self) -> Self::Raw {
+ let Self {
+ address_prefixes,
+ accounts,
+ authority_sudo_address,
+ chain_id,
+ ibc_sudo_address,
+ ibc_relayer_addresses,
+ native_asset_base_denomination,
+ ibc_parameters,
+ allowed_fee_assets,
+ fees,
+ } = self;
+ Self::Raw {
+ address_prefixes: Some(address_prefixes.to_raw()),
+ accounts: accounts.iter().map(Account::to_raw).collect(),
+ authority_sudo_address: Some(authority_sudo_address.to_raw()),
+ chain_id: chain_id.clone(),
+ ibc_sudo_address: Some(ibc_sudo_address.to_raw()),
+ ibc_relayer_addresses: ibc_relayer_addresses.iter().map(Address::to_raw).collect(),
+ native_asset_base_denomination: native_asset_base_denomination.to_string(),
+ ibc_parameters: Some(ibc_parameters.to_raw()),
+ allowed_fee_assets: allowed_fee_assets.iter().map(ToString::to_string).collect(),
+ fees: Some(fees.to_raw()),
+ }
+ }
+}
+
+impl TryFrom for GenesisAppState {
+ type Error = ::Error;
+
+ fn try_from(value: raw::GenesisAppState) -> Result {
+ Self::try_from_raw(value)
+ }
+}
+
+impl From for raw::GenesisAppState {
+ fn from(value: GenesisAppState) -> Self {
+ value.into_raw()
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct GenesisAppStateError(GenesisAppStateErrorKind);
+
+impl GenesisAppStateError {
+ fn accounts(source: AccountError) -> Self {
+ Self(GenesisAppStateErrorKind::Accounts {
+ source,
+ })
+ }
+
+ fn address_prefixes(source: AddressPrefixesError) -> Self {
+ Self(GenesisAppStateErrorKind::AddressPrefixes {
+ source,
+ })
+ }
+
+ fn address_does_not_match_base(source: Box) -> Self {
+ Self(GenesisAppStateErrorKind::AddressDoesNotMatchBase {
+ source,
+ })
+ }
+
+ fn allowed_fee_assets(source: ParseDenomError) -> Self {
+ Self(GenesisAppStateErrorKind::AllowedFeeAssets {
+ source,
+ })
+ }
+
+ fn authority_sudo_address(source: AddressError) -> Self {
+ Self(GenesisAppStateErrorKind::AuthoritySudoAddress {
+ source,
+ })
+ }
+
+ fn fees(source: FeesError) -> Self {
+ Self(GenesisAppStateErrorKind::Fees {
+ source,
+ })
+ }
+
+ fn field_not_set(name: &'static str) -> Self {
+ Self(GenesisAppStateErrorKind::FieldNotSet {
+ name,
+ })
+ }
+
+ fn ibc_relayer_addresses(source: AddressError) -> Self {
+ Self(GenesisAppStateErrorKind::IbcRelayerAddresses {
+ source,
+ })
+ }
+
+ fn ibc_sudo_address(source: AddressError) -> Self {
+ Self(GenesisAppStateErrorKind::IbcSudoAddress {
+ source,
+ })
+ }
+
+ fn native_asset_base_denomination(source: ParseTracePrefixedError) -> Self {
+ Self(GenesisAppStateErrorKind::NativeAssetBaseDenomination {
+ source,
+ })
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("failed ensuring invariants of {}", GenesisAppState::full_name())]
+enum GenesisAppStateErrorKind {
+ #[error("`accounts` field was invalid")]
+ Accounts { source: AccountError },
+ #[error("`address_prefixes` field was invalid")]
+ AddressPrefixes { source: AddressPrefixesError },
+ #[error("one of the provided addresses did not match the provided base prefix")]
+ AddressDoesNotMatchBase {
+ source: Box,
+ },
+ #[error("`allowed_fee_assets` field was invalid")]
+ AllowedFeeAssets { source: ParseDenomError },
+ #[error("`authority_sudo_address` field was invalid")]
+ AuthoritySudoAddress { source: AddressError },
+ #[error("`fees` field was invalid")]
+ Fees { source: FeesError },
+ #[error("`ibc_sudo_address` field was invalid")]
+ IbcSudoAddress { source: AddressError },
+ #[error("`ibc_relayer_addresses` field was invalid")]
+ IbcRelayerAddresses { source: AddressError },
+ #[error("field was not set: `{name}`")]
+ FieldNotSet { name: &'static str },
+ #[error("`native_asset_base_denomination` field was invalid")]
+ NativeAssetBaseDenomination { source: ParseTracePrefixedError },
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("address `{address}` at `{field}` does not have `{base_prefix}`")]
+struct AddressDoesNotMatchBase {
+ base_prefix: String,
+ address: Address,
+ field: String,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct Account {
+ pub address: Address,
+ pub balance: u128,
+}
+
+impl Protobuf for Account {
+ type Error = AccountError;
+ type Raw = raw::Account;
+
+ fn try_from_raw_ref(raw: &Self::Raw) -> Result {
+ let Self::Raw {
+ address,
+ balance,
+ } = raw;
+ 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))?;
+ let balance = balance
+ .ok_or_else(|| AccountError::field_not_set("balance"))
+ .map(Into::into)?;
+ Ok(Self {
+ address,
+ balance,
+ })
+ }
+
+ fn to_raw(&self) -> Self::Raw {
+ let Self {
+ address,
+ balance,
+ } = self;
+ Self::Raw {
+ address: Some(address.to_raw()),
+ balance: Some((*balance).into()),
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct AccountError(AccountErrorKind);
+
+impl AccountError {
+ fn address(source: AddressError) -> Self {
+ Self(AccountErrorKind::Address {
+ source,
+ })
+ }
+
+ fn field_not_set(name: &'static str) -> Self {
+ Self(AccountErrorKind::FieldNotSet {
+ name,
+ })
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("failed ensuring invariants of {}", Account::full_name())]
+enum AccountErrorKind {
+ #[error("`address` field was invalid")]
+ Address { source: AddressError },
+ #[error("field was not set: `{name}`")]
+ FieldNotSet { name: &'static str },
+}
+
+#[derive(Clone, Debug)]
+pub struct AddressPrefixes {
+ pub base: String,
+}
+
+impl Protobuf for AddressPrefixes {
+ type Error = AddressPrefixesError;
+ type Raw = raw::AddressPrefixes;
+
+ fn try_from_raw_ref(raw: &Self::Raw) -> Result {
+ let Self::Raw {
+ base,
+ } = raw;
+ try_construct_dummy_address_from_prefix(base).map_err(Self::Error::base)?;
+ Ok(Self {
+ base: base.to_string(),
+ })
+ }
+
+ fn to_raw(&self) -> Self::Raw {
+ let Self {
+ base,
+ } = self;
+ Self::Raw {
+ base: base.clone(),
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct AddressPrefixesError(AddressPrefixesErrorKind);
+
+impl AddressPrefixesError {
+ fn base(source: AddressError) -> Self {
+ Self(AddressPrefixesErrorKind::Base {
+ source,
+ })
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("failed ensuring invariants of {}", AddressPrefixes::full_name())]
+enum AddressPrefixesErrorKind {
+ #[error("`base` cannot be used to construct Astria addresses")]
+ Base { source: AddressError },
+}
+
+impl Protobuf for IBCParameters {
+ type Error = Infallible;
+ type Raw = raw::IbcParameters;
+
+ fn try_from_raw_ref(raw: &Self::Raw) -> Result {
+ Ok((*raw).into())
+ }
+
+ fn to_raw(&self) -> Self::Raw {
+ self.clone().into()
+ }
+}
+
+impl From for raw::IbcParameters {
+ fn from(value: IBCParameters) -> Self {
+ let IBCParameters {
+ ibc_enabled,
+ inbound_ics20_transfers_enabled,
+ outbound_ics20_transfers_enabled,
+ } = value;
+ Self {
+ ibc_enabled,
+ inbound_ics20_transfers_enabled,
+ outbound_ics20_transfers_enabled,
+ }
+ }
+}
+
+impl From for IBCParameters {
+ fn from(value: raw::IbcParameters) -> Self {
+ let raw::IbcParameters {
+ ibc_enabled,
+ inbound_ics20_transfers_enabled,
+ outbound_ics20_transfers_enabled,
+ } = value;
+ Self {
+ ibc_enabled,
+ inbound_ics20_transfers_enabled,
+ outbound_ics20_transfers_enabled,
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Fees {
+ pub transfer_base_fee: u128,
+ pub sequence_base_fee: u128,
+ pub sequence_byte_cost_multiplier: u128,
+ pub init_bridge_account_base_fee: u128,
+ pub bridge_lock_byte_cost_multiplier: u128,
+ pub bridge_sudo_change_fee: u128,
+ pub ics20_withdrawal_base_fee: u128,
+}
+
+impl Protobuf for Fees {
+ type Error = FeesError;
+ type Raw = raw::Fees;
+
+ fn try_from_raw_ref(raw: &Self::Raw) -> Result {
+ let Self::Raw {
+ transfer_base_fee,
+ sequence_base_fee,
+ sequence_byte_cost_multiplier,
+ init_bridge_account_base_fee,
+ bridge_lock_byte_cost_multiplier,
+ bridge_sudo_change_fee,
+ ics20_withdrawal_base_fee,
+ } = raw;
+ let transfer_base_fee = transfer_base_fee
+ .ok_or_else(|| Self::Error::field_not_set("transfer_base_fee"))?
+ .into();
+ let sequence_base_fee = sequence_base_fee
+ .ok_or_else(|| Self::Error::field_not_set("sequence_base_fee"))?
+ .into();
+ let sequence_byte_cost_multiplier = sequence_byte_cost_multiplier
+ .ok_or_else(|| Self::Error::field_not_set("sequence_byte_cost_multiplier"))?
+ .into();
+ let init_bridge_account_base_fee = init_bridge_account_base_fee
+ .ok_or_else(|| Self::Error::field_not_set("init_bridge_account_base_fee"))?
+ .into();
+ let bridge_lock_byte_cost_multiplier = bridge_lock_byte_cost_multiplier
+ .ok_or_else(|| Self::Error::field_not_set("bridge_lock_byte_cost_multiplier"))?
+ .into();
+ let bridge_sudo_change_fee = bridge_sudo_change_fee
+ .ok_or_else(|| Self::Error::field_not_set("bridge_sudo_change_fee"))?
+ .into();
+ let ics20_withdrawal_base_fee = ics20_withdrawal_base_fee
+ .ok_or_else(|| Self::Error::field_not_set("ics20_withdrawal_base_fee"))?
+ .into();
+ Ok(Self {
+ transfer_base_fee,
+ sequence_base_fee,
+ sequence_byte_cost_multiplier,
+ init_bridge_account_base_fee,
+ bridge_lock_byte_cost_multiplier,
+ bridge_sudo_change_fee,
+ ics20_withdrawal_base_fee,
+ })
+ }
+
+ fn to_raw(&self) -> Self::Raw {
+ let Self {
+ transfer_base_fee,
+ sequence_base_fee,
+ sequence_byte_cost_multiplier,
+ init_bridge_account_base_fee,
+ bridge_lock_byte_cost_multiplier,
+ bridge_sudo_change_fee,
+ ics20_withdrawal_base_fee,
+ } = self;
+ Self::Raw {
+ transfer_base_fee: Some(transfer_base_fee.into()),
+ sequence_base_fee: Some(sequence_base_fee.into()),
+ sequence_byte_cost_multiplier: Some(sequence_byte_cost_multiplier.into()),
+ init_bridge_account_base_fee: Some(init_bridge_account_base_fee.into()),
+ bridge_lock_byte_cost_multiplier: Some(bridge_lock_byte_cost_multiplier.into()),
+ bridge_sudo_change_fee: Some(bridge_sudo_change_fee.into()),
+ ics20_withdrawal_base_fee: Some(ics20_withdrawal_base_fee.into()),
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct FeesError(FeesErrorKind);
+
+impl FeesError {
+ fn field_not_set(name: &'static str) -> Self {
+ Self(FeesErrorKind::FieldNotSet {
+ name,
+ })
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("failed ensuring invariants of {}", Fees::full_name())]
+enum FeesErrorKind {
+ #[error("field was not set: `{name}`")]
+ FieldNotSet { name: &'static str },
+}
+
+/// Constructs a dummy address from a given `prefix`, otherwise fail.
+fn try_construct_dummy_address_from_prefix(prefix: &str) -> Result<(), AddressError> {
+ Address::builder()
+ .array([0u8; ADDRESS_LEN])
+ .prefix(prefix)
+ .try_build()
+ .map(|_| ())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::primitive::v1::Address;
+
+ const ASTRIA_ADDRESS_PREFIX: &str = "astria";
+
+ fn alice() -> Address {
+ Address::builder()
+ .prefix(ASTRIA_ADDRESS_PREFIX)
+ .slice(hex::decode("1c0c490f1b5528d8173c5de46d131160e4b2c0c3").unwrap())
+ .try_build()
+ .unwrap()
+ }
+
+ fn bob() -> Address {
+ Address::builder()
+ .prefix(ASTRIA_ADDRESS_PREFIX)
+ .slice(hex::decode("34fec43c7fcab9aef3b3cf8aba855e41ee69ca3a").unwrap())
+ .try_build()
+ .unwrap()
+ }
+
+ fn charlie() -> Address {
+ Address::builder()
+ .prefix(ASTRIA_ADDRESS_PREFIX)
+ .slice(hex::decode("60709e2d391864b732b4f0f51e387abb76743871").unwrap())
+ .try_build()
+ .unwrap()
+ }
+
+ fn mallory() -> Address {
+ Address::builder()
+ .prefix("other")
+ .slice(hex::decode("60709e2d391864b732b4f0f51e387abb76743871").unwrap())
+ .try_build()
+ .unwrap()
+ }
+
+ fn proto_genesis_state() -> raw::GenesisAppState {
+ raw::GenesisAppState {
+ accounts: vec![
+ raw::Account {
+ address: Some(alice().to_raw()),
+ balance: Some(1_000_000_000_000_000_000.into()),
+ },
+ raw::Account {
+ address: Some(bob().to_raw()),
+ balance: Some(1_000_000_000_000_000_000.into()),
+ },
+ raw::Account {
+ address: Some(charlie().to_raw()),
+ balance: Some(1_000_000_000_000_000_000.into()),
+ },
+ ],
+ address_prefixes: Some(raw::AddressPrefixes {
+ base: "astria".into(),
+ }),
+ authority_sudo_address: Some(alice().to_raw()),
+ chain_id: "astria-1".to_string(),
+ ibc_sudo_address: Some(alice().to_raw()),
+ ibc_relayer_addresses: vec![alice().to_raw(), bob().to_raw()],
+ native_asset_base_denomination: "nria".to_string(),
+ ibc_parameters: Some(raw::IbcParameters {
+ ibc_enabled: true,
+ inbound_ics20_transfers_enabled: true,
+ outbound_ics20_transfers_enabled: true,
+ }),
+ allowed_fee_assets: vec!["nria".into()],
+ fees: Some(raw::Fees {
+ transfer_base_fee: Some(12.into()),
+ sequence_base_fee: Some(32.into()),
+ sequence_byte_cost_multiplier: Some(1.into()),
+ init_bridge_account_base_fee: Some(48.into()),
+ bridge_lock_byte_cost_multiplier: Some(1.into()),
+ bridge_sudo_change_fee: Some(24.into()),
+ ics20_withdrawal_base_fee: Some(24.into()),
+ }),
+ }
+ }
+
+ fn genesis_state() -> GenesisAppState {
+ proto_genesis_state().try_into().unwrap()
+ }
+
+ #[test]
+ fn mismatched_addresses_are_caught() {
+ #[track_caller]
+ fn assert_bad_prefix(unchecked: raw::GenesisAppState, bad_field: &'static str) {
+ match GenesisAppState::try_from(unchecked)
+ .expect_err(
+ "converting to genesis state should have produced an error, but a valid state \
+ was returned",
+ )
+ .0
+ {
+ GenesisAppStateErrorKind::AddressDoesNotMatchBase {
+ source,
+ } => {
+ let AddressDoesNotMatchBase {
+ base_prefix,
+ address,
+ field,
+ } = *source;
+ assert_eq!(base_prefix, ASTRIA_ADDRESS_PREFIX);
+ assert_eq!(address, mallory());
+ assert_eq!(field, bad_field);
+ }
+ other => panic!(
+ "expected: `GenesisAppStateErrorKind::AddressDoesNotMatchBase\ngot: {other:?}`"
+ ),
+ };
+ }
+ assert_bad_prefix(
+ raw::GenesisAppState {
+ authority_sudo_address: Some(mallory().to_raw()),
+ ..proto_genesis_state()
+ },
+ ".authority_sudo_address",
+ );
+ assert_bad_prefix(
+ raw::GenesisAppState {
+ ibc_sudo_address: Some(mallory().to_raw()),
+ ..proto_genesis_state()
+ },
+ ".ibc_sudo_address",
+ );
+ assert_bad_prefix(
+ raw::GenesisAppState {
+ ibc_relayer_addresses: vec![alice().to_raw(), mallory().to_raw()],
+ ..proto_genesis_state()
+ },
+ ".ibc_relayer_addresses[1]",
+ );
+ assert_bad_prefix(
+ raw::GenesisAppState {
+ accounts: vec![
+ raw::Account {
+ address: Some(alice().to_raw()),
+ balance: Some(10.into()),
+ },
+ raw::Account {
+ address: Some(mallory().to_raw()),
+ balance: Some(10.into()),
+ },
+ ],
+ ..proto_genesis_state()
+ },
+ ".accounts[1].address",
+ );
+ }
+
+ #[cfg(feature = "serde")]
+ #[test]
+ fn genesis_state_is_unchanged() {
+ insta::assert_json_snapshot!(genesis_state());
+ }
+}
diff --git a/crates/astria-core/src/protocol/mod.rs b/crates/astria-core/src/protocol/mod.rs
index 2d30299b22..e285ce4d67 100644
--- a/crates/astria-core/src/protocol/mod.rs
+++ b/crates/astria-core/src/protocol/mod.rs
@@ -8,6 +8,7 @@ pub mod abci;
pub mod account;
pub mod asset;
pub mod bridge;
+pub mod genesis;
pub mod memos;
pub mod transaction;
diff --git a/crates/astria-core/src/sequencer.rs b/crates/astria-core/src/sequencer.rs
deleted file mode 100644
index 4138e96b6e..0000000000
--- a/crates/astria-core/src/sequencer.rs
+++ /dev/null
@@ -1,390 +0,0 @@
-//! Sequencer specific types that are needed outside of it.
-pub use penumbra_ibc::params::IBCParameters;
-
-use crate::primitive::v1::{
- asset::{
- self,
- TracePrefixed,
- },
- Address,
-};
-
-/// The genesis state of Astria's Sequencer.
-///
-/// Verified to only contain valid fields (right now, addresses that have the same base prefix
-/// as set in `GenesisState::address_prefixes::base`).
-///
-/// *Note on the implementation:* access to all fields is through getters to uphold invariants,
-/// but most returned values themselves have publicly exposed fields. This is to make it easier
-/// to construct an [`UncheckedGenesisState`].
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-#[cfg_attr(
- feature = "serde",
- serde(try_from = "UncheckedGenesisState", into = "UncheckedGenesisState")
-)]
-pub struct GenesisState {
- address_prefixes: AddressPrefixes,
- accounts: Vec,
- authority_sudo_address: Address,
- ibc_sudo_address: Address,
- ibc_relayer_addresses: Vec,
- native_asset_base_denomination: TracePrefixed,
- ibc_params: IBCParameters,
- allowed_fee_assets: Vec,
- fees: Fees,
-}
-
-impl GenesisState {
- #[must_use]
- pub fn address_prefixes(&self) -> &AddressPrefixes {
- &self.address_prefixes
- }
-
- #[must_use]
- pub fn accounts(&self) -> &[Account] {
- &self.accounts
- }
-
- #[must_use]
- pub fn authority_sudo_address(&self) -> &Address {
- &self.authority_sudo_address
- }
-
- #[must_use]
- pub fn ibc_sudo_address(&self) -> &Address {
- &self.ibc_sudo_address
- }
-
- #[must_use]
- pub fn ibc_relayer_addresses(&self) -> &[Address] {
- &self.ibc_relayer_addresses
- }
-
- #[must_use]
- pub fn native_asset_base_denomination(&self) -> &TracePrefixed {
- &self.native_asset_base_denomination
- }
-
- #[must_use]
- pub fn ibc_params(&self) -> &IBCParameters {
- &self.ibc_params
- }
-
- #[must_use]
- pub fn allowed_fee_assets(&self) -> &[asset::Denom] {
- &self.allowed_fee_assets
- }
-
- #[must_use]
- pub fn fees(&self) -> &Fees {
- &self.fees
- }
-}
-
-#[derive(Debug, thiserror::Error)]
-#[error(transparent)]
-pub struct VerifyGenesisError(Box);
-
-#[derive(Debug, thiserror::Error)]
-enum VerifyGenesisErrorKind {
- #[error("address `{address}` at `{field}` does not have `{base_prefix}`")]
- AddressDoesNotMatchBase {
- base_prefix: String,
- address: Address,
- field: String,
- },
-}
-
-impl From for VerifyGenesisError {
- fn from(value: VerifyGenesisErrorKind) -> Self {
- Self(Box::new(value))
- }
-}
-
-impl TryFrom for GenesisState {
- type Error = VerifyGenesisError;
-
- fn try_from(value: UncheckedGenesisState) -> Result {
- value.ensure_all_addresses_have_base_prefix()?;
-
- let UncheckedGenesisState {
- address_prefixes,
- accounts,
- authority_sudo_address,
- ibc_sudo_address,
- ibc_relayer_addresses,
- native_asset_base_denomination,
- ibc_params,
- allowed_fee_assets,
- fees,
- } = value;
-
- Ok(Self {
- address_prefixes,
- accounts,
- authority_sudo_address,
- ibc_sudo_address,
- ibc_relayer_addresses,
- native_asset_base_denomination,
- ibc_params,
- allowed_fee_assets,
- fees,
- })
- }
-}
-
-/// The unchecked genesis state for the application.
-#[derive(Debug)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct UncheckedGenesisState {
- pub address_prefixes: AddressPrefixes,
- pub accounts: Vec,
- pub authority_sudo_address: Address,
- pub ibc_sudo_address: Address,
- pub ibc_relayer_addresses: Vec,
- pub native_asset_base_denomination: TracePrefixed,
- pub ibc_params: IBCParameters,
- pub allowed_fee_assets: Vec,
- pub fees: Fees,
-}
-
-impl UncheckedGenesisState {
- fn ensure_address_has_base_prefix(
- &self,
- address: &Address,
- field: &str,
- ) -> Result<(), VerifyGenesisError> {
- if self.address_prefixes.base != address.prefix() {
- return Err(VerifyGenesisErrorKind::AddressDoesNotMatchBase {
- base_prefix: self.address_prefixes.base.clone(),
- address: *address,
- field: field.to_string(),
- }
- .into());
- }
- Ok(())
- }
-
- fn ensure_all_addresses_have_base_prefix(&self) -> Result<(), VerifyGenesisError> {
- for (i, account) in self.accounts.iter().enumerate() {
- self.ensure_address_has_base_prefix(
- &account.address,
- &format!(".accounts[{i}].address"),
- )?;
- }
- self.ensure_address_has_base_prefix(
- &self.authority_sudo_address,
- ".authority_sudo_address",
- )?;
- self.ensure_address_has_base_prefix(&self.ibc_sudo_address, ".ibc_sudo_address")?;
- for (i, address) in self.ibc_relayer_addresses.iter().enumerate() {
- self.ensure_address_has_base_prefix(address, &format!(".ibc_relayer_addresses[{i}]"))?;
- }
- Ok(())
- }
-}
-
-impl From for UncheckedGenesisState {
- fn from(value: GenesisState) -> Self {
- let GenesisState {
- address_prefixes,
- accounts,
- authority_sudo_address,
- ibc_sudo_address,
- ibc_relayer_addresses,
- native_asset_base_denomination,
- ibc_params,
- allowed_fee_assets,
- fees,
- } = value;
- Self {
- address_prefixes,
- accounts,
- authority_sudo_address,
- ibc_sudo_address,
- ibc_relayer_addresses,
- native_asset_base_denomination,
- ibc_params,
- allowed_fee_assets,
- fees,
- }
- }
-}
-
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct Fees {
- pub transfer_base_fee: u128,
- pub sequence_base_fee: u128,
- pub sequence_byte_cost_multiplier: u128,
- pub init_bridge_account_base_fee: u128,
- pub bridge_lock_byte_cost_multiplier: u128,
- pub bridge_sudo_change_fee: u128,
- pub ics20_withdrawal_base_fee: u128,
-}
-
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct Account {
- pub address: Address,
- pub balance: u128,
-}
-
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct AddressPrefixes {
- pub base: String,
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::primitive::v1::Address;
-
- const ASTRIA_ADDRESS_PREFIX: &str = "astria";
-
- fn alice() -> Address {
- Address::builder()
- .prefix(ASTRIA_ADDRESS_PREFIX)
- .slice(hex::decode("1c0c490f1b5528d8173c5de46d131160e4b2c0c3").unwrap())
- .try_build()
- .unwrap()
- }
-
- fn bob() -> Address {
- Address::builder()
- .prefix(ASTRIA_ADDRESS_PREFIX)
- .slice(hex::decode("34fec43c7fcab9aef3b3cf8aba855e41ee69ca3a").unwrap())
- .try_build()
- .unwrap()
- }
-
- fn charlie() -> Address {
- Address::builder()
- .prefix(ASTRIA_ADDRESS_PREFIX)
- .slice(hex::decode("60709e2d391864b732b4f0f51e387abb76743871").unwrap())
- .try_build()
- .unwrap()
- }
-
- fn mallory() -> Address {
- Address::builder()
- .prefix("other")
- .slice(hex::decode("60709e2d391864b732b4f0f51e387abb76743871").unwrap())
- .try_build()
- .unwrap()
- }
-
- fn unchecked_genesis_state() -> UncheckedGenesisState {
- UncheckedGenesisState {
- accounts: vec![
- Account {
- address: alice(),
- balance: 1_000_000_000_000_000_000,
- },
- Account {
- address: bob(),
- balance: 1_000_000_000_000_000_000,
- },
- Account {
- address: charlie(),
- balance: 1_000_000_000_000_000_000,
- },
- ],
- address_prefixes: AddressPrefixes {
- base: "astria".into(),
- },
- authority_sudo_address: alice(),
- ibc_sudo_address: alice(),
- ibc_relayer_addresses: vec![alice(), bob()],
- native_asset_base_denomination: "nria".parse().unwrap(),
- ibc_params: IBCParameters {
- ibc_enabled: true,
- inbound_ics20_transfers_enabled: true,
- outbound_ics20_transfers_enabled: true,
- },
- allowed_fee_assets: vec!["nria".parse().unwrap()],
- fees: Fees {
- transfer_base_fee: 12,
- sequence_base_fee: 32,
- sequence_byte_cost_multiplier: 1,
- init_bridge_account_base_fee: 48,
- bridge_lock_byte_cost_multiplier: 1,
- bridge_sudo_change_fee: 24,
- ics20_withdrawal_base_fee: 24,
- },
- }
- }
-
- fn genesis_state() -> GenesisState {
- unchecked_genesis_state().try_into().unwrap()
- }
-
- #[test]
- fn mismatched_addresses_are_caught() {
- #[track_caller]
- fn assert_bad_prefix(unchecked: UncheckedGenesisState, bad_field: &'static str) {
- match *GenesisState::try_from(unchecked)
- .expect_err(
- "converting to genesis state should have produced an error, but a valid state \
- was returned",
- )
- .0
- {
- VerifyGenesisErrorKind::AddressDoesNotMatchBase {
- base_prefix,
- address,
- field,
- } => {
- assert_eq!(base_prefix, ASTRIA_ADDRESS_PREFIX);
- assert_eq!(address, mallory());
- assert_eq!(field, bad_field);
- }
- };
- }
- assert_bad_prefix(
- UncheckedGenesisState {
- authority_sudo_address: mallory(),
- ..unchecked_genesis_state()
- },
- ".authority_sudo_address",
- );
- assert_bad_prefix(
- UncheckedGenesisState {
- ibc_sudo_address: mallory(),
- ..unchecked_genesis_state()
- },
- ".ibc_sudo_address",
- );
- assert_bad_prefix(
- UncheckedGenesisState {
- ibc_relayer_addresses: vec![alice(), mallory()],
- ..unchecked_genesis_state()
- },
- ".ibc_relayer_addresses[1]",
- );
- assert_bad_prefix(
- UncheckedGenesisState {
- accounts: vec![
- Account {
- address: alice(),
- balance: 10,
- },
- Account {
- address: mallory(),
- balance: 10,
- },
- ],
- ..unchecked_genesis_state()
- },
- ".accounts[1].address",
- );
- }
-
- #[cfg(feature = "serde")]
- #[test]
- fn genesis_state_is_unchanged() {
- insta::assert_json_snapshot!(genesis_state());
- }
-}
diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs
index 05d66baa07..b28083fb82 100644
--- a/crates/astria-sequencer-utils/src/genesis_example.rs
+++ b/crates/astria-sequencer-utils/src/genesis_example.rs
@@ -5,15 +5,15 @@ use std::{
};
use astria_core::{
+ generated::protocol::genesis::v1alpha1::IbcParameters,
primitive::v1::Address,
- sequencer::{
+ protocol::genesis::v1alpha1::{
Account,
AddressPrefixes,
Fees,
- GenesisState,
- IBCParameters,
- UncheckedGenesisState,
+ GenesisAppState,
},
+ Protobuf,
};
use astria_eyre::eyre::{
Result,
@@ -46,47 +46,61 @@ fn charlie() -> Address {
.unwrap()
}
-fn genesis_state() -> GenesisState {
- UncheckedGenesisState {
- accounts: vec![
- Account {
- address: alice(),
- balance: 1_000_000_000_000_000_000,
- },
- Account {
- address: bob(),
- balance: 1_000_000_000_000_000_000,
- },
- Account {
- address: charlie(),
- balance: 1_000_000_000_000_000_000,
- },
- ],
- address_prefixes: AddressPrefixes {
- base: "astria".into(),
+fn accounts() -> Vec {
+ vec![
+ Account {
+ address: alice(),
+ balance: 1_000_000_000_000_000_000,
},
- authority_sudo_address: alice(),
- ibc_sudo_address: alice(),
- ibc_relayer_addresses: vec![alice(), bob()],
+ Account {
+ address: bob(),
+ balance: 1_000_000_000_000_000_000,
+ },
+ Account {
+ address: charlie(),
+ balance: 1_000_000_000_000_000_000,
+ },
+ ]
+}
+
+fn address_prefixes() -> AddressPrefixes {
+ AddressPrefixes {
+ base: "astria".into(),
+ }
+}
+
+fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1::GenesisAppState {
+ astria_core::generated::protocol::genesis::v1alpha1::GenesisAppState {
+ accounts: accounts().into_iter().map(Protobuf::into_raw).collect(),
+ address_prefixes: Some(address_prefixes().into_raw()),
+ authority_sudo_address: Some(alice().to_raw()),
+ chain_id: "test-1".into(),
+ ibc_sudo_address: Some(alice().to_raw()),
+ ibc_relayer_addresses: vec![alice().to_raw(), bob().to_raw()],
native_asset_base_denomination: "nria".parse().unwrap(),
- ibc_params: IBCParameters {
+ ibc_parameters: Some(IbcParameters {
ibc_enabled: true,
inbound_ics20_transfers_enabled: true,
outbound_ics20_transfers_enabled: true,
- },
+ }),
allowed_fee_assets: vec!["nria".parse().unwrap()],
- fees: Fees {
- transfer_base_fee: 12,
- sequence_base_fee: 32,
- sequence_byte_cost_multiplier: 1,
- init_bridge_account_base_fee: 48,
- bridge_lock_byte_cost_multiplier: 1,
- bridge_sudo_change_fee: 24,
- ics20_withdrawal_base_fee: 24,
- },
+ fees: Some(
+ Fees {
+ transfer_base_fee: 12,
+ sequence_base_fee: 32,
+ sequence_byte_cost_multiplier: 1,
+ init_bridge_account_base_fee: 48,
+ bridge_lock_byte_cost_multiplier: 1,
+ bridge_sudo_change_fee: 24,
+ ics20_withdrawal_base_fee: 24,
+ }
+ .into_raw(),
+ ),
}
- .try_into()
- .unwrap()
+}
+
+fn genesis_state() -> GenesisAppState {
+ GenesisAppState::try_from_raw(proto_genesis_state()).unwrap()
}
#[derive(clap::Args, Debug)]
diff --git a/crates/astria-sequencer/src/accounts/component.rs b/crates/astria-sequencer/src/accounts/component.rs
index 615cb24bbd..e08d7b357a 100644
--- a/crates/astria-sequencer/src/accounts/component.rs
+++ b/crates/astria-sequencer/src/accounts/component.rs
@@ -4,6 +4,7 @@ use anyhow::{
Context,
Result,
};
+use astria_core::protocol::genesis::v1alpha1::GenesisAppState;
use tendermint::abci::request::{
BeginBlock,
EndBlock,
@@ -21,7 +22,7 @@ pub(crate) struct AccountsComponent;
#[async_trait::async_trait]
impl Component for AccountsComponent {
- type AppState = astria_core::sequencer::GenesisState;
+ type AppState = GenesisAppState;
#[instrument(name = "AccountsComponent::init_chain", skip_all)]
async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()>
diff --git a/crates/astria-sequencer/src/app/benchmarks.rs b/crates/astria-sequencer/src/app/benchmarks.rs
index db5448f668..b667720662 100644
--- a/crates/astria-sequencer/src/app/benchmarks.rs
+++ b/crates/astria-sequencer/src/app/benchmarks.rs
@@ -5,14 +5,14 @@
use std::time::Duration;
-use astria_core::sequencer::{
- Account,
- AddressPrefixes,
- GenesisState,
- UncheckedGenesisState,
+use astria_core::{
+ protocol::genesis::v1alpha1::{
+ Account,
+ GenesisAppState,
+ },
+ Protobuf,
};
use cnidarium::Storage;
-use penumbra_ibc::params::IBCParameters;
use crate::{
app::{
@@ -25,11 +25,7 @@ use crate::{
SIGNER_COUNT,
},
proposal::block_size_constraints::BlockSizeConstraints,
- test_utils::{
- astria_address,
- nria,
- ASTRIA_PREFIX,
- },
+ test_utils::astria_address,
};
/// The max time for any benchmark.
@@ -58,23 +54,18 @@ impl Fixture {
.pow(19)
.saturating_add(u128::try_from(index).unwrap()),
})
+ .map(Protobuf::into_raw)
.collect::>();
- let address_prefixes = AddressPrefixes {
- base: ASTRIA_PREFIX.into(),
- };
- let first_address = accounts.first().unwrap().address;
- let unchecked_genesis_state = UncheckedGenesisState {
- accounts,
- address_prefixes,
- authority_sudo_address: first_address,
- ibc_sudo_address: first_address,
- ibc_relayer_addresses: vec![],
- native_asset_base_denomination: nria(),
- ibc_params: IBCParameters::default(),
- allowed_fee_assets: vec![nria().into()],
- fees: test_utils::default_fees(),
- };
- let genesis_state = GenesisState::try_from(unchecked_genesis_state).unwrap();
+ let first_address = accounts.first().cloned().unwrap().address;
+ let genesis_state = GenesisAppState::try_from_raw(
+ astria_core::generated::protocol::genesis::v1alpha1::GenesisAppState {
+ accounts,
+ authority_sudo_address: first_address.clone(),
+ ibc_sudo_address: first_address.clone(),
+ ..crate::app::test_utils::proto_genesis_state()
+ },
+ )
+ .unwrap();
let (app, storage) =
test_utils::initialize_app_with_storage(Some(genesis_state), vec![]).await;
diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs
index 5300077ee8..e27feb6ec0 100644
--- a/crates/astria-sequencer/src/app/mod.rs
+++ b/crates/astria-sequencer/src/app/mod.rs
@@ -27,6 +27,7 @@ use astria_core::{
generated::protocol::transactions::v1alpha1 as raw,
protocol::{
abci::AbciErrorCode,
+ genesis::v1alpha1::GenesisAppState,
transaction::v1alpha1::{
action::ValidatorUpdate,
Action,
@@ -209,7 +210,7 @@ impl App {
pub(crate) async fn init_chain(
&mut self,
storage: Storage,
- genesis_state: astria_core::sequencer::GenesisState,
+ genesis_state: GenesisAppState,
genesis_validators: Vec,
chain_id: String,
) -> anyhow::Result {
diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs
index fcc4223fa1..05ffb9b7e0 100644
--- a/crates/astria-sequencer/src/app/test_utils.rs
+++ b/crates/astria-sequencer/src/app/test_utils.rs
@@ -3,26 +3,26 @@ use std::sync::Arc;
use astria_core::{
crypto::SigningKey,
primitive::v1::RollupId,
- protocol::transaction::v1alpha1::{
- action::{
- SequenceAction,
- ValidatorUpdate,
+ protocol::{
+ genesis::v1alpha1::{
+ Account,
+ AddressPrefixes,
+ GenesisAppState,
+ },
+ transaction::v1alpha1::{
+ action::{
+ SequenceAction,
+ ValidatorUpdate,
+ },
+ SignedTransaction,
+ TransactionParams,
+ UnsignedTransaction,
},
- SignedTransaction,
- TransactionParams,
- UnsignedTransaction,
- },
- sequencer::{
- Account,
- AddressPrefixes,
- Fees,
- GenesisState,
- UncheckedGenesisState,
},
+ Protobuf,
};
use bytes::Bytes;
use cnidarium::Storage;
-use penumbra_ibc::params::IBCParameters;
use crate::{
app::App,
@@ -58,7 +58,6 @@ pub(crate) fn get_bridge_signing_key() -> SigningKey {
SigningKey::from(bridge_secret_bytes)
}
-#[cfg_attr(feature = "benchmark", allow(dead_code))]
pub(crate) fn default_genesis_accounts() -> Vec {
vec![
Account {
@@ -77,8 +76,8 @@ pub(crate) fn default_genesis_accounts() -> Vec {
}
#[cfg_attr(feature = "benchmark", allow(dead_code))]
-pub(crate) fn default_fees() -> Fees {
- Fees {
+pub(crate) fn default_fees() -> astria_core::protocol::genesis::v1alpha1::Fees {
+ astria_core::protocol::genesis::v1alpha1::Fees {
transfer_base_fee: 12,
sequence_base_fee: 32,
sequence_byte_cost_multiplier: 1,
@@ -89,28 +88,45 @@ pub(crate) fn default_fees() -> Fees {
}
}
-pub(crate) fn unchecked_genesis_state() -> UncheckedGenesisState {
- UncheckedGenesisState {
- accounts: default_genesis_accounts(),
- address_prefixes: AddressPrefixes {
- base: crate::test_utils::ASTRIA_PREFIX.into(),
- },
- authority_sudo_address: astria_address_from_hex_string(JUDY_ADDRESS),
- ibc_sudo_address: astria_address_from_hex_string(TED_ADDRESS),
+pub(crate) fn address_prefixes() -> AddressPrefixes {
+ AddressPrefixes {
+ base: crate::test_utils::ASTRIA_PREFIX.into(),
+ }
+}
+
+pub(crate) fn proto_genesis_state()
+-> astria_core::generated::protocol::genesis::v1alpha1::GenesisAppState {
+ use astria_core::generated::protocol::genesis::v1alpha1::{
+ GenesisAppState,
+ IbcParameters,
+ };
+ GenesisAppState {
+ address_prefixes: Some(address_prefixes().to_raw()),
+ accounts: default_genesis_accounts()
+ .into_iter()
+ .map(Protobuf::into_raw)
+ .collect(),
+ authority_sudo_address: Some(astria_address_from_hex_string(JUDY_ADDRESS).to_raw()),
+ chain_id: "test-1".to_string(),
+ ibc_sudo_address: Some(astria_address_from_hex_string(TED_ADDRESS).to_raw()),
ibc_relayer_addresses: vec![],
- native_asset_base_denomination: crate::test_utils::nria(),
- ibc_params: IBCParameters::default(),
- allowed_fee_assets: vec![crate::test_utils::nria().into()],
- fees: default_fees(),
+ native_asset_base_denomination: crate::test_utils::nria().to_string(),
+ ibc_parameters: Some(IbcParameters {
+ ibc_enabled: true,
+ inbound_ics20_transfers_enabled: true,
+ outbound_ics20_transfers_enabled: true,
+ }),
+ allowed_fee_assets: vec![crate::test_utils::nria().to_string()],
+ fees: Some(default_fees().to_raw()),
}
}
-pub(crate) fn genesis_state() -> GenesisState {
- unchecked_genesis_state().try_into().unwrap()
+pub(crate) fn genesis_state() -> GenesisAppState {
+ proto_genesis_state().try_into().unwrap()
}
pub(crate) async fn initialize_app_with_storage(
- genesis_state: Option,
+ genesis_state: Option,
genesis_validators: Vec,
) -> (App, Storage) {
let storage = cnidarium::TempStorage::new()
@@ -138,7 +154,7 @@ pub(crate) async fn initialize_app_with_storage(
#[cfg_attr(feature = "benchmark", allow(dead_code))]
pub(crate) async fn initialize_app(
- genesis_state: Option,
+ genesis_state: Option,
genesis_validators: Vec,
) -> App {
let (app, _storage) = initialize_app_with_storage(genesis_state, genesis_validators).await;
diff --git a/crates/astria-sequencer/src/app/tests_app.rs b/crates/astria-sequencer/src/app/tests_app.rs
index 58ec904b17..aa6d9551ad 100644
--- a/crates/astria-sequencer/src/app/tests_app.rs
+++ b/crates/astria-sequencer/src/app/tests_app.rs
@@ -5,16 +5,18 @@ use astria_core::{
asset::TracePrefixed,
RollupId,
},
- protocol::transaction::v1alpha1::{
- action::{
- BridgeLockAction,
- SequenceAction,
- TransferAction,
+ protocol::{
+ genesis::v1alpha1::Account,
+ transaction::v1alpha1::{
+ action::{
+ BridgeLockAction,
+ SequenceAction,
+ TransferAction,
+ },
+ TransactionParams,
+ UnsignedTransaction,
},
- TransactionParams,
- UnsignedTransaction,
},
- sequencer::Account,
sequencerblock::v1alpha1::block::Deposit,
};
use cnidarium::StateDelta;
diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs
index f6065ecf0e..6909cc833d 100644
--- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs
+++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs
@@ -16,29 +16,27 @@ use std::{
use astria_core::{
primitive::v1::RollupId,
- protocol::transaction::v1alpha1::{
- action::{
- BridgeLockAction,
- BridgeSudoChangeAction,
- BridgeUnlockAction,
- IbcRelayerChangeAction,
- SequenceAction,
- TransferAction,
- ValidatorUpdate,
+ protocol::{
+ genesis::v1alpha1::Account,
+ transaction::v1alpha1::{
+ action::{
+ BridgeLockAction,
+ BridgeSudoChangeAction,
+ BridgeUnlockAction,
+ IbcRelayerChangeAction,
+ SequenceAction,
+ TransferAction,
+ ValidatorUpdate,
+ },
+ Action,
+ TransactionParams,
+ UnsignedTransaction,
},
- Action,
- TransactionParams,
- UnsignedTransaction,
- },
- sequencer::{
- Account,
- AddressPrefixes,
- UncheckedGenesisState,
},
sequencerblock::v1alpha1::block::Deposit,
+ Protobuf,
};
use cnidarium::StateDelta;
-use penumbra_ibc::params::IBCParameters;
use prost::{
bytes::Bytes,
Message as _,
@@ -53,12 +51,12 @@ use tendermint::{
use crate::{
app::test_utils::{
- default_fees,
default_genesis_accounts,
get_alice_signing_key,
get_bridge_signing_key,
initialize_app,
initialize_app_with_storage,
+ proto_genesis_state,
BOB_ADDRESS,
CAROL_ADDRESS,
},
@@ -72,26 +70,6 @@ use crate::{
},
};
-/// XXX: This should be expressed in terms of `crate::app::test_utils::unchecked_genesis_state` to
-/// be consistent everywhere. `get_alice_signing_key` already is, why not this?
-fn unchecked_genesis_state() -> UncheckedGenesisState {
- let alice = get_alice_signing_key();
- let alice_address = astria_address(&alice.address_bytes());
- UncheckedGenesisState {
- accounts: vec![],
- address_prefixes: AddressPrefixes {
- base: ASTRIA_PREFIX.into(),
- },
- authority_sudo_address: alice_address,
- ibc_sudo_address: alice_address,
- ibc_relayer_addresses: vec![],
- native_asset_base_denomination: nria(),
- ibc_params: IBCParameters::default(),
- allowed_fee_assets: vec![nria().into()],
- fees: default_fees(),
- }
-}
-
#[tokio::test]
async fn app_genesis_snapshot() {
let app = initialize_app(None, vec![]).await;
@@ -192,15 +170,20 @@ async fn app_execute_transaction_with_every_action_snapshot() {
let bridge_address = astria_address(&bridge.address_bytes());
let bob_address = astria_address_from_hex_string(BOB_ADDRESS);
let carol_address = astria_address_from_hex_string(CAROL_ADDRESS);
- let mut accounts = default_genesis_accounts();
- accounts.push(Account {
- address: bridge_address,
- balance: 1_000_000_000,
- });
- let genesis_state = UncheckedGenesisState {
+ let accounts = {
+ let mut acc = default_genesis_accounts();
+ acc.push(Account {
+ address: bridge_address,
+ balance: 1_000_000_000,
+ });
+ acc.into_iter().map(Protobuf::into_raw).collect()
+ };
+ let genesis_state = astria_core::generated::protocol::genesis::v1alpha1::GenesisAppState {
accounts,
- ..unchecked_genesis_state()
+ authority_sudo_address: Some(alice.try_address(ASTRIA_PREFIX).unwrap().to_raw()),
+ ibc_sudo_address: Some(alice.try_address(ASTRIA_PREFIX).unwrap().to_raw()),
+ ..proto_genesis_state()
}
.try_into()
.unwrap();
diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs
index e88d0b5227..c433c48fcf 100644
--- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs
+++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs
@@ -6,35 +6,39 @@ use astria_core::{
asset,
RollupId,
},
- protocol::transaction::v1alpha1::{
- action::{
- BridgeLockAction,
- BridgeUnlockAction,
- IbcRelayerChangeAction,
- SequenceAction,
- SudoAddressChangeAction,
- TransferAction,
- ValidatorUpdate,
+ protocol::{
+ genesis::v1alpha1::GenesisAppState,
+ transaction::v1alpha1::{
+ action::{
+ BridgeLockAction,
+ BridgeUnlockAction,
+ IbcRelayerChangeAction,
+ SequenceAction,
+ SudoAddressChangeAction,
+ TransferAction,
+ ValidatorUpdate,
+ },
+ Action,
+ TransactionParams,
+ UnsignedTransaction,
},
- Action,
- TransactionParams,
- UnsignedTransaction,
- },
- sequencer::{
- AddressPrefixes,
- GenesisState,
- UncheckedGenesisState,
},
sequencerblock::v1alpha1::block::Deposit,
+ Protobuf as _,
};
use bytes::Bytes;
use cnidarium::StateDelta;
-use penumbra_ibc::params::IBCParameters;
+use super::test_utils::get_alice_signing_key;
use crate::{
accounts::StateReadExt as _,
app::{
- test_utils::*,
+ test_utils::{
+ get_bridge_signing_key,
+ initialize_app,
+ BOB_ADDRESS,
+ CAROL_ADDRESS,
+ },
ActionHandler as _,
},
assets::StateReadExt as _,
@@ -49,6 +53,7 @@ use crate::{
astria_address,
astria_address_from_hex_string,
nria,
+ ASTRIA_PREFIX,
},
transaction::{
InvalidChainId,
@@ -56,27 +61,26 @@ use crate::{
},
};
-/// XXX: This should be expressed in terms of `crate::app::test_utils::unchecked_genesis_state` to
-/// be consistent everywhere. `get_alice_sining_key` already is, why not this??
-fn unchecked_genesis_state() -> UncheckedGenesisState {
- let alice = get_alice_signing_key();
- UncheckedGenesisState {
- accounts: default_genesis_accounts(),
- address_prefixes: AddressPrefixes {
- base: crate::test_utils::ASTRIA_PREFIX.into(),
- },
- authority_sudo_address: crate::test_utils::astria_address(&alice.address_bytes()),
- ibc_sudo_address: crate::test_utils::astria_address(&alice.address_bytes()),
- ibc_relayer_addresses: vec![],
- native_asset_base_denomination: crate::test_utils::nria(),
- ibc_params: IBCParameters::default(),
- allowed_fee_assets: vec![crate::test_utils::nria().into()],
- fees: default_fees(),
+fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1::GenesisAppState {
+ astria_core::generated::protocol::genesis::v1alpha1::GenesisAppState {
+ authority_sudo_address: Some(
+ get_alice_signing_key()
+ .try_address(ASTRIA_PREFIX)
+ .unwrap()
+ .to_raw(),
+ ),
+ ibc_sudo_address: Some(
+ get_alice_signing_key()
+ .try_address(ASTRIA_PREFIX)
+ .unwrap()
+ .to_raw(),
+ ),
+ ..crate::app::test_utils::proto_genesis_state()
}
}
-fn genesis_state() -> GenesisState {
- unchecked_genesis_state().try_into().unwrap()
+fn genesis_state() -> GenesisAppState {
+ GenesisAppState::try_from_raw(proto_genesis_state()).unwrap()
}
fn test_asset() -> asset::Denom {
@@ -367,9 +371,10 @@ async fn app_execute_transaction_ibc_relayer_change_deletion() {
let alice = get_alice_signing_key();
let alice_address = astria_address(&alice.address_bytes());
- let genesis_state = UncheckedGenesisState {
- ibc_relayer_addresses: vec![alice_address],
- ..unchecked_genesis_state()
+ let genesis_state = {
+ let mut state = proto_genesis_state();
+ state.ibc_relayer_addresses.push(alice_address.to_raw());
+ state
}
.try_into()
.unwrap();
@@ -393,10 +398,13 @@ async fn app_execute_transaction_ibc_relayer_change_deletion() {
async fn app_execute_transaction_ibc_relayer_change_invalid() {
let alice = get_alice_signing_key();
let alice_address = astria_address(&alice.address_bytes());
- let genesis_state = UncheckedGenesisState {
- ibc_sudo_address: astria_address(&[0; 20]),
- ibc_relayer_addresses: vec![alice_address],
- ..unchecked_genesis_state()
+ let genesis_state = {
+ let mut state = proto_genesis_state();
+ state
+ .ibc_sudo_address
+ .replace(astria_address(&[0; 20]).to_raw());
+ state.ibc_relayer_addresses.push(alice_address.to_raw());
+ state
}
.try_into()
.unwrap();
@@ -447,10 +455,15 @@ async fn app_execute_transaction_sudo_address_change_error() {
let alice_address = astria_address(&alice.address_bytes());
let authority_sudo_address = astria_address_from_hex_string(CAROL_ADDRESS);
- let genesis_state = UncheckedGenesisState {
- authority_sudo_address,
- ibc_sudo_address: astria_address(&[0u8; 20]),
- ..unchecked_genesis_state()
+ let genesis_state = {
+ let mut state = proto_genesis_state();
+ state
+ .authority_sudo_address
+ .replace(authority_sudo_address.to_raw());
+ state
+ .ibc_sudo_address
+ .replace(astria_address(&[0u8; 20]).to_raw());
+ state
}
.try_into()
.unwrap();
@@ -509,9 +522,10 @@ async fn app_execute_transaction_fee_asset_change_removal() {
let alice = get_alice_signing_key();
let alice_address = astria_address(&alice.address_bytes());
- let genesis_state = UncheckedGenesisState {
- allowed_fee_assets: vec![nria().into(), test_asset()],
- ..unchecked_genesis_state()
+ let genesis_state = {
+ let mut state = proto_genesis_state();
+ state.allowed_fee_assets.push(test_asset().to_string());
+ state
}
.try_into()
.unwrap();
diff --git a/crates/astria-sequencer/src/bridge/component.rs b/crates/astria-sequencer/src/bridge/component.rs
index cdc8f9b98b..88c105e9d0 100644
--- a/crates/astria-sequencer/src/bridge/component.rs
+++ b/crates/astria-sequencer/src/bridge/component.rs
@@ -1,6 +1,7 @@
use std::sync::Arc;
use anyhow::Result;
+use astria_core::protocol::genesis::v1alpha1::GenesisAppState;
use tendermint::abci::request::{
BeginBlock,
EndBlock,
@@ -15,7 +16,7 @@ pub(crate) struct BridgeComponent;
#[async_trait::async_trait]
impl Component for BridgeComponent {
- type AppState = astria_core::sequencer::GenesisState;
+ type AppState = GenesisAppState;
#[instrument(name = "BridgeComponent::init_chain", skip_all)]
async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> {
diff --git a/crates/astria-sequencer/src/ibc/component.rs b/crates/astria-sequencer/src/ibc/component.rs
index 3a44b6865d..6f0ac0d8db 100644
--- a/crates/astria-sequencer/src/ibc/component.rs
+++ b/crates/astria-sequencer/src/ibc/component.rs
@@ -4,6 +4,7 @@ use anyhow::{
Context,
Result,
};
+use astria_core::protocol::genesis::v1alpha1::GenesisAppState;
use penumbra_ibc::{
component::Ibc,
genesis::Content,
@@ -27,14 +28,14 @@ pub(crate) struct IbcComponent;
#[async_trait::async_trait]
impl Component for IbcComponent {
- type AppState = astria_core::sequencer::GenesisState;
+ type AppState = GenesisAppState;
#[instrument(name = "IbcComponent::init_chain", skip_all)]
async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> {
Ibc::init_chain(
&mut state,
Some(&Content {
- ibc_params: app_state.ibc_params().clone(),
+ ibc_params: app_state.ibc_parameters().clone(),
}),
)
.await;
diff --git a/crates/astria-sequencer/src/sequence/component.rs b/crates/astria-sequencer/src/sequence/component.rs
index 84265321a5..742fb6ea1f 100644
--- a/crates/astria-sequencer/src/sequence/component.rs
+++ b/crates/astria-sequencer/src/sequence/component.rs
@@ -1,6 +1,7 @@
use std::sync::Arc;
use anyhow::Result;
+use astria_core::protocol::genesis::v1alpha1::GenesisAppState;
use tendermint::abci::request::{
BeginBlock,
EndBlock,
@@ -15,7 +16,7 @@ pub(crate) struct SequenceComponent;
#[async_trait::async_trait]
impl Component for SequenceComponent {
- type AppState = astria_core::sequencer::GenesisState;
+ type AppState = GenesisAppState;
#[instrument(name = "SequenceComponent::init_chain", skip_all)]
async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> {
diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs
index 0049267657..f5b6c9d56a 100644
--- a/crates/astria-sequencer/src/service/consensus.rs
+++ b/crates/astria-sequencer/src/service/consensus.rs
@@ -2,6 +2,7 @@ use anyhow::{
bail,
Context,
};
+use astria_core::protocol::genesis::v1alpha1::GenesisAppState;
use cnidarium::Storage;
use tendermint::v0_38::abci::{
request,
@@ -125,9 +126,8 @@ impl Consensus {
bail!("database already initialized");
}
- let genesis_state: astria_core::sequencer::GenesisState =
- serde_json::from_slice(&init_chain.app_state_bytes)
- .context("failed to parse app_state in genesis file")?;
+ let genesis_state: GenesisAppState = serde_json::from_slice(&init_chain.app_state_bytes)
+ .context("failed to parse genesis app state from init chain request")?;
let app_hash = self
.app
.init_chain(
@@ -216,11 +216,6 @@ mod test {
TransactionParams,
UnsignedTransaction,
},
- sequencer::{
- Account,
- AddressPrefixes,
- UncheckedGenesisState,
- },
};
use bytes::Bytes;
use prost::Message as _;
@@ -233,7 +228,6 @@ mod test {
use super::*;
use crate::{
- app::test_utils::default_fees,
mempool::Mempool,
metrics::Metrics,
proposal::commitment::generate_rollup_datas_commitment,
@@ -446,26 +440,22 @@ mod test {
}
async fn new_consensus_service(funded_key: Option) -> (Consensus, Mempool) {
- let accounts = if funded_key.is_some() {
- vec![Account {
- address: crate::test_utils::astria_address(&funded_key.unwrap().address_bytes()),
- balance: 10u128.pow(19),
- }]
+ let accounts = if let Some(funded_key) = funded_key {
+ vec![
+ astria_core::generated::protocol::genesis::v1alpha1::Account {
+ address: Some(
+ crate::test_utils::astria_address(&funded_key.address_bytes()).to_raw(),
+ ),
+ balance: Some(10u128.pow(19).into()),
+ },
+ ]
} else {
vec![]
};
- let genesis_state = UncheckedGenesisState {
- accounts,
- address_prefixes: AddressPrefixes {
- base: crate::test_utils::ASTRIA_PREFIX.into(),
- },
- authority_sudo_address: crate::test_utils::astria_address(&[0; 20]),
- ibc_sudo_address: crate::test_utils::astria_address(&[0; 20]),
- ibc_relayer_addresses: vec![],
- native_asset_base_denomination: crate::test_utils::nria(),
- ibc_params: penumbra_ibc::params::IBCParameters::default(),
- allowed_fee_assets: vec!["nria".parse().unwrap()],
- fees: default_fees(),
+ let genesis_state = {
+ let mut state = crate::app::test_utils::proto_genesis_state();
+ state.accounts = accounts;
+ state
}
.try_into()
.unwrap();
diff --git a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto
new file mode 100644
index 0000000000..89fa8fb3ae
--- /dev/null
+++ b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto
@@ -0,0 +1,47 @@
+syntax = "proto3";
+
+package astria.protocol.genesis.v1alpha1;
+
+import "astria/primitive/v1/types.proto";
+
+message GenesisAppState {
+ string chain_id = 1;
+ AddressPrefixes address_prefixes = 2;
+ repeated Account accounts = 3;
+ astria.primitive.v1.Address authority_sudo_address = 4;
+ astria.primitive.v1.Address ibc_sudo_address = 5;
+ repeated astria.primitive.v1.Address ibc_relayer_addresses = 6;
+ string native_asset_base_denomination = 7;
+ IbcParameters ibc_parameters = 8;
+ repeated string allowed_fee_assets = 9;
+ Fees fees = 10;
+}
+
+message Account {
+ astria.primitive.v1.Address address = 1;
+ astria.primitive.v1.Uint128 balance = 2;
+}
+
+message AddressPrefixes {
+ string base = 1;
+}
+
+// IBC configuration data.
+message IbcParameters {
+ // Whether IBC (forming connections, processing IBC packets) is enabled.
+ bool ibc_enabled = 1;
+ // Whether inbound ICS-20 transfers are enabled
+ bool inbound_ics20_transfers_enabled = 2;
+ // Whether outbound ICS-20 transfers are enabled
+ bool outbound_ics20_transfers_enabled = 3;
+}
+
+message Fees {
+ astria.primitive.v1.Uint128 transfer_base_fee = 1;
+ astria.primitive.v1.Uint128 sequence_base_fee = 2;
+ astria.primitive.v1.Uint128 sequence_byte_cost_multiplier = 3;
+ astria.primitive.v1.Uint128 init_bridge_account_base_fee = 4;
+ astria.primitive.v1.Uint128 bridge_lock_byte_cost_multiplier = 5;
+ astria.primitive.v1.Uint128 bridge_sudo_change_fee = 6;
+ astria.primitive.v1.Uint128 ics20_withdrawal_base_fee = 7;
+}
diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs
index 20700a0d40..5404a5392e 100644
--- a/tools/protobuf-compiler/src/main.rs
+++ b/tools/protobuf-compiler/src/main.rs
@@ -77,6 +77,10 @@ fn main() {
"crate::generated::astria_vendored::tendermint::abci::ValidatorUpdate",
)
.type_attribute(".astria.primitive.v1.Uint128", "#[derive(Copy)]")
+ .type_attribute(
+ ".astria.protocol.genesis.v1alpha1.IbcParameters",
+ "#[derive(Copy)]",
+ )
.use_arc_self(true)
// override prost-types with pbjson-types
.compile_well_known_types(true)