From 7c396415649c0dea0fa88d62e8bc54342f8c745a Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 13:57:22 -0400 Subject: [PATCH 01/89] add slinky proto files and generated rust --- .../src/generated/cosmos.app.v1alpha1.rs | 101 + .../generated/cosmos.app.v1alpha1.serde.rs | 329 +++ .../src/generated/slinky.abci.v1.rs | 17 + .../src/generated/slinky.abci.v1.serde.rs | 96 + .../generated/slinky.marketmap.module.v1.rs | 21 + .../slinky.marketmap.module.v1.serde.rs | 109 + .../src/generated/slinky.marketmap.v1.rs | 1578 +++++++++++ .../generated/slinky.marketmap.v1.serde.rs | 2405 +++++++++++++++++ .../src/generated/slinky.oracle.module.v1.rs | 16 + .../slinky.oracle.module.v1.serde.rs | 91 + .../src/generated/slinky.oracle.v1.rs | 1219 +++++++++ .../src/generated/slinky.oracle.v1.serde.rs | 1639 +++++++++++ .../src/generated/slinky.service.v1.rs | 438 +++ .../src/generated/slinky.service.v1.serde.rs | 344 +++ .../src/generated/slinky.types.v1.rs | 17 + .../src/generated/slinky.types.v1.serde.rs | 108 + .../cosmos/app/v1alpha1/module.proto | 81 + .../cosmos_sdk/cosmos/msg/v1/msg.proto | 25 + .../cosmos_sdk/cosmos_proto/cosmos.proto | 112 + .../slinky/abci/v1/vote_extensions.proto | 12 + .../slinky/marketmap/module/v1/module.proto | 21 + .../slinky/marketmap/v1/genesis.proto | 22 + .../vendored/slinky/marketmap/v1/market.proto | 73 + .../vendored/slinky/marketmap/v1/params.proto | 15 + .../vendored/slinky/marketmap/v1/query.proto | 83 + proto/vendored/slinky/marketmap/v1/tx.proto | 121 + .../slinky/oracle/module/v1/module.proto | 16 + proto/vendored/slinky/oracle/v1/genesis.proto | 65 + proto/vendored/slinky/oracle/v1/query.proto | 86 + proto/vendored/slinky/oracle/v1/tx.proto | 62 + proto/vendored/slinky/service/v1/oracle.proto | 42 + .../slinky/types/v1/currency_pair.proto | 11 + tools/protobuf-compiler/src/main.rs | 2 + 33 files changed, 9377 insertions(+) create mode 100644 crates/astria-core/src/generated/cosmos.app.v1alpha1.rs create mode 100644 crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs create mode 100644 crates/astria-core/src/generated/slinky.abci.v1.rs create mode 100644 crates/astria-core/src/generated/slinky.abci.v1.serde.rs create mode 100644 crates/astria-core/src/generated/slinky.marketmap.module.v1.rs create mode 100644 crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs create mode 100644 crates/astria-core/src/generated/slinky.marketmap.v1.rs create mode 100644 crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs create mode 100644 crates/astria-core/src/generated/slinky.oracle.module.v1.rs create mode 100644 crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs create mode 100644 crates/astria-core/src/generated/slinky.oracle.v1.rs create mode 100644 crates/astria-core/src/generated/slinky.oracle.v1.serde.rs create mode 100644 crates/astria-core/src/generated/slinky.service.v1.rs create mode 100644 crates/astria-core/src/generated/slinky.service.v1.serde.rs create mode 100644 crates/astria-core/src/generated/slinky.types.v1.rs create mode 100644 crates/astria-core/src/generated/slinky.types.v1.serde.rs create mode 100644 proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto create mode 100644 proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto create mode 100644 proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto create mode 100644 proto/vendored/slinky/abci/v1/vote_extensions.proto create mode 100644 proto/vendored/slinky/marketmap/module/v1/module.proto create mode 100644 proto/vendored/slinky/marketmap/v1/genesis.proto create mode 100644 proto/vendored/slinky/marketmap/v1/market.proto create mode 100644 proto/vendored/slinky/marketmap/v1/params.proto create mode 100644 proto/vendored/slinky/marketmap/v1/query.proto create mode 100644 proto/vendored/slinky/marketmap/v1/tx.proto create mode 100644 proto/vendored/slinky/oracle/module/v1/module.proto create mode 100644 proto/vendored/slinky/oracle/v1/genesis.proto create mode 100644 proto/vendored/slinky/oracle/v1/query.proto create mode 100644 proto/vendored/slinky/oracle/v1/tx.proto create mode 100644 proto/vendored/slinky/service/v1/oracle.proto create mode 100644 proto/vendored/slinky/types/v1/currency_pair.proto diff --git a/crates/astria-core/src/generated/cosmos.app.v1alpha1.rs b/crates/astria-core/src/generated/cosmos.app.v1alpha1.rs new file mode 100644 index 0000000000..ec85bb0b92 --- /dev/null +++ b/crates/astria-core/src/generated/cosmos.app.v1alpha1.rs @@ -0,0 +1,101 @@ +/// ModuleDescriptor describes an app module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ModuleDescriptor { + /// go_import names the package that should be imported by an app to load the + /// module in the runtime module registry. It is required to make debugging + /// of configuration errors easier for users. + #[prost(string, tag = "1")] + pub go_import: ::prost::alloc::string::String, + /// use_package refers to a protobuf package that this module + /// uses and exposes to the world. In an app, only one module should "use" + /// or own a single protobuf package. It is assumed that the module uses + /// all of the .proto files in a single package. + #[prost(message, repeated, tag = "2")] + pub use_package: ::prost::alloc::vec::Vec, + /// can_migrate_from defines which module versions this module can migrate + /// state from. The framework will check that one module version is able to + /// migrate from a previous module version before attempting to update its + /// config. It is assumed that modules can transitively migrate from earlier + /// versions. For instance if v3 declares it can migrate from v2, and v2 + /// declares it can migrate from v1, the framework knows how to migrate + /// from v1 to v3, assuming all 3 module versions are registered at runtime. + #[prost(message, repeated, tag = "3")] + pub can_migrate_from: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for ModuleDescriptor { + const NAME: &'static str = "ModuleDescriptor"; + const PACKAGE: &'static str = "cosmos.app.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("cosmos.app.v1alpha1.{}", Self::NAME) + } +} +/// PackageReference is a reference to a protobuf package used by a module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PackageReference { + /// name is the fully-qualified name of the package. + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + /// revision is the optional revision of the package that is being used. + /// Protobuf packages used in Cosmos should generally have a major version + /// as the last part of the package name, ex. foo.bar.baz.v1. + /// The revision of a package can be thought of as the minor version of a + /// package which has additional backwards compatible definitions that weren't + /// present in a previous version. + /// + /// A package should indicate its revision with a source code comment + /// above the package declaration in one of its files containing the + /// text "Revision N" where N is an integer revision. All packages start + /// at revision 0 the first time they are released in a module. + /// + /// When a new version of a module is released and items are added to existing + /// .proto files, these definitions should contain comments of the form + /// "Since: Revision N" where N is an integer revision. + /// + /// When the module runtime starts up, it will check the pinned proto + /// image and panic if there are runtime protobuf definitions that are not + /// in the pinned descriptor which do not have + /// a "Since Revision N" comment or have a "Since Revision N" comment where + /// N is <= to the revision specified here. This indicates that the protobuf + /// files have been updated, but the pinned file descriptor hasn't. + /// + /// If there are items in the pinned file descriptor with a revision + /// greater than the value indicated here, this will also cause a panic + /// as it may mean that the pinned descriptor for a legacy module has been + /// improperly updated or that there is some other versioning discrepancy. + /// Runtime protobuf definitions will also be checked for compatibility + /// with pinned file descriptors to make sure there are no incompatible changes. + /// + /// This behavior ensures that: + /// * pinned proto images are up-to-date + /// * protobuf files are carefully annotated with revision comments which + /// are important good client UX + /// * protobuf files are changed in backwards and forwards compatible ways + #[prost(uint32, tag = "2")] + pub revision: u32, +} +impl ::prost::Name for PackageReference { + const NAME: &'static str = "PackageReference"; + const PACKAGE: &'static str = "cosmos.app.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("cosmos.app.v1alpha1.{}", Self::NAME) + } +} +/// MigrateFromInfo is information on a module version that a newer module +/// can migrate from. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MigrateFromInfo { + /// module is the fully-qualified protobuf name of the module config object + /// for the previous module version, ex: "cosmos.group.module.v1.Module". + #[prost(string, tag = "1")] + pub module: ::prost::alloc::string::String, +} +impl ::prost::Name for MigrateFromInfo { + const NAME: &'static str = "MigrateFromInfo"; + const PACKAGE: &'static str = "cosmos.app.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("cosmos.app.v1alpha1.{}", Self::NAME) + } +} diff --git a/crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs b/crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs new file mode 100644 index 0000000000..a1f82e3385 --- /dev/null +++ b/crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs @@ -0,0 +1,329 @@ +impl serde::Serialize for MigrateFromInfo { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.module.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("cosmos.app.v1alpha1.MigrateFromInfo", len)?; + if !self.module.is_empty() { + struct_ser.serialize_field("module", &self.module)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MigrateFromInfo { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "module", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Module, + } + 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 { + "module" => Ok(GeneratedField::Module), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MigrateFromInfo; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct cosmos.app.v1alpha1.MigrateFromInfo") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut module__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Module => { + if module__.is_some() { + return Err(serde::de::Error::duplicate_field("module")); + } + module__ = Some(map_.next_value()?); + } + } + } + Ok(MigrateFromInfo { + module: module__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("cosmos.app.v1alpha1.MigrateFromInfo", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ModuleDescriptor { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.go_import.is_empty() { + len += 1; + } + if !self.use_package.is_empty() { + len += 1; + } + if !self.can_migrate_from.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("cosmos.app.v1alpha1.ModuleDescriptor", len)?; + if !self.go_import.is_empty() { + struct_ser.serialize_field("go_import", &self.go_import)?; + } + if !self.use_package.is_empty() { + struct_ser.serialize_field("use_package", &self.use_package)?; + } + if !self.can_migrate_from.is_empty() { + struct_ser.serialize_field("can_migrate_from", &self.can_migrate_from)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ModuleDescriptor { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "go_import", + "goImport", + "use_package", + "usePackage", + "can_migrate_from", + "canMigrateFrom", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + GoImport, + UsePackage, + CanMigrateFrom, + } + 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 { + "goImport" | "go_import" => Ok(GeneratedField::GoImport), + "usePackage" | "use_package" => Ok(GeneratedField::UsePackage), + "canMigrateFrom" | "can_migrate_from" => Ok(GeneratedField::CanMigrateFrom), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ModuleDescriptor; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct cosmos.app.v1alpha1.ModuleDescriptor") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut go_import__ = None; + let mut use_package__ = None; + let mut can_migrate_from__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::GoImport => { + if go_import__.is_some() { + return Err(serde::de::Error::duplicate_field("goImport")); + } + go_import__ = Some(map_.next_value()?); + } + GeneratedField::UsePackage => { + if use_package__.is_some() { + return Err(serde::de::Error::duplicate_field("usePackage")); + } + use_package__ = Some(map_.next_value()?); + } + GeneratedField::CanMigrateFrom => { + if can_migrate_from__.is_some() { + return Err(serde::de::Error::duplicate_field("canMigrateFrom")); + } + can_migrate_from__ = Some(map_.next_value()?); + } + } + } + Ok(ModuleDescriptor { + go_import: go_import__.unwrap_or_default(), + use_package: use_package__.unwrap_or_default(), + can_migrate_from: can_migrate_from__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("cosmos.app.v1alpha1.ModuleDescriptor", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for PackageReference { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.name.is_empty() { + len += 1; + } + if self.revision != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("cosmos.app.v1alpha1.PackageReference", len)?; + if !self.name.is_empty() { + struct_ser.serialize_field("name", &self.name)?; + } + if self.revision != 0 { + struct_ser.serialize_field("revision", &self.revision)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for PackageReference { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "name", + "revision", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Name, + Revision, + } + 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 { + "name" => Ok(GeneratedField::Name), + "revision" => Ok(GeneratedField::Revision), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = PackageReference; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct cosmos.app.v1alpha1.PackageReference") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut name__ = None; + let mut revision__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Name => { + if name__.is_some() { + return Err(serde::de::Error::duplicate_field("name")); + } + name__ = Some(map_.next_value()?); + } + GeneratedField::Revision => { + if revision__.is_some() { + return Err(serde::de::Error::duplicate_field("revision")); + } + revision__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(PackageReference { + name: name__.unwrap_or_default(), + revision: revision__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("cosmos.app.v1alpha1.PackageReference", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/slinky.abci.v1.rs b/crates/astria-core/src/generated/slinky.abci.v1.rs new file mode 100644 index 0000000000..38cc867c1d --- /dev/null +++ b/crates/astria-core/src/generated/slinky.abci.v1.rs @@ -0,0 +1,17 @@ +/// OracleVoteExtension defines the vote extension structure for oracle prices. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OracleVoteExtension { + /// Prices defines a map of id(CurrencyPair) -> price.Bytes() . i.e. 1 -> + /// 0x123.. (bytes). Notice the `id` function is determined by the + /// `CurrencyPairIDStrategy` used in the VoteExtensionHandler. + #[prost(map = "uint64, bytes", tag = "1")] + pub prices: ::std::collections::HashMap>, +} +impl ::prost::Name for OracleVoteExtension { + const NAME: &'static str = "OracleVoteExtension"; + const PACKAGE: &'static str = "slinky.abci.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.abci.v1.{}", Self::NAME) + } +} diff --git a/crates/astria-core/src/generated/slinky.abci.v1.serde.rs b/crates/astria-core/src/generated/slinky.abci.v1.serde.rs new file mode 100644 index 0000000000..d04c436a02 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.abci.v1.serde.rs @@ -0,0 +1,96 @@ +impl serde::Serialize for OracleVoteExtension { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.prices.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.abci.v1.OracleVoteExtension", len)?; + if !self.prices.is_empty() { + let v: std::collections::HashMap<_, _> = self.prices.iter() + .map(|(k, v)| (k, pbjson::private::base64::encode(v))).collect(); + struct_ser.serialize_field("prices", &v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for OracleVoteExtension { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "prices", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Prices, + } + 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 { + "prices" => Ok(GeneratedField::Prices), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = OracleVoteExtension; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.abci.v1.OracleVoteExtension") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut prices__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Prices => { + if prices__.is_some() { + return Err(serde::de::Error::duplicate_field("prices")); + } + prices__ = Some( + map_.next_value::, ::pbjson::private::BytesDeserialize<_>>>()? + .into_iter().map(|(k,v)| (k.0, v.0)).collect() + ); + } + } + } + Ok(OracleVoteExtension { + prices: prices__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.abci.v1.OracleVoteExtension", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/slinky.marketmap.module.v1.rs b/crates/astria-core/src/generated/slinky.marketmap.module.v1.rs new file mode 100644 index 0000000000..1f8612a429 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.marketmap.module.v1.rs @@ -0,0 +1,21 @@ +/// Module is the config object of the builder module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Module { + /// Authority defines the custom module authority. If not set, defaults to the + /// governance module. + #[prost(string, tag = "1")] + pub authority: ::prost::alloc::string::String, + /// HooksOrder specifies the order of marketmap hooks and should be a list + /// of module names which provide a marketmap hooks instance. If no order is + /// provided, then hooks will be applied in alphabetical order of module names. + #[prost(string, repeated, tag = "2")] + pub hooks_order: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +impl ::prost::Name for Module { + const NAME: &'static str = "Module"; + const PACKAGE: &'static str = "slinky.marketmap.module.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.module.v1.{}", Self::NAME) + } +} diff --git a/crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs b/crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs new file mode 100644 index 0000000000..877db8b5fc --- /dev/null +++ b/crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs @@ -0,0 +1,109 @@ +impl serde::Serialize for Module { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.authority.is_empty() { + len += 1; + } + if !self.hooks_order.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.module.v1.Module", len)?; + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + if !self.hooks_order.is_empty() { + struct_ser.serialize_field("hooks_order", &self.hooks_order)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Module { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "authority", + "hooks_order", + "hooksOrder", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Authority, + HooksOrder, + } + 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 { + "authority" => Ok(GeneratedField::Authority), + "hooksOrder" | "hooks_order" => Ok(GeneratedField::HooksOrder), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Module; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.module.v1.Module") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut authority__ = None; + let mut hooks_order__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + GeneratedField::HooksOrder => { + if hooks_order__.is_some() { + return Err(serde::de::Error::duplicate_field("hooksOrder")); + } + hooks_order__ = Some(map_.next_value()?); + } + } + } + Ok(Module { + authority: authority__.unwrap_or_default(), + hooks_order: hooks_order__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.module.v1.Module", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/slinky.marketmap.v1.rs b/crates/astria-core/src/generated/slinky.marketmap.v1.rs new file mode 100644 index 0000000000..6d334e4dd0 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.marketmap.v1.rs @@ -0,0 +1,1578 @@ +/// Market encapsulates a Ticker and its provider-specific configuration. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Market { + /// Ticker represents a price feed for a given asset pair i.e. BTC/USD. The + /// price feed is scaled to a number of decimal places and has a minimum number + /// of providers required to consider the ticker valid. + #[prost(message, optional, tag = "1")] + pub ticker: ::core::option::Option, + /// ProviderConfigs is the list of provider-specific configs for this Market. + #[prost(message, repeated, tag = "2")] + pub provider_configs: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for Market { + const NAME: &'static str = "Market"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// Ticker represents a price feed for a given asset pair i.e. BTC/USD. The price +/// feed is scaled to a number of decimal places and has a minimum number of +/// providers required to consider the ticker valid. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Ticker { + /// CurrencyPair is the currency pair for this ticker. + #[prost(message, optional, tag = "1")] + pub currency_pair: ::core::option::Option, + /// Decimals is the number of decimal places for the ticker. The number of + /// decimal places is used to convert the price to a human-readable format. + #[prost(uint64, tag = "2")] + pub decimals: u64, + /// MinProviderCount is the minimum number of providers required to consider + /// the ticker valid. + #[prost(uint64, tag = "3")] + pub min_provider_count: u64, + /// Enabled is the flag that denotes if the Ticker is enabled for price + /// fetching by an oracle. + #[prost(bool, tag = "14")] + pub enabled: bool, + /// MetadataJSON is a string of JSON that encodes any extra configuration + /// for the given ticker. + #[prost(string, tag = "15")] + pub metadata_json: ::prost::alloc::string::String, +} +impl ::prost::Name for Ticker { + const NAME: &'static str = "Ticker"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProviderConfig { + /// Name corresponds to the name of the provider for which the configuration is + /// being set. + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + /// OffChainTicker is the off-chain representation of the ticker i.e. BTC/USD. + /// The off-chain ticker is unique to a given provider and is used to fetch the + /// price of the ticker from the provider. + #[prost(string, tag = "2")] + pub off_chain_ticker: ::prost::alloc::string::String, + /// NormalizeByPair is the currency pair for this ticker to be normalized by. + /// For example, if the desired Ticker is BTC/USD, this market could be reached + /// using: OffChainTicker = BTC/USDT NormalizeByPair = USDT/USD This field is + /// optional and nullable. + #[prost(message, optional, tag = "3")] + pub normalize_by_pair: ::core::option::Option, + /// Invert is a boolean indicating if the BASE and QUOTE of the market should + /// be inverted. i.e. BASE -> QUOTE, QUOTE -> BASE + #[prost(bool, tag = "4")] + pub invert: bool, + /// MetadataJSON is a string of JSON that encodes any extra configuration + /// for the given provider config. + #[prost(string, tag = "15")] + pub metadata_json: ::prost::alloc::string::String, +} +impl ::prost::Name for ProviderConfig { + const NAME: &'static str = "ProviderConfig"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MarketMap maps ticker strings to their Markets. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketMap { + /// Markets is the full list of tickers and their associated configurations + /// to be stored on-chain. + #[prost(map = "string, message", tag = "1")] + pub markets: ::std::collections::HashMap<::prost::alloc::string::String, Market>, +} +impl ::prost::Name for MarketMap { + const NAME: &'static str = "MarketMap"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// Params defines the parameters for the x/marketmap module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Params { + /// MarketAuthorities is the list of authority accounts that are able to + /// control updating the marketmap. + #[prost(string, repeated, tag = "1")] + pub market_authorities: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Admin is an address that can remove addresses from the MarketAuthorities + /// list. Only governance can add to the MarketAuthorities or change the Admin. + #[prost(string, tag = "2")] + pub admin: ::prost::alloc::string::String, +} +impl ::prost::Name for Params { + const NAME: &'static str = "Params"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// GenesisState defines the x/marketmap module's genesis state. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GenesisState { + /// MarketMap defines the global set of market configurations for all providers + /// and markets. + #[prost(message, optional, tag = "1")] + pub market_map: ::core::option::Option, + /// LastUpdated is the last block height that the market map was updated. + /// This field can be used as an optimization for clients checking if there + /// is a new update to the map. + #[prost(uint64, tag = "2")] + pub last_updated: u64, + /// Params are the parameters for the x/marketmap module. + #[prost(message, optional, tag = "3")] + pub params: ::core::option::Option, +} +impl ::prost::Name for GenesisState { + const NAME: &'static str = "GenesisState"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MarketMapRequest is the query request for the MarketMap query. +/// It takes no arguments. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketMapRequest {} +impl ::prost::Name for MarketMapRequest { + const NAME: &'static str = "MarketMapRequest"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MarketMapResponse is the query response for the MarketMap query. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketMapResponse { + /// MarketMap defines the global set of market configurations for all providers + /// and markets. + #[prost(message, optional, tag = "1")] + pub market_map: ::core::option::Option, + /// LastUpdated is the last block height that the market map was updated. + /// This field can be used as an optimization for clients checking if there + /// is a new update to the map. + #[prost(uint64, tag = "2")] + pub last_updated: u64, + /// ChainId is the chain identifier for the market map. + #[prost(string, tag = "3")] + pub chain_id: ::prost::alloc::string::String, +} +impl ::prost::Name for MarketMapResponse { + const NAME: &'static str = "MarketMapResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MarketRequest is the query request for the Market query. +/// It takes the currency pair of the market as an argument. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketRequest { + /// CurrencyPair is the currency pair associated with the market being + /// requested. + #[prost(message, optional, tag = "1")] + pub currency_pair: ::core::option::Option, +} +impl ::prost::Name for MarketRequest { + const NAME: &'static str = "MarketRequest"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MarketResponse is the query response for the Market query. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketResponse { + /// Market is the configuration of a single market to be price-fetched for. + #[prost(message, optional, tag = "1")] + pub market: ::core::option::Option, +} +impl ::prost::Name for MarketResponse { + const NAME: &'static str = "MarketResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// ParamsRequest is the request type for the Query/Params RPC method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ParamsRequest {} +impl ::prost::Name for ParamsRequest { + const NAME: &'static str = "ParamsRequest"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// ParamsResponse is the response type for the Query/Params RPC method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ParamsResponse { + #[prost(message, optional, tag = "1")] + pub params: ::core::option::Option, +} +impl ::prost::Name for ParamsResponse { + const NAME: &'static str = "ParamsResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// LastUpdatedRequest is the request type for the Query/LastUpdated RPC +/// method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LastUpdatedRequest {} +impl ::prost::Name for LastUpdatedRequest { + const NAME: &'static str = "LastUpdatedRequest"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// LastUpdatedResponse is the response type for the Query/LastUpdated RPC +/// method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LastUpdatedResponse { + #[prost(uint64, tag = "1")] + pub last_updated: u64, +} +impl ::prost::Name for LastUpdatedResponse { + const NAME: &'static str = "LastUpdatedResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod query_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Query is the query service for the x/marketmap module. + #[derive(Debug, Clone)] + pub struct QueryClient { + inner: tonic::client::Grpc, + } + impl QueryClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl QueryClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> QueryClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + QueryClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// MarketMap returns the full market map stored in the x/marketmap + /// module. + pub async fn market_map( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Query/MarketMap", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Query", "MarketMap")); + self.inner.unary(req, path, codec).await + } + /// Market returns a market stored in the x/marketmap + /// module. + pub async fn market( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Query/Market", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Query", "Market")); + self.inner.unary(req, path, codec).await + } + /// LastUpdated returns the last height the market map was updated at. + pub async fn last_updated( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Query/LastUpdated", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Query", "LastUpdated")); + self.inner.unary(req, path, codec).await + } + /// Params returns the current x/marketmap module parameters. + pub async fn params( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Query/Params", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Query", "Params")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod query_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with QueryServer. + #[async_trait] + pub trait Query: Send + Sync + 'static { + /// MarketMap returns the full market map stored in the x/marketmap + /// module. + async fn market_map( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Market returns a market stored in the x/marketmap + /// module. + async fn market( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// LastUpdated returns the last height the market map was updated at. + async fn last_updated( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Params returns the current x/marketmap module parameters. + async fn params( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + } + /// Query is the query service for the x/marketmap module. + #[derive(Debug)] + pub struct QueryServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl QueryServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for QueryServer + where + T: Query, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/slinky.marketmap.v1.Query/MarketMap" => { + #[allow(non_camel_case_types)] + struct MarketMapSvc(pub Arc); + impl tonic::server::UnaryService + for MarketMapSvc { + type Response = super::MarketMapResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::market_map(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = MarketMapSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.marketmap.v1.Query/Market" => { + #[allow(non_camel_case_types)] + struct MarketSvc(pub Arc); + impl tonic::server::UnaryService + for MarketSvc { + type Response = super::MarketResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::market(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = MarketSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.marketmap.v1.Query/LastUpdated" => { + #[allow(non_camel_case_types)] + struct LastUpdatedSvc(pub Arc); + impl tonic::server::UnaryService + for LastUpdatedSvc { + type Response = super::LastUpdatedResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::last_updated(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = LastUpdatedSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.marketmap.v1.Query/Params" => { + #[allow(non_camel_case_types)] + struct ParamsSvc(pub Arc); + impl tonic::server::UnaryService + for ParamsSvc { + type Response = super::ParamsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::params(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ParamsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for QueryServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for QueryServer { + const NAME: &'static str = "slinky.marketmap.v1.Query"; + } +} +/// MsgUpsertMarkets defines a message carrying a payload for performing market upserts (update or +/// create if does not exist) in the x/marketmap module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgUpsertMarkets { + /// Authority is the signer of this transaction. This authority must be + /// authorized by the module to execute the message. + #[prost(string, tag = "1")] + pub authority: ::prost::alloc::string::String, + /// CreateMarkets is the list of all markets to be created for the given + /// transaction. + #[prost(message, repeated, tag = "2")] + pub markets: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for MsgUpsertMarkets { + const NAME: &'static str = "MsgUpsertMarkets"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgUpsertMarketsResponse is the response from the UpsertMarkets API in the x/marketmap module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgUpsertMarketsResponse { + /// UpdatedMarkets is a map between the ticker and whether the market was updated. + #[prost(map = "string, bool", tag = "1")] + pub market_updates: ::std::collections::HashMap< + ::prost::alloc::string::String, + bool, + >, +} +impl ::prost::Name for MsgUpsertMarketsResponse { + const NAME: &'static str = "MsgUpsertMarketsResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgCreateMarkets defines a message carrying a payload for creating markets in +/// the x/marketmap module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgCreateMarkets { + /// Authority is the signer of this transaction. This authority must be + /// authorized by the module to execute the message. + #[prost(string, tag = "1")] + pub authority: ::prost::alloc::string::String, + /// CreateMarkets is the list of all markets to be created for the given + /// transaction. + #[prost(message, repeated, tag = "2")] + pub create_markets: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for MsgCreateMarkets { + const NAME: &'static str = "MsgCreateMarkets"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgUpdateMarketMapResponse is the response message for MsgUpdateMarketMap. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgCreateMarketsResponse {} +impl ::prost::Name for MsgCreateMarketsResponse { + const NAME: &'static str = "MsgCreateMarketsResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgUpdateMarkets defines a message carrying a payload for updating the +/// x/marketmap module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgUpdateMarkets { + /// Authority is the signer of this transaction. This authority must be + /// authorized by the module to execute the message. + #[prost(string, tag = "1")] + pub authority: ::prost::alloc::string::String, + /// UpdateMarkets is the list of all markets to be updated for the given + /// transaction. + #[prost(message, repeated, tag = "2")] + pub update_markets: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for MsgUpdateMarkets { + const NAME: &'static str = "MsgUpdateMarkets"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgUpdateMarketsResponse is the response message for MsgUpdateMarkets. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgUpdateMarketsResponse {} +impl ::prost::Name for MsgUpdateMarketsResponse { + const NAME: &'static str = "MsgUpdateMarketsResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgParams defines the Msg/Params request type. It contains the +/// new parameters for the x/marketmap module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgParams { + /// Params defines the new parameters for the x/marketmap module. + #[prost(message, optional, tag = "1")] + pub params: ::core::option::Option, + /// Authority defines the authority that is updating the x/marketmap module + /// parameters. + #[prost(string, tag = "2")] + pub authority: ::prost::alloc::string::String, +} +impl ::prost::Name for MsgParams { + const NAME: &'static str = "MsgParams"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgParamsResponse defines the Msg/Params response type. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgParamsResponse {} +impl ::prost::Name for MsgParamsResponse { + const NAME: &'static str = "MsgParamsResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgRemoveMarketAuthorities defines the Msg/RemoveMarketAuthoritiesResponse +/// request type. It contains the new addresses to remove from the list of +/// authorities +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgRemoveMarketAuthorities { + /// RemoveAddresses is the list of addresses to remove. + #[prost(string, repeated, tag = "1")] + pub remove_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Admin defines the authority that is the x/marketmap + /// Admin account. This account is set in the module parameters. + #[prost(string, tag = "2")] + pub admin: ::prost::alloc::string::String, +} +impl ::prost::Name for MsgRemoveMarketAuthorities { + const NAME: &'static str = "MsgRemoveMarketAuthorities"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// MsgRemoveMarketAuthoritiesResponse defines the +/// Msg/RemoveMarketAuthoritiesResponse response type. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgRemoveMarketAuthoritiesResponse {} +impl ::prost::Name for MsgRemoveMarketAuthoritiesResponse { + const NAME: &'static str = "MsgRemoveMarketAuthoritiesResponse"; + const PACKAGE: &'static str = "slinky.marketmap.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod msg_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Msg is the message service for the x/marketmap module. + #[derive(Debug, Clone)] + pub struct MsgClient { + inner: tonic::client::Grpc, + } + impl MsgClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl MsgClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> MsgClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + MsgClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// CreateMarkets creates markets from the given message. + pub async fn create_markets( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Msg/CreateMarkets", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "CreateMarkets")); + self.inner.unary(req, path, codec).await + } + /// UpdateMarkets updates markets from the given message. + pub async fn update_markets( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Msg/UpdateMarkets", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "UpdateMarkets")); + self.inner.unary(req, path, codec).await + } + /// UpdateParams defines a method for updating the x/marketmap module + /// parameters. + pub async fn update_params( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Msg/UpdateParams", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "UpdateParams")); + self.inner.unary(req, path, codec).await + } + /// RemoveMarketAuthorities defines a method for removing market authorities + /// from the x/marketmap module. the signer must be the admin. + pub async fn remove_market_authorities( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Msg/RemoveMarketAuthorities", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("slinky.marketmap.v1.Msg", "RemoveMarketAuthorities"), + ); + self.inner.unary(req, path, codec).await + } + /// UpsertMarkets wraps both Create / Update markets into a single message. Specifically + /// if a market does not exist it will be created, otherwise it will be updated. The response + /// will be a map between ticker -> updated. + pub async fn upsert_markets( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.marketmap.v1.Msg/UpsertMarkets", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "UpsertMarkets")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod msg_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with MsgServer. + #[async_trait] + pub trait Msg: Send + Sync + 'static { + /// CreateMarkets creates markets from the given message. + async fn create_markets( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// UpdateMarkets updates markets from the given message. + async fn update_markets( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// UpdateParams defines a method for updating the x/marketmap module + /// parameters. + async fn update_params( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// RemoveMarketAuthorities defines a method for removing market authorities + /// from the x/marketmap module. the signer must be the admin. + async fn remove_market_authorities( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// UpsertMarkets wraps both Create / Update markets into a single message. Specifically + /// if a market does not exist it will be created, otherwise it will be updated. The response + /// will be a map between ticker -> updated. + async fn upsert_markets( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Msg is the message service for the x/marketmap module. + #[derive(Debug)] + pub struct MsgServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl MsgServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for MsgServer + where + T: Msg, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/slinky.marketmap.v1.Msg/CreateMarkets" => { + #[allow(non_camel_case_types)] + struct CreateMarketsSvc(pub Arc); + impl tonic::server::UnaryService + for CreateMarketsSvc { + type Response = super::MsgCreateMarketsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_markets(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateMarketsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.marketmap.v1.Msg/UpdateMarkets" => { + #[allow(non_camel_case_types)] + struct UpdateMarketsSvc(pub Arc); + impl tonic::server::UnaryService + for UpdateMarketsSvc { + type Response = super::MsgUpdateMarketsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::update_markets(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpdateMarketsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.marketmap.v1.Msg/UpdateParams" => { + #[allow(non_camel_case_types)] + struct UpdateParamsSvc(pub Arc); + impl tonic::server::UnaryService + for UpdateParamsSvc { + type Response = super::MsgParamsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::update_params(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpdateParamsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.marketmap.v1.Msg/RemoveMarketAuthorities" => { + #[allow(non_camel_case_types)] + struct RemoveMarketAuthoritiesSvc(pub Arc); + impl< + T: Msg, + > tonic::server::UnaryService + for RemoveMarketAuthoritiesSvc { + type Response = super::MsgRemoveMarketAuthoritiesResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::remove_market_authorities(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = RemoveMarketAuthoritiesSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.marketmap.v1.Msg/UpsertMarkets" => { + #[allow(non_camel_case_types)] + struct UpsertMarketsSvc(pub Arc); + impl tonic::server::UnaryService + for UpsertMarketsSvc { + type Response = super::MsgUpsertMarketsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::upsert_markets(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpsertMarketsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for MsgServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for MsgServer { + const NAME: &'static str = "slinky.marketmap.v1.Msg"; + } +} diff --git a/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs b/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs new file mode 100644 index 0000000000..eff856eef6 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs @@ -0,0 +1,2405 @@ +impl serde::Serialize for GenesisState { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.market_map.is_some() { + len += 1; + } + if self.last_updated != 0 { + len += 1; + } + if self.params.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.GenesisState", len)?; + if let Some(v) = self.market_map.as_ref() { + struct_ser.serialize_field("market_map", v)?; + } + if self.last_updated != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("last_updated", ToString::to_string(&self.last_updated).as_str())?; + } + if let Some(v) = self.params.as_ref() { + struct_ser.serialize_field("params", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GenesisState { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market_map", + "marketMap", + "last_updated", + "lastUpdated", + "params", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MarketMap, + LastUpdated, + Params, + } + 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 { + "marketMap" | "market_map" => Ok(GeneratedField::MarketMap), + "lastUpdated" | "last_updated" => Ok(GeneratedField::LastUpdated), + "params" => Ok(GeneratedField::Params), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GenesisState; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.GenesisState") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market_map__ = None; + let mut last_updated__ = None; + let mut params__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MarketMap => { + if market_map__.is_some() { + return Err(serde::de::Error::duplicate_field("marketMap")); + } + market_map__ = map_.next_value()?; + } + GeneratedField::LastUpdated => { + if last_updated__.is_some() { + return Err(serde::de::Error::duplicate_field("lastUpdated")); + } + last_updated__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Params => { + if params__.is_some() { + return Err(serde::de::Error::duplicate_field("params")); + } + params__ = map_.next_value()?; + } + } + } + Ok(GenesisState { + market_map: market_map__, + last_updated: last_updated__.unwrap_or_default(), + params: params__, + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.GenesisState", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for LastUpdatedRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.LastUpdatedRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for LastUpdatedRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = LastUpdatedRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.LastUpdatedRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(LastUpdatedRequest { + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.LastUpdatedRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for LastUpdatedResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.last_updated != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.LastUpdatedResponse", len)?; + if self.last_updated != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("last_updated", ToString::to_string(&self.last_updated).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for LastUpdatedResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "last_updated", + "lastUpdated", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + LastUpdated, + } + 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 { + "lastUpdated" | "last_updated" => Ok(GeneratedField::LastUpdated), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = LastUpdatedResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.LastUpdatedResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut last_updated__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::LastUpdated => { + if last_updated__.is_some() { + return Err(serde::de::Error::duplicate_field("lastUpdated")); + } + last_updated__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(LastUpdatedResponse { + last_updated: last_updated__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.LastUpdatedResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Market { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.ticker.is_some() { + len += 1; + } + if !self.provider_configs.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.Market", len)?; + if let Some(v) = self.ticker.as_ref() { + struct_ser.serialize_field("ticker", v)?; + } + if !self.provider_configs.is_empty() { + struct_ser.serialize_field("provider_configs", &self.provider_configs)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Market { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "ticker", + "provider_configs", + "providerConfigs", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Ticker, + ProviderConfigs, + } + 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 { + "ticker" => Ok(GeneratedField::Ticker), + "providerConfigs" | "provider_configs" => Ok(GeneratedField::ProviderConfigs), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Market; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.Market") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut ticker__ = None; + let mut provider_configs__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Ticker => { + if ticker__.is_some() { + return Err(serde::de::Error::duplicate_field("ticker")); + } + ticker__ = map_.next_value()?; + } + GeneratedField::ProviderConfigs => { + if provider_configs__.is_some() { + return Err(serde::de::Error::duplicate_field("providerConfigs")); + } + provider_configs__ = Some(map_.next_value()?); + } + } + } + Ok(Market { + ticker: ticker__, + provider_configs: provider_configs__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.Market", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MarketMap { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.markets.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MarketMap", len)?; + if !self.markets.is_empty() { + struct_ser.serialize_field("markets", &self.markets)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MarketMap { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "markets", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Markets, + } + 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 { + "markets" => Ok(GeneratedField::Markets), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MarketMap; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MarketMap") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut markets__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Markets => { + if markets__.is_some() { + return Err(serde::de::Error::duplicate_field("markets")); + } + markets__ = Some( + map_.next_value::>()? + ); + } + } + } + Ok(MarketMap { + markets: markets__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MarketMap", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MarketMapRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MarketMapRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MarketMapRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MarketMapRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MarketMapRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(MarketMapRequest { + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MarketMapRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MarketMapResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.market_map.is_some() { + len += 1; + } + if self.last_updated != 0 { + len += 1; + } + if !self.chain_id.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MarketMapResponse", len)?; + if let Some(v) = self.market_map.as_ref() { + struct_ser.serialize_field("market_map", v)?; + } + if self.last_updated != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("last_updated", ToString::to_string(&self.last_updated).as_str())?; + } + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chain_id", &self.chain_id)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MarketMapResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market_map", + "marketMap", + "last_updated", + "lastUpdated", + "chain_id", + "chainId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MarketMap, + LastUpdated, + ChainId, + } + 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 { + "marketMap" | "market_map" => Ok(GeneratedField::MarketMap), + "lastUpdated" | "last_updated" => Ok(GeneratedField::LastUpdated), + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MarketMapResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MarketMapResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market_map__ = None; + let mut last_updated__ = None; + let mut chain_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MarketMap => { + if market_map__.is_some() { + return Err(serde::de::Error::duplicate_field("marketMap")); + } + market_map__ = map_.next_value()?; + } + GeneratedField::LastUpdated => { + if last_updated__.is_some() { + return Err(serde::de::Error::duplicate_field("lastUpdated")); + } + last_updated__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + } + } + Ok(MarketMapResponse { + market_map: market_map__, + last_updated: last_updated__.unwrap_or_default(), + chain_id: chain_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MarketMapResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MarketRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.currency_pair.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MarketRequest", len)?; + if let Some(v) = self.currency_pair.as_ref() { + struct_ser.serialize_field("currency_pair", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MarketRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pair", + "currencyPair", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPair, + } + 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 { + "currencyPair" | "currency_pair" => Ok(GeneratedField::CurrencyPair), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MarketRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MarketRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pair__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPair => { + if currency_pair__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPair")); + } + currency_pair__ = map_.next_value()?; + } + } + } + Ok(MarketRequest { + currency_pair: currency_pair__, + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MarketRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MarketResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.market.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MarketResponse", len)?; + if let Some(v) = self.market.as_ref() { + struct_ser.serialize_field("market", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MarketResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Market, + } + 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 { + "market" => Ok(GeneratedField::Market), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MarketResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MarketResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Market => { + if market__.is_some() { + return Err(serde::de::Error::duplicate_field("market")); + } + market__ = map_.next_value()?; + } + } + } + Ok(MarketResponse { + market: market__, + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MarketResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgCreateMarkets { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.authority.is_empty() { + len += 1; + } + if !self.create_markets.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgCreateMarkets", len)?; + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + if !self.create_markets.is_empty() { + struct_ser.serialize_field("create_markets", &self.create_markets)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgCreateMarkets { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "authority", + "create_markets", + "createMarkets", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Authority, + CreateMarkets, + } + 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 { + "authority" => Ok(GeneratedField::Authority), + "createMarkets" | "create_markets" => Ok(GeneratedField::CreateMarkets), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgCreateMarkets; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgCreateMarkets") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut authority__ = None; + let mut create_markets__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + GeneratedField::CreateMarkets => { + if create_markets__.is_some() { + return Err(serde::de::Error::duplicate_field("createMarkets")); + } + create_markets__ = Some(map_.next_value()?); + } + } + } + Ok(MsgCreateMarkets { + authority: authority__.unwrap_or_default(), + create_markets: create_markets__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgCreateMarkets", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgCreateMarketsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgCreateMarketsResponse", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgCreateMarketsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgCreateMarketsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgCreateMarketsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(MsgCreateMarketsResponse { + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgCreateMarketsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgParams { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.params.is_some() { + len += 1; + } + if !self.authority.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgParams", len)?; + if let Some(v) = self.params.as_ref() { + struct_ser.serialize_field("params", v)?; + } + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgParams { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "params", + "authority", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Params, + Authority, + } + 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 { + "params" => Ok(GeneratedField::Params), + "authority" => Ok(GeneratedField::Authority), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgParams; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgParams") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut params__ = None; + let mut authority__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Params => { + if params__.is_some() { + return Err(serde::de::Error::duplicate_field("params")); + } + params__ = map_.next_value()?; + } + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + } + } + Ok(MsgParams { + params: params__, + authority: authority__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgParams", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgParamsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgParamsResponse", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgParamsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgParamsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgParamsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(MsgParamsResponse { + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgParamsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgRemoveMarketAuthorities { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.remove_addresses.is_empty() { + len += 1; + } + if !self.admin.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthorities", len)?; + if !self.remove_addresses.is_empty() { + struct_ser.serialize_field("remove_addresses", &self.remove_addresses)?; + } + if !self.admin.is_empty() { + struct_ser.serialize_field("admin", &self.admin)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgRemoveMarketAuthorities { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "remove_addresses", + "removeAddresses", + "admin", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + RemoveAddresses, + Admin, + } + 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 { + "removeAddresses" | "remove_addresses" => Ok(GeneratedField::RemoveAddresses), + "admin" => Ok(GeneratedField::Admin), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgRemoveMarketAuthorities; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgRemoveMarketAuthorities") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut remove_addresses__ = None; + let mut admin__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::RemoveAddresses => { + if remove_addresses__.is_some() { + return Err(serde::de::Error::duplicate_field("removeAddresses")); + } + remove_addresses__ = Some(map_.next_value()?); + } + GeneratedField::Admin => { + if admin__.is_some() { + return Err(serde::de::Error::duplicate_field("admin")); + } + admin__ = Some(map_.next_value()?); + } + } + } + Ok(MsgRemoveMarketAuthorities { + remove_addresses: remove_addresses__.unwrap_or_default(), + admin: admin__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthorities", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgRemoveMarketAuthoritiesResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthoritiesResponse", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgRemoveMarketAuthoritiesResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgRemoveMarketAuthoritiesResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgRemoveMarketAuthoritiesResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(MsgRemoveMarketAuthoritiesResponse { + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthoritiesResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgUpdateMarkets { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.authority.is_empty() { + len += 1; + } + if !self.update_markets.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpdateMarkets", len)?; + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + if !self.update_markets.is_empty() { + struct_ser.serialize_field("update_markets", &self.update_markets)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgUpdateMarkets { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "authority", + "update_markets", + "updateMarkets", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Authority, + UpdateMarkets, + } + 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 { + "authority" => Ok(GeneratedField::Authority), + "updateMarkets" | "update_markets" => Ok(GeneratedField::UpdateMarkets), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgUpdateMarkets; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgUpdateMarkets") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut authority__ = None; + let mut update_markets__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + GeneratedField::UpdateMarkets => { + if update_markets__.is_some() { + return Err(serde::de::Error::duplicate_field("updateMarkets")); + } + update_markets__ = Some(map_.next_value()?); + } + } + } + Ok(MsgUpdateMarkets { + authority: authority__.unwrap_or_default(), + update_markets: update_markets__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpdateMarkets", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgUpdateMarketsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpdateMarketsResponse", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgUpdateMarketsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgUpdateMarketsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgUpdateMarketsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(MsgUpdateMarketsResponse { + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpdateMarketsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgUpsertMarkets { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.authority.is_empty() { + len += 1; + } + if !self.markets.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpsertMarkets", len)?; + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + if !self.markets.is_empty() { + struct_ser.serialize_field("markets", &self.markets)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgUpsertMarkets { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "authority", + "markets", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Authority, + Markets, + } + 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 { + "authority" => Ok(GeneratedField::Authority), + "markets" => Ok(GeneratedField::Markets), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgUpsertMarkets; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgUpsertMarkets") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut authority__ = None; + let mut markets__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + GeneratedField::Markets => { + if markets__.is_some() { + return Err(serde::de::Error::duplicate_field("markets")); + } + markets__ = Some(map_.next_value()?); + } + } + } + Ok(MsgUpsertMarkets { + authority: authority__.unwrap_or_default(), + markets: markets__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpsertMarkets", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgUpsertMarketsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.market_updates.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpsertMarketsResponse", len)?; + if !self.market_updates.is_empty() { + struct_ser.serialize_field("market_updates", &self.market_updates)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgUpsertMarketsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market_updates", + "marketUpdates", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MarketUpdates, + } + 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 { + "marketUpdates" | "market_updates" => Ok(GeneratedField::MarketUpdates), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgUpsertMarketsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.MsgUpsertMarketsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market_updates__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MarketUpdates => { + if market_updates__.is_some() { + return Err(serde::de::Error::duplicate_field("marketUpdates")); + } + market_updates__ = Some( + map_.next_value::>()? + ); + } + } + } + Ok(MsgUpsertMarketsResponse { + market_updates: market_updates__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpsertMarketsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Params { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.market_authorities.is_empty() { + len += 1; + } + if !self.admin.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.Params", len)?; + if !self.market_authorities.is_empty() { + struct_ser.serialize_field("market_authorities", &self.market_authorities)?; + } + if !self.admin.is_empty() { + struct_ser.serialize_field("admin", &self.admin)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Params { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market_authorities", + "marketAuthorities", + "admin", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MarketAuthorities, + Admin, + } + 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 { + "marketAuthorities" | "market_authorities" => Ok(GeneratedField::MarketAuthorities), + "admin" => Ok(GeneratedField::Admin), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Params; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.Params") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market_authorities__ = None; + let mut admin__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MarketAuthorities => { + if market_authorities__.is_some() { + return Err(serde::de::Error::duplicate_field("marketAuthorities")); + } + market_authorities__ = Some(map_.next_value()?); + } + GeneratedField::Admin => { + if admin__.is_some() { + return Err(serde::de::Error::duplicate_field("admin")); + } + admin__ = Some(map_.next_value()?); + } + } + } + Ok(Params { + market_authorities: market_authorities__.unwrap_or_default(), + admin: admin__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.Params", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ParamsRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.ParamsRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ParamsRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ParamsRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.ParamsRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(ParamsRequest { + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.ParamsRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ParamsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.params.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.ParamsResponse", len)?; + if let Some(v) = self.params.as_ref() { + struct_ser.serialize_field("params", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ParamsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "params", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Params, + } + 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 { + "params" => Ok(GeneratedField::Params), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ParamsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.ParamsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut params__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Params => { + if params__.is_some() { + return Err(serde::de::Error::duplicate_field("params")); + } + params__ = map_.next_value()?; + } + } + } + Ok(ParamsResponse { + params: params__, + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.ParamsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ProviderConfig { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.name.is_empty() { + len += 1; + } + if !self.off_chain_ticker.is_empty() { + len += 1; + } + if self.normalize_by_pair.is_some() { + len += 1; + } + if self.invert { + len += 1; + } + if !self.metadata_json.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.ProviderConfig", len)?; + if !self.name.is_empty() { + struct_ser.serialize_field("name", &self.name)?; + } + if !self.off_chain_ticker.is_empty() { + struct_ser.serialize_field("off_chain_ticker", &self.off_chain_ticker)?; + } + if let Some(v) = self.normalize_by_pair.as_ref() { + struct_ser.serialize_field("normalize_by_pair", v)?; + } + if self.invert { + struct_ser.serialize_field("invert", &self.invert)?; + } + if !self.metadata_json.is_empty() { + struct_ser.serialize_field("metadata_JSON", &self.metadata_json)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ProviderConfig { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "name", + "off_chain_ticker", + "offChainTicker", + "normalize_by_pair", + "normalizeByPair", + "invert", + "metadata_JSON", + "metadataJSON", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Name, + OffChainTicker, + NormalizeByPair, + Invert, + MetadataJson, + } + 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 { + "name" => Ok(GeneratedField::Name), + "offChainTicker" | "off_chain_ticker" => Ok(GeneratedField::OffChainTicker), + "normalizeByPair" | "normalize_by_pair" => Ok(GeneratedField::NormalizeByPair), + "invert" => Ok(GeneratedField::Invert), + "metadataJSON" | "metadata_JSON" => Ok(GeneratedField::MetadataJson), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ProviderConfig; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.ProviderConfig") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut name__ = None; + let mut off_chain_ticker__ = None; + let mut normalize_by_pair__ = None; + let mut invert__ = None; + let mut metadata_json__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Name => { + if name__.is_some() { + return Err(serde::de::Error::duplicate_field("name")); + } + name__ = Some(map_.next_value()?); + } + GeneratedField::OffChainTicker => { + if off_chain_ticker__.is_some() { + return Err(serde::de::Error::duplicate_field("offChainTicker")); + } + off_chain_ticker__ = Some(map_.next_value()?); + } + GeneratedField::NormalizeByPair => { + if normalize_by_pair__.is_some() { + return Err(serde::de::Error::duplicate_field("normalizeByPair")); + } + normalize_by_pair__ = map_.next_value()?; + } + GeneratedField::Invert => { + if invert__.is_some() { + return Err(serde::de::Error::duplicate_field("invert")); + } + invert__ = Some(map_.next_value()?); + } + GeneratedField::MetadataJson => { + if metadata_json__.is_some() { + return Err(serde::de::Error::duplicate_field("metadataJSON")); + } + metadata_json__ = Some(map_.next_value()?); + } + } + } + Ok(ProviderConfig { + name: name__.unwrap_or_default(), + off_chain_ticker: off_chain_ticker__.unwrap_or_default(), + normalize_by_pair: normalize_by_pair__, + invert: invert__.unwrap_or_default(), + metadata_json: metadata_json__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.ProviderConfig", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Ticker { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.currency_pair.is_some() { + len += 1; + } + if self.decimals != 0 { + len += 1; + } + if self.min_provider_count != 0 { + len += 1; + } + if self.enabled { + len += 1; + } + if !self.metadata_json.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.Ticker", len)?; + if let Some(v) = self.currency_pair.as_ref() { + struct_ser.serialize_field("currency_pair", v)?; + } + if self.decimals != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("decimals", ToString::to_string(&self.decimals).as_str())?; + } + if self.min_provider_count != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("min_provider_count", ToString::to_string(&self.min_provider_count).as_str())?; + } + if self.enabled { + struct_ser.serialize_field("enabled", &self.enabled)?; + } + if !self.metadata_json.is_empty() { + struct_ser.serialize_field("metadata_JSON", &self.metadata_json)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Ticker { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pair", + "currencyPair", + "decimals", + "min_provider_count", + "minProviderCount", + "enabled", + "metadata_JSON", + "metadataJSON", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPair, + Decimals, + MinProviderCount, + Enabled, + MetadataJson, + } + 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 { + "currencyPair" | "currency_pair" => Ok(GeneratedField::CurrencyPair), + "decimals" => Ok(GeneratedField::Decimals), + "minProviderCount" | "min_provider_count" => Ok(GeneratedField::MinProviderCount), + "enabled" => Ok(GeneratedField::Enabled), + "metadataJSON" | "metadata_JSON" => Ok(GeneratedField::MetadataJson), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Ticker; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.marketmap.v1.Ticker") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pair__ = None; + let mut decimals__ = None; + let mut min_provider_count__ = None; + let mut enabled__ = None; + let mut metadata_json__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPair => { + if currency_pair__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPair")); + } + currency_pair__ = map_.next_value()?; + } + GeneratedField::Decimals => { + if decimals__.is_some() { + return Err(serde::de::Error::duplicate_field("decimals")); + } + decimals__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::MinProviderCount => { + if min_provider_count__.is_some() { + return Err(serde::de::Error::duplicate_field("minProviderCount")); + } + min_provider_count__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Enabled => { + if enabled__.is_some() { + return Err(serde::de::Error::duplicate_field("enabled")); + } + enabled__ = Some(map_.next_value()?); + } + GeneratedField::MetadataJson => { + if metadata_json__.is_some() { + return Err(serde::de::Error::duplicate_field("metadataJSON")); + } + metadata_json__ = Some(map_.next_value()?); + } + } + } + Ok(Ticker { + currency_pair: currency_pair__, + decimals: decimals__.unwrap_or_default(), + min_provider_count: min_provider_count__.unwrap_or_default(), + enabled: enabled__.unwrap_or_default(), + metadata_json: metadata_json__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.marketmap.v1.Ticker", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/slinky.oracle.module.v1.rs b/crates/astria-core/src/generated/slinky.oracle.module.v1.rs new file mode 100644 index 0000000000..f185539e81 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.oracle.module.v1.rs @@ -0,0 +1,16 @@ +/// Module is the config object of the builder module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Module { + /// Authority defines the custom module authority. If not set, defaults to the + /// governance module. + #[prost(string, tag = "1")] + pub authority: ::prost::alloc::string::String, +} +impl ::prost::Name for Module { + const NAME: &'static str = "Module"; + const PACKAGE: &'static str = "slinky.oracle.module.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.module.v1.{}", Self::NAME) + } +} diff --git a/crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs b/crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs new file mode 100644 index 0000000000..6783394950 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs @@ -0,0 +1,91 @@ +impl serde::Serialize for Module { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.authority.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.module.v1.Module", len)?; + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Module { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "authority", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Authority, + } + 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 { + "authority" => Ok(GeneratedField::Authority), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Module; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.module.v1.Module") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut authority__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + } + } + Ok(Module { + authority: authority__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.module.v1.Module", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/slinky.oracle.v1.rs b/crates/astria-core/src/generated/slinky.oracle.v1.rs new file mode 100644 index 0000000000..76999bb2a9 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.oracle.v1.rs @@ -0,0 +1,1219 @@ +/// QuotePrice is the representation of the aggregated prices for a CurrencyPair, +/// where price represents the price of Base in terms of Quote +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QuotePrice { + #[prost(string, tag = "1")] + pub price: ::prost::alloc::string::String, + /// BlockTimestamp tracks the block height associated with this price update. + /// We include block timestamp alongside the price to ensure that smart + /// contracts and applications are not utilizing stale oracle prices + #[prost(message, optional, tag = "2")] + pub block_timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + /// BlockHeight is height of block mentioned above + #[prost(uint64, tag = "3")] + pub block_height: u64, +} +impl ::prost::Name for QuotePrice { + const NAME: &'static str = "QuotePrice"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// CurrencyPairState represents the stateful information tracked by the x/oracle +/// module per-currency-pair. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CurrencyPairState { + /// QuotePrice is the latest price for a currency-pair, notice this value can + /// be null in the case that no price exists for the currency-pair + #[prost(message, optional, tag = "1")] + pub price: ::core::option::Option, + /// Nonce is the number of updates this currency-pair has received + #[prost(uint64, tag = "2")] + pub nonce: u64, + /// ID is the ID of the CurrencyPair + #[prost(uint64, tag = "3")] + pub id: u64, +} +impl ::prost::Name for CurrencyPairState { + const NAME: &'static str = "CurrencyPairState"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// CurrencyPairGenesis is the information necessary for initialization of a +/// CurrencyPair. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CurrencyPairGenesis { + /// The CurrencyPair to be added to module state + #[prost(message, optional, tag = "1")] + pub currency_pair: ::core::option::Option, + /// A genesis price if one exists (note this will be empty, unless it results + /// from forking the state of this module) + #[prost(message, optional, tag = "2")] + pub currency_pair_price: ::core::option::Option, + /// nonce is the nonce (number of updates) for the CP (same case as above, + /// likely 0 unless it results from fork of module) + #[prost(uint64, tag = "3")] + pub nonce: u64, + /// id is the ID of the CurrencyPair + #[prost(uint64, tag = "4")] + pub id: u64, +} +impl ::prost::Name for CurrencyPairGenesis { + const NAME: &'static str = "CurrencyPairGenesis"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GenesisState is the genesis-state for the x/oracle module, it takes a set of +/// predefined CurrencyPairGeneses +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GenesisState { + /// CurrencyPairGenesis is the set of CurrencyPairGeneses for the module. I.e + /// the starting set of CurrencyPairs for the module + information regarding + /// their latest update. + #[prost(message, repeated, tag = "1")] + pub currency_pair_genesis: ::prost::alloc::vec::Vec, + /// NextID is the next ID to be used for a CurrencyPair + #[prost(uint64, tag = "2")] + pub next_id: u64, +} +impl ::prost::Name for GenesisState { + const NAME: &'static str = "GenesisState"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetAllCurrencyPairsRequest {} +impl ::prost::Name for GetAllCurrencyPairsRequest { + const NAME: &'static str = "GetAllCurrencyPairsRequest"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GetAllCurrencyPairsResponse returns all CurrencyPairs that the module is +/// currently tracking. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetAllCurrencyPairsResponse { + #[prost(message, repeated, tag = "1")] + pub currency_pairs: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for GetAllCurrencyPairsResponse { + const NAME: &'static str = "GetAllCurrencyPairsResponse"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GetPriceRequest either takes a CurrencyPair, or an identifier for the +/// CurrencyPair in the format base/quote. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetPriceRequest { + /// CurrencyPair represents the pair that the user wishes to query. + #[prost(message, optional, tag = "1")] + pub currency_pair: ::core::option::Option, +} +impl ::prost::Name for GetPriceRequest { + const NAME: &'static str = "GetPriceRequest"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GetPriceResponse is the response from the GetPrice grpc method exposed from +/// the x/oracle query service. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetPriceResponse { + /// QuotePrice represents the quote-price for the CurrencyPair given in + /// GetPriceRequest (possibly nil if no update has been made) + #[prost(message, optional, tag = "1")] + pub price: ::core::option::Option, + /// nonce represents the nonce for the CurrencyPair if it exists in state + #[prost(uint64, tag = "2")] + pub nonce: u64, + /// decimals represents the number of decimals that the quote-price is + /// represented in. For Pairs where ETHEREUM is the quote this will be 18, + /// otherwise it will be 8. + #[prost(uint64, tag = "3")] + pub decimals: u64, + /// ID represents the identifier for the CurrencyPair. + #[prost(uint64, tag = "4")] + pub id: u64, +} +impl ::prost::Name for GetPriceResponse { + const NAME: &'static str = "GetPriceResponse"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GetPricesRequest takes an identifier for the CurrencyPair +/// in the format base/quote. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetPricesRequest { + #[prost(string, repeated, tag = "1")] + pub currency_pair_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +impl ::prost::Name for GetPricesRequest { + const NAME: &'static str = "GetPricesRequest"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GetPricesResponse is the response from the GetPrices grpc method exposed from +/// the x/oracle query service. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetPricesResponse { + #[prost(message, repeated, tag = "1")] + pub prices: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for GetPricesResponse { + const NAME: &'static str = "GetPricesResponse"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GetCurrencyPairMappingRequest is the GetCurrencyPairMapping request type. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetCurrencyPairMappingRequest {} +impl ::prost::Name for GetCurrencyPairMappingRequest { + const NAME: &'static str = "GetCurrencyPairMappingRequest"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// GetCurrencyPairMappingResponse is the GetCurrencyPairMapping response type. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetCurrencyPairMappingResponse { + /// currency_pair_mapping is a mapping of the id representing the currency pair + /// to the currency pair itself. + #[prost(map = "uint64, message", tag = "1")] + pub currency_pair_mapping: ::std::collections::HashMap< + u64, + super::super::types::v1::CurrencyPair, + >, +} +impl ::prost::Name for GetCurrencyPairMappingResponse { + const NAME: &'static str = "GetCurrencyPairMappingResponse"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod query_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Query is the query service for the x/oracle module. + #[derive(Debug, Clone)] + pub struct QueryClient { + inner: tonic::client::Grpc, + } + impl QueryClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl QueryClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> QueryClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + QueryClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Get all the currency pairs the x/oracle module is tracking price-data for. + pub async fn get_all_currency_pairs( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.oracle.v1.Query/GetAllCurrencyPairs", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("slinky.oracle.v1.Query", "GetAllCurrencyPairs"), + ); + self.inner.unary(req, path, codec).await + } + /// Given a CurrencyPair (or its identifier) return the latest QuotePrice for + /// that CurrencyPair. + pub async fn get_price( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.oracle.v1.Query/GetPrice", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.oracle.v1.Query", "GetPrice")); + self.inner.unary(req, path, codec).await + } + pub async fn get_prices( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.oracle.v1.Query/GetPrices", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.oracle.v1.Query", "GetPrices")); + self.inner.unary(req, path, codec).await + } + /// Get the mapping of currency pair ID -> currency pair. This is useful for + /// indexers that have access to the ID of a currency pair, but no way to get + /// the underlying currency pair from it. + pub async fn get_currency_pair_mapping( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.oracle.v1.Query/GetCurrencyPairMapping", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("slinky.oracle.v1.Query", "GetCurrencyPairMapping"), + ); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod query_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with QueryServer. + #[async_trait] + pub trait Query: Send + Sync + 'static { + /// Get all the currency pairs the x/oracle module is tracking price-data for. + async fn get_all_currency_pairs( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Given a CurrencyPair (or its identifier) return the latest QuotePrice for + /// that CurrencyPair. + async fn get_price( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn get_prices( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Get the mapping of currency pair ID -> currency pair. This is useful for + /// indexers that have access to the ID of a currency pair, but no way to get + /// the underlying currency pair from it. + async fn get_currency_pair_mapping( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Query is the query service for the x/oracle module. + #[derive(Debug)] + pub struct QueryServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl QueryServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for QueryServer + where + T: Query, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/slinky.oracle.v1.Query/GetAllCurrencyPairs" => { + #[allow(non_camel_case_types)] + struct GetAllCurrencyPairsSvc(pub Arc); + impl< + T: Query, + > tonic::server::UnaryService + for GetAllCurrencyPairsSvc { + type Response = super::GetAllCurrencyPairsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_all_currency_pairs(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetAllCurrencyPairsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.oracle.v1.Query/GetPrice" => { + #[allow(non_camel_case_types)] + struct GetPriceSvc(pub Arc); + impl tonic::server::UnaryService + for GetPriceSvc { + type Response = super::GetPriceResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_price(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetPriceSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.oracle.v1.Query/GetPrices" => { + #[allow(non_camel_case_types)] + struct GetPricesSvc(pub Arc); + impl tonic::server::UnaryService + for GetPricesSvc { + type Response = super::GetPricesResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_prices(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetPricesSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.oracle.v1.Query/GetCurrencyPairMapping" => { + #[allow(non_camel_case_types)] + struct GetCurrencyPairMappingSvc(pub Arc); + impl< + T: Query, + > tonic::server::UnaryService + for GetCurrencyPairMappingSvc { + type Response = super::GetCurrencyPairMappingResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_currency_pair_mapping(inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetCurrencyPairMappingSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for QueryServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for QueryServer { + const NAME: &'static str = "slinky.oracle.v1.Query"; + } +} +/// Given an authority + a set of CurrencyPairs, the x/oracle module will +/// check to see that the authority has permissions to update the set of +/// CurrencyPairs tracked in the oracle, and add the given CurrencyPairs to be +/// tracked in each VoteExtension +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgAddCurrencyPairs { + /// authority is the address of the account that is authorized to update the + /// x/oracle's CurrencyPairs + #[prost(string, tag = "1")] + pub authority: ::prost::alloc::string::String, + /// set of CurrencyPairs to be added to the module (+ prices if they are to be + /// set) + #[prost(message, repeated, tag = "2")] + pub currency_pairs: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for MsgAddCurrencyPairs { + const NAME: &'static str = "MsgAddCurrencyPairs"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgAddCurrencyPairsResponse {} +impl ::prost::Name for MsgAddCurrencyPairsResponse { + const NAME: &'static str = "MsgAddCurrencyPairsResponse"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// Given an authority + a set of CurrencyPairIDs, the x/oracle module's message +/// service will remove all of the CurrencyPairs identified by each +/// CurrencyPairID in the request from state. Notice, if a given currency-pair +/// does not exist in state, the module ignores that currency-pair and continues +/// removing the rest. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgRemoveCurrencyPairs { + /// authority is the address of the account that is authorized to update the + /// x/oracle's CurrencyPairs + #[prost(string, tag = "1")] + pub authority: ::prost::alloc::string::String, + /// currency_pair_ids are the stringified representation of a currency-pairs + /// (base/quote) to be removed from the module's state + #[prost(string, repeated, tag = "2")] + pub currency_pair_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +impl ::prost::Name for MsgRemoveCurrencyPairs { + const NAME: &'static str = "MsgRemoveCurrencyPairs"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgRemoveCurrencyPairsResponse {} +impl ::prost::Name for MsgRemoveCurrencyPairsResponse { + const NAME: &'static str = "MsgRemoveCurrencyPairsResponse"; + const PACKAGE: &'static str = "slinky.oracle.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod msg_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Msg is the message service for the x/oracle module. + #[derive(Debug, Clone)] + pub struct MsgClient { + inner: tonic::client::Grpc, + } + impl MsgClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl MsgClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> MsgClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + MsgClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// AddCurrencyPairs will be used only by governance to update the set of + /// available CurrencyPairs. Given a set of CurrencyPair objects, update + /// the available currency pairs in the module . + pub async fn add_currency_pairs( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.oracle.v1.Msg/AddCurrencyPairs", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.oracle.v1.Msg", "AddCurrencyPairs")); + self.inner.unary(req, path, codec).await + } + /// RemoveCurrencyPairs will be used explicitly by governance to remove the + /// given set of currency-pairs from the module's state. Thus these + /// CurrencyPairs will no longer have price-data available from this module. + pub async fn remove_currency_pairs( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.oracle.v1.Msg/RemoveCurrencyPairs", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.oracle.v1.Msg", "RemoveCurrencyPairs")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod msg_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with MsgServer. + #[async_trait] + pub trait Msg: Send + Sync + 'static { + /// AddCurrencyPairs will be used only by governance to update the set of + /// available CurrencyPairs. Given a set of CurrencyPair objects, update + /// the available currency pairs in the module . + async fn add_currency_pairs( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// RemoveCurrencyPairs will be used explicitly by governance to remove the + /// given set of currency-pairs from the module's state. Thus these + /// CurrencyPairs will no longer have price-data available from this module. + async fn remove_currency_pairs( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Msg is the message service for the x/oracle module. + #[derive(Debug)] + pub struct MsgServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl MsgServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for MsgServer + where + T: Msg, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/slinky.oracle.v1.Msg/AddCurrencyPairs" => { + #[allow(non_camel_case_types)] + struct AddCurrencyPairsSvc(pub Arc); + impl tonic::server::UnaryService + for AddCurrencyPairsSvc { + type Response = super::MsgAddCurrencyPairsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::add_currency_pairs(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = AddCurrencyPairsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.oracle.v1.Msg/RemoveCurrencyPairs" => { + #[allow(non_camel_case_types)] + struct RemoveCurrencyPairsSvc(pub Arc); + impl< + T: Msg, + > tonic::server::UnaryService + for RemoveCurrencyPairsSvc { + type Response = super::MsgRemoveCurrencyPairsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::remove_currency_pairs(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = RemoveCurrencyPairsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for MsgServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for MsgServer { + const NAME: &'static str = "slinky.oracle.v1.Msg"; + } +} diff --git a/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs b/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs new file mode 100644 index 0000000000..ee28a3bdf6 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs @@ -0,0 +1,1639 @@ +impl serde::Serialize for CurrencyPairGenesis { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.currency_pair.is_some() { + len += 1; + } + if self.currency_pair_price.is_some() { + len += 1; + } + if self.nonce != 0 { + len += 1; + } + if self.id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.CurrencyPairGenesis", len)?; + if let Some(v) = self.currency_pair.as_ref() { + struct_ser.serialize_field("currency_pair", v)?; + } + if let Some(v) = self.currency_pair_price.as_ref() { + struct_ser.serialize_field("currency_pair_price", v)?; + } + if self.nonce != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("nonce", ToString::to_string(&self.nonce).as_str())?; + } + if self.id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for CurrencyPairGenesis { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pair", + "currencyPair", + "currency_pair_price", + "currencyPairPrice", + "nonce", + "id", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPair, + CurrencyPairPrice, + Nonce, + Id, + } + 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 { + "currencyPair" | "currency_pair" => Ok(GeneratedField::CurrencyPair), + "currencyPairPrice" | "currency_pair_price" => Ok(GeneratedField::CurrencyPairPrice), + "nonce" => Ok(GeneratedField::Nonce), + "id" => Ok(GeneratedField::Id), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = CurrencyPairGenesis; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.CurrencyPairGenesis") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pair__ = None; + let mut currency_pair_price__ = None; + let mut nonce__ = None; + let mut id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPair => { + if currency_pair__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPair")); + } + currency_pair__ = map_.next_value()?; + } + GeneratedField::CurrencyPairPrice => { + if currency_pair_price__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPairPrice")); + } + currency_pair_price__ = map_.next_value()?; + } + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); + } + nonce__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Id => { + if id__.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(CurrencyPairGenesis { + currency_pair: currency_pair__, + currency_pair_price: currency_pair_price__, + nonce: nonce__.unwrap_or_default(), + id: id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.CurrencyPairGenesis", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for CurrencyPairState { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.price.is_some() { + len += 1; + } + if self.nonce != 0 { + len += 1; + } + if self.id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.CurrencyPairState", len)?; + if let Some(v) = self.price.as_ref() { + struct_ser.serialize_field("price", v)?; + } + if self.nonce != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("nonce", ToString::to_string(&self.nonce).as_str())?; + } + if self.id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for CurrencyPairState { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "price", + "nonce", + "id", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Price, + Nonce, + Id, + } + 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 { + "price" => Ok(GeneratedField::Price), + "nonce" => Ok(GeneratedField::Nonce), + "id" => Ok(GeneratedField::Id), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = CurrencyPairState; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.CurrencyPairState") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut price__ = None; + let mut nonce__ = None; + let mut id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Price => { + if price__.is_some() { + return Err(serde::de::Error::duplicate_field("price")); + } + price__ = map_.next_value()?; + } + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); + } + nonce__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Id => { + if id__.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(CurrencyPairState { + price: price__, + nonce: nonce__.unwrap_or_default(), + id: id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.CurrencyPairState", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GenesisState { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.currency_pair_genesis.is_empty() { + len += 1; + } + if self.next_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GenesisState", len)?; + if !self.currency_pair_genesis.is_empty() { + struct_ser.serialize_field("currency_pair_genesis", &self.currency_pair_genesis)?; + } + if self.next_id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("next_id", ToString::to_string(&self.next_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GenesisState { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pair_genesis", + "currencyPairGenesis", + "next_id", + "nextId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPairGenesis, + NextId, + } + 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 { + "currencyPairGenesis" | "currency_pair_genesis" => Ok(GeneratedField::CurrencyPairGenesis), + "nextId" | "next_id" => Ok(GeneratedField::NextId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GenesisState; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GenesisState") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pair_genesis__ = None; + let mut next_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPairGenesis => { + if currency_pair_genesis__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPairGenesis")); + } + currency_pair_genesis__ = Some(map_.next_value()?); + } + GeneratedField::NextId => { + if next_id__.is_some() { + return Err(serde::de::Error::duplicate_field("nextId")); + } + next_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(GenesisState { + currency_pair_genesis: currency_pair_genesis__.unwrap_or_default(), + next_id: next_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GenesisState", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetAllCurrencyPairsRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetAllCurrencyPairsRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetAllCurrencyPairsRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetAllCurrencyPairsRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(GetAllCurrencyPairsRequest { + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetAllCurrencyPairsRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetAllCurrencyPairsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.currency_pairs.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetAllCurrencyPairsResponse", len)?; + if !self.currency_pairs.is_empty() { + struct_ser.serialize_field("currency_pairs", &self.currency_pairs)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pairs", + "currencyPairs", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPairs, + } + 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 { + "currencyPairs" | "currency_pairs" => Ok(GeneratedField::CurrencyPairs), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetAllCurrencyPairsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetAllCurrencyPairsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pairs__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPairs => { + if currency_pairs__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPairs")); + } + currency_pairs__ = Some(map_.next_value()?); + } + } + } + Ok(GetAllCurrencyPairsResponse { + currency_pairs: currency_pairs__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetAllCurrencyPairsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetCurrencyPairMappingRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetCurrencyPairMappingRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetCurrencyPairMappingRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetCurrencyPairMappingRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(GetCurrencyPairMappingRequest { + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetCurrencyPairMappingRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetCurrencyPairMappingResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.currency_pair_mapping.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetCurrencyPairMappingResponse", len)?; + if !self.currency_pair_mapping.is_empty() { + struct_ser.serialize_field("currency_pair_mapping", &self.currency_pair_mapping)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pair_mapping", + "currencyPairMapping", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPairMapping, + } + 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 { + "currencyPairMapping" | "currency_pair_mapping" => Ok(GeneratedField::CurrencyPairMapping), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetCurrencyPairMappingResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetCurrencyPairMappingResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pair_mapping__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPairMapping => { + if currency_pair_mapping__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPairMapping")); + } + currency_pair_mapping__ = Some( + map_.next_value::, _>>()? + .into_iter().map(|(k,v)| (k.0, v)).collect() + ); + } + } + } + Ok(GetCurrencyPairMappingResponse { + currency_pair_mapping: currency_pair_mapping__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetCurrencyPairMappingResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetPriceRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.currency_pair.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetPriceRequest", len)?; + if let Some(v) = self.currency_pair.as_ref() { + struct_ser.serialize_field("currency_pair", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetPriceRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pair", + "currencyPair", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPair, + } + 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 { + "currencyPair" | "currency_pair" => Ok(GeneratedField::CurrencyPair), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetPriceRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetPriceRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pair__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPair => { + if currency_pair__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPair")); + } + currency_pair__ = map_.next_value()?; + } + } + } + Ok(GetPriceRequest { + currency_pair: currency_pair__, + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetPriceRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetPriceResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.price.is_some() { + len += 1; + } + if self.nonce != 0 { + len += 1; + } + if self.decimals != 0 { + len += 1; + } + if self.id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetPriceResponse", len)?; + if let Some(v) = self.price.as_ref() { + struct_ser.serialize_field("price", v)?; + } + if self.nonce != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("nonce", ToString::to_string(&self.nonce).as_str())?; + } + if self.decimals != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("decimals", ToString::to_string(&self.decimals).as_str())?; + } + if self.id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetPriceResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "price", + "nonce", + "decimals", + "id", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Price, + Nonce, + Decimals, + Id, + } + 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 { + "price" => Ok(GeneratedField::Price), + "nonce" => Ok(GeneratedField::Nonce), + "decimals" => Ok(GeneratedField::Decimals), + "id" => Ok(GeneratedField::Id), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetPriceResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetPriceResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut price__ = None; + let mut nonce__ = None; + let mut decimals__ = None; + let mut id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Price => { + if price__.is_some() { + return Err(serde::de::Error::duplicate_field("price")); + } + price__ = map_.next_value()?; + } + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); + } + nonce__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Decimals => { + if decimals__.is_some() { + return Err(serde::de::Error::duplicate_field("decimals")); + } + decimals__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Id => { + if id__.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(GetPriceResponse { + price: price__, + nonce: nonce__.unwrap_or_default(), + decimals: decimals__.unwrap_or_default(), + id: id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetPriceResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetPricesRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.currency_pair_ids.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetPricesRequest", len)?; + if !self.currency_pair_ids.is_empty() { + struct_ser.serialize_field("currency_pair_ids", &self.currency_pair_ids)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetPricesRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "currency_pair_ids", + "currencyPairIds", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + CurrencyPairIds, + } + 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 { + "currencyPairIds" | "currency_pair_ids" => Ok(GeneratedField::CurrencyPairIds), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetPricesRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetPricesRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut currency_pair_ids__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::CurrencyPairIds => { + if currency_pair_ids__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPairIds")); + } + currency_pair_ids__ = Some(map_.next_value()?); + } + } + } + Ok(GetPricesRequest { + currency_pair_ids: currency_pair_ids__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetPricesRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetPricesResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.prices.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetPricesResponse", len)?; + if !self.prices.is_empty() { + struct_ser.serialize_field("prices", &self.prices)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetPricesResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "prices", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Prices, + } + 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 { + "prices" => Ok(GeneratedField::Prices), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetPricesResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.GetPricesResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut prices__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Prices => { + if prices__.is_some() { + return Err(serde::de::Error::duplicate_field("prices")); + } + prices__ = Some(map_.next_value()?); + } + } + } + Ok(GetPricesResponse { + prices: prices__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.GetPricesResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgAddCurrencyPairs { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.authority.is_empty() { + len += 1; + } + if !self.currency_pairs.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgAddCurrencyPairs", len)?; + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + if !self.currency_pairs.is_empty() { + struct_ser.serialize_field("currency_pairs", &self.currency_pairs)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgAddCurrencyPairs { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "authority", + "currency_pairs", + "currencyPairs", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Authority, + CurrencyPairs, + } + 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 { + "authority" => Ok(GeneratedField::Authority), + "currencyPairs" | "currency_pairs" => Ok(GeneratedField::CurrencyPairs), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgAddCurrencyPairs; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.MsgAddCurrencyPairs") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut authority__ = None; + let mut currency_pairs__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + GeneratedField::CurrencyPairs => { + if currency_pairs__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPairs")); + } + currency_pairs__ = Some(map_.next_value()?); + } + } + } + Ok(MsgAddCurrencyPairs { + authority: authority__.unwrap_or_default(), + currency_pairs: currency_pairs__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.MsgAddCurrencyPairs", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgAddCurrencyPairsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgAddCurrencyPairsResponse", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgAddCurrencyPairsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgAddCurrencyPairsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.MsgAddCurrencyPairsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(MsgAddCurrencyPairsResponse { + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.MsgAddCurrencyPairsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgRemoveCurrencyPairs { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.authority.is_empty() { + len += 1; + } + if !self.currency_pair_ids.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairs", len)?; + if !self.authority.is_empty() { + struct_ser.serialize_field("authority", &self.authority)?; + } + if !self.currency_pair_ids.is_empty() { + struct_ser.serialize_field("currency_pair_ids", &self.currency_pair_ids)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgRemoveCurrencyPairs { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "authority", + "currency_pair_ids", + "currencyPairIds", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Authority, + CurrencyPairIds, + } + 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 { + "authority" => Ok(GeneratedField::Authority), + "currencyPairIds" | "currency_pair_ids" => Ok(GeneratedField::CurrencyPairIds), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgRemoveCurrencyPairs; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.MsgRemoveCurrencyPairs") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut authority__ = None; + let mut currency_pair_ids__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Authority => { + if authority__.is_some() { + return Err(serde::de::Error::duplicate_field("authority")); + } + authority__ = Some(map_.next_value()?); + } + GeneratedField::CurrencyPairIds => { + if currency_pair_ids__.is_some() { + return Err(serde::de::Error::duplicate_field("currencyPairIds")); + } + currency_pair_ids__ = Some(map_.next_value()?); + } + } + } + Ok(MsgRemoveCurrencyPairs { + authority: authority__.unwrap_or_default(), + currency_pair_ids: currency_pair_ids__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairs", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for MsgRemoveCurrencyPairsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairsResponse", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MsgRemoveCurrencyPairsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MsgRemoveCurrencyPairsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.MsgRemoveCurrencyPairsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(MsgRemoveCurrencyPairsResponse { + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairsResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QuotePrice { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.price.is_empty() { + len += 1; + } + if self.block_timestamp.is_some() { + len += 1; + } + if self.block_height != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.QuotePrice", len)?; + if !self.price.is_empty() { + struct_ser.serialize_field("price", &self.price)?; + } + if let Some(v) = self.block_timestamp.as_ref() { + struct_ser.serialize_field("block_timestamp", v)?; + } + if self.block_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("block_height", ToString::to_string(&self.block_height).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QuotePrice { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "price", + "block_timestamp", + "blockTimestamp", + "block_height", + "blockHeight", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Price, + BlockTimestamp, + BlockHeight, + } + 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 { + "price" => Ok(GeneratedField::Price), + "blockTimestamp" | "block_timestamp" => Ok(GeneratedField::BlockTimestamp), + "blockHeight" | "block_height" => Ok(GeneratedField::BlockHeight), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QuotePrice; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.oracle.v1.QuotePrice") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut price__ = None; + let mut block_timestamp__ = None; + let mut block_height__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Price => { + if price__.is_some() { + return Err(serde::de::Error::duplicate_field("price")); + } + price__ = Some(map_.next_value()?); + } + GeneratedField::BlockTimestamp => { + if block_timestamp__.is_some() { + return Err(serde::de::Error::duplicate_field("blockTimestamp")); + } + block_timestamp__ = map_.next_value()?; + } + GeneratedField::BlockHeight => { + if block_height__.is_some() { + return Err(serde::de::Error::duplicate_field("blockHeight")); + } + block_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(QuotePrice { + price: price__.unwrap_or_default(), + block_timestamp: block_timestamp__, + block_height: block_height__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.oracle.v1.QuotePrice", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/slinky.service.v1.rs b/crates/astria-core/src/generated/slinky.service.v1.rs new file mode 100644 index 0000000000..c475329749 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.service.v1.rs @@ -0,0 +1,438 @@ +/// QueryPricesRequest defines the request type for the the Prices method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryPricesRequest {} +impl ::prost::Name for QueryPricesRequest { + const NAME: &'static str = "QueryPricesRequest"; + const PACKAGE: &'static str = "slinky.service.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.service.v1.{}", Self::NAME) + } +} +/// QueryPricesResponse defines the response type for the Prices method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryPricesResponse { + /// prices defines the list of prices. + #[prost(map = "string, string", tag = "1")] + pub prices: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(message, optional, tag = "2")] + pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, +} +impl ::prost::Name for QueryPricesResponse { + const NAME: &'static str = "QueryPricesResponse"; + const PACKAGE: &'static str = "slinky.service.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.service.v1.{}", Self::NAME) + } +} +/// QueryMarketMapRequest defines the request type for the MarketMap method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryMarketMapRequest {} +impl ::prost::Name for QueryMarketMapRequest { + const NAME: &'static str = "QueryMarketMapRequest"; + const PACKAGE: &'static str = "slinky.service.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.service.v1.{}", Self::NAME) + } +} +/// QueryMarketMapResponse defines the response type for the MarketMap method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryMarketMapResponse { + /// market_map defines the current market map configuration. + #[prost(message, optional, tag = "1")] + pub market_map: ::core::option::Option, +} +impl ::prost::Name for QueryMarketMapResponse { + const NAME: &'static str = "QueryMarketMapResponse"; + const PACKAGE: &'static str = "slinky.service.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.service.v1.{}", Self::NAME) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod oracle_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Oracle defines the gRPC oracle service. + #[derive(Debug, Clone)] + pub struct OracleClient { + inner: tonic::client::Grpc, + } + impl OracleClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl OracleClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> OracleClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + OracleClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Prices defines a method for fetching the latest prices. + pub async fn prices( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.service.v1.Oracle/Prices", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.service.v1.Oracle", "Prices")); + self.inner.unary(req, path, codec).await + } + /// MarketMap defines a method for fetching the latest market map + /// configuration. + pub async fn market_map( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/slinky.service.v1.Oracle/MarketMap", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("slinky.service.v1.Oracle", "MarketMap")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod oracle_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with OracleServer. + #[async_trait] + pub trait Oracle: Send + Sync + 'static { + /// Prices defines a method for fetching the latest prices. + async fn prices( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// MarketMap defines a method for fetching the latest market map + /// configuration. + async fn market_map( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Oracle defines the gRPC oracle service. + #[derive(Debug)] + pub struct OracleServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl OracleServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for OracleServer + where + T: Oracle, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/slinky.service.v1.Oracle/Prices" => { + #[allow(non_camel_case_types)] + struct PricesSvc(pub Arc); + impl< + T: Oracle, + > tonic::server::UnaryService + for PricesSvc { + type Response = super::QueryPricesResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::prices(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = PricesSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/slinky.service.v1.Oracle/MarketMap" => { + #[allow(non_camel_case_types)] + struct MarketMapSvc(pub Arc); + impl< + T: Oracle, + > tonic::server::UnaryService + for MarketMapSvc { + type Response = super::QueryMarketMapResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::market_map(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = MarketMapSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for OracleServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for OracleServer { + const NAME: &'static str = "slinky.service.v1.Oracle"; + } +} diff --git a/crates/astria-core/src/generated/slinky.service.v1.serde.rs b/crates/astria-core/src/generated/slinky.service.v1.serde.rs new file mode 100644 index 0000000000..fc98e5a6c9 --- /dev/null +++ b/crates/astria-core/src/generated/slinky.service.v1.serde.rs @@ -0,0 +1,344 @@ +impl serde::Serialize for QueryMarketMapRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.service.v1.QueryMarketMapRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryMarketMapRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryMarketMapRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.service.v1.QueryMarketMapRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(QueryMarketMapRequest { + }) + } + } + deserializer.deserialize_struct("slinky.service.v1.QueryMarketMapRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QueryMarketMapResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.market_map.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.service.v1.QueryMarketMapResponse", len)?; + if let Some(v) = self.market_map.as_ref() { + struct_ser.serialize_field("market_map", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryMarketMapResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market_map", + "marketMap", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MarketMap, + } + 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 { + "marketMap" | "market_map" => Ok(GeneratedField::MarketMap), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryMarketMapResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.service.v1.QueryMarketMapResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market_map__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MarketMap => { + if market_map__.is_some() { + return Err(serde::de::Error::duplicate_field("marketMap")); + } + market_map__ = map_.next_value()?; + } + } + } + Ok(QueryMarketMapResponse { + market_map: market_map__, + }) + } + } + deserializer.deserialize_struct("slinky.service.v1.QueryMarketMapResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QueryPricesRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("slinky.service.v1.QueryPricesRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryPricesRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryPricesRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.service.v1.QueryPricesRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(QueryPricesRequest { + }) + } + } + deserializer.deserialize_struct("slinky.service.v1.QueryPricesRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QueryPricesResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.prices.is_empty() { + len += 1; + } + if self.timestamp.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.service.v1.QueryPricesResponse", len)?; + if !self.prices.is_empty() { + struct_ser.serialize_field("prices", &self.prices)?; + } + if let Some(v) = self.timestamp.as_ref() { + struct_ser.serialize_field("timestamp", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryPricesResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "prices", + "timestamp", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Prices, + Timestamp, + } + 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 { + "prices" => Ok(GeneratedField::Prices), + "timestamp" => Ok(GeneratedField::Timestamp), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryPricesResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.service.v1.QueryPricesResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut prices__ = None; + let mut timestamp__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Prices => { + if prices__.is_some() { + return Err(serde::de::Error::duplicate_field("prices")); + } + prices__ = Some( + map_.next_value::>()? + ); + } + GeneratedField::Timestamp => { + if timestamp__.is_some() { + return Err(serde::de::Error::duplicate_field("timestamp")); + } + timestamp__ = map_.next_value()?; + } + } + } + Ok(QueryPricesResponse { + prices: prices__.unwrap_or_default(), + timestamp: timestamp__, + }) + } + } + deserializer.deserialize_struct("slinky.service.v1.QueryPricesResponse", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/slinky.types.v1.rs b/crates/astria-core/src/generated/slinky.types.v1.rs new file mode 100644 index 0000000000..502e88bcbc --- /dev/null +++ b/crates/astria-core/src/generated/slinky.types.v1.rs @@ -0,0 +1,17 @@ +/// CurrencyPair is the standard representation of a pair of assets, where one +/// (Base) is priced in terms of the other (Quote) +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CurrencyPair { + #[prost(string, tag = "1")] + pub base: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub quote: ::prost::alloc::string::String, +} +impl ::prost::Name for CurrencyPair { + const NAME: &'static str = "CurrencyPair"; + const PACKAGE: &'static str = "slinky.types.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("slinky.types.v1.{}", Self::NAME) + } +} diff --git a/crates/astria-core/src/generated/slinky.types.v1.serde.rs b/crates/astria-core/src/generated/slinky.types.v1.serde.rs new file mode 100644 index 0000000000..bc9ee5789c --- /dev/null +++ b/crates/astria-core/src/generated/slinky.types.v1.serde.rs @@ -0,0 +1,108 @@ +impl serde::Serialize for CurrencyPair { + #[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; + } + if !self.quote.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("slinky.types.v1.CurrencyPair", len)?; + if !self.base.is_empty() { + struct_ser.serialize_field("Base", &self.base)?; + } + if !self.quote.is_empty() { + struct_ser.serialize_field("Quote", &self.quote)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for CurrencyPair { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "Base", + "Quote", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Base, + Quote, + } + 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), + "Quote" => Ok(GeneratedField::Quote), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = CurrencyPair; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct slinky.types.v1.CurrencyPair") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut base__ = None; + let mut quote__ = 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()?); + } + GeneratedField::Quote => { + if quote__.is_some() { + return Err(serde::de::Error::duplicate_field("Quote")); + } + quote__ = Some(map_.next_value()?); + } + } + } + Ok(CurrencyPair { + base: base__.unwrap_or_default(), + quote: quote__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("slinky.types.v1.CurrencyPair", FIELDS, GeneratedVisitor) + } +} diff --git a/proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto b/proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto new file mode 100644 index 0000000000..745b1fd69e --- /dev/null +++ b/proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto @@ -0,0 +1,81 @@ +syntax = "proto3"; +package cosmos.app.v1alpha1; +import "google/protobuf/descriptor.proto"; +extend google.protobuf.MessageOptions { + // module indicates that this proto type is a config object for an app module + // and optionally provides other descriptive information about the module. + // It is recommended that a new module config object and go module is versioned + // for every state machine breaking version of a module. The recommended + // pattern for doing this is to put module config objects in a separate proto + // package from the API they expose. Ex: the cosmos.group.v1 API would be + // exposed by module configs cosmos.group.module.v1, cosmos.group.module.v2, etc. + ModuleDescriptor module = 57193479; +} +// ModuleDescriptor describes an app module. +message ModuleDescriptor { + // go_import names the package that should be imported by an app to load the + // module in the runtime module registry. It is required to make debugging + // of configuration errors easier for users. + string go_import = 1; + // use_package refers to a protobuf package that this module + // uses and exposes to the world. In an app, only one module should "use" + // or own a single protobuf package. It is assumed that the module uses + // all of the .proto files in a single package. + repeated PackageReference use_package = 2; + // can_migrate_from defines which module versions this module can migrate + // state from. The framework will check that one module version is able to + // migrate from a previous module version before attempting to update its + // config. It is assumed that modules can transitively migrate from earlier + // versions. For instance if v3 declares it can migrate from v2, and v2 + // declares it can migrate from v1, the framework knows how to migrate + // from v1 to v3, assuming all 3 module versions are registered at runtime. + repeated MigrateFromInfo can_migrate_from = 3; +} +// PackageReference is a reference to a protobuf package used by a module. +message PackageReference { + // name is the fully-qualified name of the package. + string name = 1; + // revision is the optional revision of the package that is being used. + // Protobuf packages used in Cosmos should generally have a major version + // as the last part of the package name, ex. foo.bar.baz.v1. + // The revision of a package can be thought of as the minor version of a + // package which has additional backwards compatible definitions that weren't + // present in a previous version. + // + // A package should indicate its revision with a source code comment + // above the package declaration in one of its files containing the + // text "Revision N" where N is an integer revision. All packages start + // at revision 0 the first time they are released in a module. + // + // When a new version of a module is released and items are added to existing + // .proto files, these definitions should contain comments of the form + // "Since: Revision N" where N is an integer revision. + // + // When the module runtime starts up, it will check the pinned proto + // image and panic if there are runtime protobuf definitions that are not + // in the pinned descriptor which do not have + // a "Since Revision N" comment or have a "Since Revision N" comment where + // N is <= to the revision specified here. This indicates that the protobuf + // files have been updated, but the pinned file descriptor hasn't. + // + // If there are items in the pinned file descriptor with a revision + // greater than the value indicated here, this will also cause a panic + // as it may mean that the pinned descriptor for a legacy module has been + // improperly updated or that there is some other versioning discrepancy. + // Runtime protobuf definitions will also be checked for compatibility + // with pinned file descriptors to make sure there are no incompatible changes. + // + // This behavior ensures that: + // * pinned proto images are up-to-date + // * protobuf files are carefully annotated with revision comments which + // are important good client UX + // * protobuf files are changed in backwards and forwards compatible ways + uint32 revision = 2; +} +// MigrateFromInfo is information on a module version that a newer module +// can migrate from. +message MigrateFromInfo { + // module is the fully-qualified protobuf name of the module config object + // for the previous module version, ex: "cosmos.group.module.v1.Module". + string module = 1; +} diff --git a/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto b/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto new file mode 100644 index 0000000000..32be62827e --- /dev/null +++ b/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package cosmos.msg.v1; +import "google/protobuf/descriptor.proto"; +// TODO(fdymylja): once we fully migrate to protov2 the go_package needs to be updated. +// We need this right now because gogoproto codegen needs to import the extension. +option go_package = "github.com/cosmos/cosmos-sdk/types/msgservice"; +extend google.protobuf.ServiceOptions { + // service indicates that the service is a Msg service and that requests + // must be transported via blockchain transactions rather than gRPC. + // Tooling can use this annotation to distinguish between Msg services and + // other types of services via reflection. + bool service = 11110000; +} +extend google.protobuf.MessageOptions { + // signer must be used in cosmos messages in order + // to signal to external clients which fields in a + // given cosmos message must be filled with signer + // information (address). + // The field must be the protobuf name of the message + // field extended with this MessageOption. + // The field must either be of string kind, or of message + // kind in case the signer information is contained within + // a message inside the cosmos message. + repeated string signer = 11110000; +} diff --git a/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto b/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto new file mode 100644 index 0000000000..d827478af9 --- /dev/null +++ b/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto @@ -0,0 +1,112 @@ +syntax = "proto3"; +package cosmos_proto; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cosmos/cosmos-proto;cosmos_proto"; + +extend google.protobuf.MethodOptions { + + // method_added_in is used to indicate from which version the method was added. + string method_added_in = 93001; +} + +extend google.protobuf.MessageOptions { + + // implements_interface is used to indicate the type name of the interface + // that a message implements so that it can be used in google.protobuf.Any + // fields that accept that interface. A message can implement multiple + // interfaces. Interfaces should be declared using a declare_interface + // file option. + repeated string implements_interface = 93001; + + // message_added_in is used to indicate from which version the message was added. + string message_added_in = 93002; +} + +extend google.protobuf.FieldOptions { + + // accepts_interface is used to annotate that a google.protobuf.Any + // field accepts messages that implement the specified interface. + // Interfaces should be declared using a declare_interface file option. + string accepts_interface = 93001; + + // scalar is used to indicate that this field follows the formatting defined + // by the named scalar which should be declared with declare_scalar. Code + // generators may choose to use this information to map this field to a + // language-specific type representing the scalar. + string scalar = 93002; + + // field_added_in is used to indicate from which version the field was added. + string field_added_in = 93003; +} + +extend google.protobuf.FileOptions { + + // declare_interface declares an interface type to be used with + // accepts_interface and implements_interface. Interface names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given interface type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/interfaces.proto in the file descriptor set. + repeated InterfaceDescriptor declare_interface = 793021; + + // declare_scalar declares a scalar type to be used with + // the scalar field option. Scalar names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given scalar type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/scalars.proto in the file descriptor set. + repeated ScalarDescriptor declare_scalar = 793022; + + // file_added_in is used to indicate from which the version the file was added. + string file_added_in = 793023; +} + +// InterfaceDescriptor describes an interface type to be used with +// accepts_interface and implements_interface and declared by declare_interface. +message InterfaceDescriptor { + + // name is the name of the interface. It should be a short-name (without + // a period) such that the fully qualified name of the interface will be + // package.name, ex. for the package a.b and interface named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the interface and its + // purpose. + string description = 2; +} + +// ScalarDescriptor describes an scalar type to be used with +// the scalar field option and declared by declare_scalar. +// Scalars extend simple protobuf built-in types with additional +// syntax and semantics, for instance to represent big integers. +// Scalars should ideally define an encoding such that there is only one +// valid syntactical representation for a given semantic meaning, +// i.e. the encoding should be deterministic. +message ScalarDescriptor { + + // name is the name of the scalar. It should be a short-name (without + // a period) such that the fully qualified name of the scalar will be + // package.name, ex. for the package a.b and scalar named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the scalar and its + // encoding format. For instance a big integer or decimal scalar should + // specify precisely the expected encoding format. + string description = 2; + + // field_type is the type of field with which this scalar can be used. + // Scalars can be used with one and only one type of field so that + // encoding standards and simple and clear. Currently only string and + // bytes fields are supported for scalars. + repeated ScalarType field_type = 3; +} + +enum ScalarType { + SCALAR_TYPE_UNSPECIFIED = 0; + SCALAR_TYPE_STRING = 1; + SCALAR_TYPE_BYTES = 2; +} diff --git a/proto/vendored/slinky/abci/v1/vote_extensions.proto b/proto/vendored/slinky/abci/v1/vote_extensions.proto new file mode 100644 index 0000000000..1d3b02e841 --- /dev/null +++ b/proto/vendored/slinky/abci/v1/vote_extensions.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package slinky.abci.v1; + +option go_package = "github.com/skip-mev/slinky/abci/ve/types"; + +// OracleVoteExtension defines the vote extension structure for oracle prices. +message OracleVoteExtension { + // Prices defines a map of id(CurrencyPair) -> price.Bytes() . i.e. 1 -> + // 0x123.. (bytes). Notice the `id` function is determined by the + // `CurrencyPairIDStrategy` used in the VoteExtensionHandler. + map prices = 1; +} diff --git a/proto/vendored/slinky/marketmap/module/v1/module.proto b/proto/vendored/slinky/marketmap/module/v1/module.proto new file mode 100644 index 0000000000..57d3e91492 --- /dev/null +++ b/proto/vendored/slinky/marketmap/module/v1/module.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package slinky.marketmap.module.v1; + +import "cosmos_sdk/cosmos/app/v1alpha1/module.proto"; + +// Module is the config object of the builder module. +message Module { + option (cosmos.app.v1alpha1.module) = { + go_import : "github.com/skip-mev/slinky/x/marketmap" + }; + + // Authority defines the custom module authority. If not set, defaults to the + // governance module. + string authority = 1; + + // HooksOrder specifies the order of marketmap hooks and should be a list + // of module names which provide a marketmap hooks instance. If no order is + // provided, then hooks will be applied in alphabetical order of module names. + repeated string hooks_order = 2; +} diff --git a/proto/vendored/slinky/marketmap/v1/genesis.proto b/proto/vendored/slinky/marketmap/v1/genesis.proto new file mode 100644 index 0000000000..b407b11868 --- /dev/null +++ b/proto/vendored/slinky/marketmap/v1/genesis.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package slinky.marketmap.v1; + +import "slinky/marketmap/v1/market.proto"; +import "slinky/marketmap/v1/params.proto"; + +option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; + +// GenesisState defines the x/marketmap module's genesis state. +message GenesisState { + // MarketMap defines the global set of market configurations for all providers + // and markets. + MarketMap market_map = 1; + + // LastUpdated is the last block height that the market map was updated. + // This field can be used as an optimization for clients checking if there + // is a new update to the map. + uint64 last_updated = 2; + + // Params are the parameters for the x/marketmap module. + Params params = 3; +} diff --git a/proto/vendored/slinky/marketmap/v1/market.proto b/proto/vendored/slinky/marketmap/v1/market.proto new file mode 100644 index 0000000000..db69c16dc1 --- /dev/null +++ b/proto/vendored/slinky/marketmap/v1/market.proto @@ -0,0 +1,73 @@ +syntax = "proto3"; +package slinky.marketmap.v1; + +option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; + +import "slinky/types/v1/currency_pair.proto"; + +// Market encapsulates a Ticker and its provider-specific configuration. +message Market { + // Ticker represents a price feed for a given asset pair i.e. BTC/USD. The + // price feed is scaled to a number of decimal places and has a minimum number + // of providers required to consider the ticker valid. + Ticker ticker = 1; + + // ProviderConfigs is the list of provider-specific configs for this Market. + repeated ProviderConfig provider_configs = 2; +} + +// Ticker represents a price feed for a given asset pair i.e. BTC/USD. The price +// feed is scaled to a number of decimal places and has a minimum number of +// providers required to consider the ticker valid. +message Ticker { + // CurrencyPair is the currency pair for this ticker. + slinky.types.v1.CurrencyPair currency_pair = 1; + + // Decimals is the number of decimal places for the ticker. The number of + // decimal places is used to convert the price to a human-readable format. + uint64 decimals = 2; + + // MinProviderCount is the minimum number of providers required to consider + // the ticker valid. + uint64 min_provider_count = 3; + + // Enabled is the flag that denotes if the Ticker is enabled for price + // fetching by an oracle. + bool enabled = 14; + + // MetadataJSON is a string of JSON that encodes any extra configuration + // for the given ticker. + string metadata_JSON = 15; +} + +message ProviderConfig { + // Name corresponds to the name of the provider for which the configuration is + // being set. + string name = 1; + + // OffChainTicker is the off-chain representation of the ticker i.e. BTC/USD. + // The off-chain ticker is unique to a given provider and is used to fetch the + // price of the ticker from the provider. + string off_chain_ticker = 2; + + // NormalizeByPair is the currency pair for this ticker to be normalized by. + // For example, if the desired Ticker is BTC/USD, this market could be reached + // using: OffChainTicker = BTC/USDT NormalizeByPair = USDT/USD This field is + // optional and nullable. + slinky.types.v1.CurrencyPair normalize_by_pair = 3; + + // Invert is a boolean indicating if the BASE and QUOTE of the market should + // be inverted. i.e. BASE -> QUOTE, QUOTE -> BASE + bool invert = 4; + + // MetadataJSON is a string of JSON that encodes any extra configuration + // for the given provider config. + string metadata_JSON = 15; +} + +// MarketMap maps ticker strings to their Markets. +message MarketMap { + // Markets is the full list of tickers and their associated configurations + // to be stored on-chain. + map markets = 1; +} diff --git a/proto/vendored/slinky/marketmap/v1/params.proto b/proto/vendored/slinky/marketmap/v1/params.proto new file mode 100644 index 0000000000..b93f590ea6 --- /dev/null +++ b/proto/vendored/slinky/marketmap/v1/params.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package slinky.marketmap.v1; + +option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; + +// Params defines the parameters for the x/marketmap module. +message Params { + // MarketAuthorities is the list of authority accounts that are able to + // control updating the marketmap. + repeated string market_authorities = 1; + + // Admin is an address that can remove addresses from the MarketAuthorities + // list. Only governance can add to the MarketAuthorities or change the Admin. + string admin = 2; +} diff --git a/proto/vendored/slinky/marketmap/v1/query.proto b/proto/vendored/slinky/marketmap/v1/query.proto new file mode 100644 index 0000000000..002255f790 --- /dev/null +++ b/proto/vendored/slinky/marketmap/v1/query.proto @@ -0,0 +1,83 @@ +syntax = "proto3"; +package slinky.marketmap.v1; + +import "google/api/annotations.proto"; +import "slinky/types/v1/currency_pair.proto"; +import "slinky/marketmap/v1/market.proto"; +import "slinky/marketmap/v1/params.proto"; + +option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; + +// Query is the query service for the x/marketmap module. +service Query { + // MarketMap returns the full market map stored in the x/marketmap + // module. + rpc MarketMap(MarketMapRequest) returns (MarketMapResponse) { + option (google.api.http).get = "/slinky/marketmap/v1/marketmap"; + } + + // Market returns a market stored in the x/marketmap + // module. + rpc Market(MarketRequest) returns (MarketResponse) { + option (google.api.http).get = "/slinky/marketmap/v1/market"; + } + + // LastUpdated returns the last height the market map was updated at. + rpc LastUpdated(LastUpdatedRequest) returns (LastUpdatedResponse) { + option (google.api.http).get = "/slinky/marketmap/v1/last_updated"; + } + + // Params returns the current x/marketmap module parameters. + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/slinky/marketmap/v1/params" + }; + } +} + +// MarketMapRequest is the query request for the MarketMap query. +// It takes no arguments. +message MarketMapRequest {} + +// MarketMapResponse is the query response for the MarketMap query. +message MarketMapResponse { + // MarketMap defines the global set of market configurations for all providers + // and markets. + MarketMap market_map = 1; + + // LastUpdated is the last block height that the market map was updated. + // This field can be used as an optimization for clients checking if there + // is a new update to the map. + uint64 last_updated = 2; + + // ChainId is the chain identifier for the market map. + string chain_id = 3; +} + +// MarketRequest is the query request for the Market query. +// It takes the currency pair of the market as an argument. +message MarketRequest { + // CurrencyPair is the currency pair associated with the market being + // requested. + slinky.types.v1.CurrencyPair currency_pair = 1; +} + +// MarketResponse is the query response for the Market query. +message MarketResponse { + // Market is the configuration of a single market to be price-fetched for. + Market market = 1; +} + +// ParamsRequest is the request type for the Query/Params RPC method. +message ParamsRequest {} + +// ParamsResponse is the response type for the Query/Params RPC method. +message ParamsResponse { Params params = 1; } + +// LastUpdatedRequest is the request type for the Query/LastUpdated RPC +// method. +message LastUpdatedRequest {} + +// LastUpdatedResponse is the response type for the Query/LastUpdated RPC +// method. +message LastUpdatedResponse { uint64 last_updated = 1; } diff --git a/proto/vendored/slinky/marketmap/v1/tx.proto b/proto/vendored/slinky/marketmap/v1/tx.proto new file mode 100644 index 0000000000..e8932361d2 --- /dev/null +++ b/proto/vendored/slinky/marketmap/v1/tx.proto @@ -0,0 +1,121 @@ +syntax = "proto3"; +package slinky.marketmap.v1; + +import "cosmos_sdk/cosmos/msg/v1/msg.proto"; +import "cosmos_sdk/cosmos_proto/cosmos.proto"; +import "slinky/marketmap/v1/market.proto"; +import "slinky/marketmap/v1/params.proto"; + +option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; + +// Msg is the message service for the x/marketmap module. +service Msg { + option (cosmos.msg.v1.service) = true; + + // CreateMarkets creates markets from the given message. + rpc CreateMarkets(MsgCreateMarkets) returns (MsgCreateMarketsResponse); + + // UpdateMarkets updates markets from the given message. + rpc UpdateMarkets(MsgUpdateMarkets) returns (MsgUpdateMarketsResponse); + + // UpdateParams defines a method for updating the x/marketmap module + // parameters. + rpc UpdateParams(MsgParams) returns (MsgParamsResponse); + + // RemoveMarketAuthorities defines a method for removing market authorities + // from the x/marketmap module. the signer must be the admin. + rpc RemoveMarketAuthorities(MsgRemoveMarketAuthorities) + returns (MsgRemoveMarketAuthoritiesResponse); + + // UpsertMarkets wraps both Create / Update markets into a single message. Specifically + // if a market does not exist it will be created, otherwise it will be updated. The response + // will be a map between ticker -> updated. + rpc UpsertMarkets(MsgUpsertMarkets) returns (MsgUpsertMarketsResponse); +} + +// MsgUpsertMarkets defines a message carrying a payload for performing market upserts (update or +// create if does not exist) in the x/marketmap module. +message MsgUpsertMarkets { + option (cosmos.msg.v1.signer) = "authority"; + + // Authority is the signer of this transaction. This authority must be + // authorized by the module to execute the message. + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // CreateMarkets is the list of all markets to be created for the given + // transaction. + repeated Market markets = 2; +} + +// MsgUpsertMarketsResponse is the response from the UpsertMarkets API in the x/marketmap module. +message MsgUpsertMarketsResponse { + // UpdatedMarkets is a map between the ticker and whether the market was updated. + map market_updates = 1; +} + +// MsgCreateMarkets defines a message carrying a payload for creating markets in +// the x/marketmap module. +message MsgCreateMarkets { + option (cosmos.msg.v1.signer) = "authority"; + + // Authority is the signer of this transaction. This authority must be + // authorized by the module to execute the message. + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // CreateMarkets is the list of all markets to be created for the given + // transaction. + repeated Market create_markets = 2; +} + +// MsgUpdateMarketMapResponse is the response message for MsgUpdateMarketMap. +message MsgCreateMarketsResponse {} + +// MsgUpdateMarkets defines a message carrying a payload for updating the +// x/marketmap module. +message MsgUpdateMarkets { + option (cosmos.msg.v1.signer) = "authority"; + + // Authority is the signer of this transaction. This authority must be + // authorized by the module to execute the message. + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // UpdateMarkets is the list of all markets to be updated for the given + // transaction. + repeated Market update_markets = 2; +} + +// MsgUpdateMarketsResponse is the response message for MsgUpdateMarkets. +message MsgUpdateMarketsResponse {} + +// MsgParams defines the Msg/Params request type. It contains the +// new parameters for the x/marketmap module. +message MsgParams { + option (cosmos.msg.v1.signer) = "authority"; + + // Params defines the new parameters for the x/marketmap module. + Params params = 1; + // Authority defines the authority that is updating the x/marketmap module + // parameters. + string authority = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} + +// MsgParamsResponse defines the Msg/Params response type. +message MsgParamsResponse {} + +// MsgRemoveMarketAuthorities defines the Msg/RemoveMarketAuthoritiesResponse +// request type. It contains the new addresses to remove from the list of +// authorities +message MsgRemoveMarketAuthorities { + option (cosmos.msg.v1.signer) = "admin"; + + // RemoveAddresses is the list of addresses to remove. + repeated string remove_addresses = 1; + + // Admin defines the authority that is the x/marketmap + // Admin account. This account is set in the module parameters. + string admin = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} + +// MsgRemoveMarketAuthoritiesResponse defines the +// Msg/RemoveMarketAuthoritiesResponse response type. +message MsgRemoveMarketAuthoritiesResponse {} diff --git a/proto/vendored/slinky/oracle/module/v1/module.proto b/proto/vendored/slinky/oracle/module/v1/module.proto new file mode 100644 index 0000000000..448d31c024 --- /dev/null +++ b/proto/vendored/slinky/oracle/module/v1/module.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package slinky.oracle.module.v1; + +import "cosmos_sdk/cosmos/app/v1alpha1/module.proto"; + +// Module is the config object of the builder module. +message Module { + option (cosmos.app.v1alpha1.module) = { + go_import : "github.com/skip-mev/slinky/x/oracle" + }; + + // Authority defines the custom module authority. If not set, defaults to the + // governance module. + string authority = 1; +} diff --git a/proto/vendored/slinky/oracle/v1/genesis.proto b/proto/vendored/slinky/oracle/v1/genesis.proto new file mode 100644 index 0000000000..f3fd1f187e --- /dev/null +++ b/proto/vendored/slinky/oracle/v1/genesis.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; +package slinky.oracle.v1; + +option go_package = "github.com/skip-mev/slinky/x/oracle/types"; + +import "google/protobuf/timestamp.proto"; +import "cosmos_sdk/cosmos_proto/cosmos.proto"; +import "slinky/types/v1/currency_pair.proto"; + +// QuotePrice is the representation of the aggregated prices for a CurrencyPair, +// where price represents the price of Base in terms of Quote +message QuotePrice { + string price = 1 [ + (cosmos_proto.scalar) = "cosmos.Int" + ]; + + // BlockTimestamp tracks the block height associated with this price update. + // We include block timestamp alongside the price to ensure that smart + // contracts and applications are not utilizing stale oracle prices + google.protobuf.Timestamp block_timestamp = 2; + + // BlockHeight is height of block mentioned above + uint64 block_height = 3; +} + +// CurrencyPairState represents the stateful information tracked by the x/oracle +// module per-currency-pair. +message CurrencyPairState { + // QuotePrice is the latest price for a currency-pair, notice this value can + // be null in the case that no price exists for the currency-pair + QuotePrice price = 1; + + // Nonce is the number of updates this currency-pair has received + uint64 nonce = 2; + + // ID is the ID of the CurrencyPair + uint64 id = 3; +} + +// CurrencyPairGenesis is the information necessary for initialization of a +// CurrencyPair. +message CurrencyPairGenesis { + // The CurrencyPair to be added to module state + slinky.types.v1.CurrencyPair currency_pair = 1; + // A genesis price if one exists (note this will be empty, unless it results + // from forking the state of this module) + QuotePrice currency_pair_price = 2; + // nonce is the nonce (number of updates) for the CP (same case as above, + // likely 0 unless it results from fork of module) + uint64 nonce = 3; + // id is the ID of the CurrencyPair + uint64 id = 4; +} + +// GenesisState is the genesis-state for the x/oracle module, it takes a set of +// predefined CurrencyPairGeneses +message GenesisState { + // CurrencyPairGenesis is the set of CurrencyPairGeneses for the module. I.e + // the starting set of CurrencyPairs for the module + information regarding + // their latest update. + repeated CurrencyPairGenesis currency_pair_genesis = 1; + + // NextID is the next ID to be used for a CurrencyPair + uint64 next_id = 2; +} diff --git a/proto/vendored/slinky/oracle/v1/query.proto b/proto/vendored/slinky/oracle/v1/query.proto new file mode 100644 index 0000000000..993e7dc01e --- /dev/null +++ b/proto/vendored/slinky/oracle/v1/query.proto @@ -0,0 +1,86 @@ +syntax = "proto3"; +package slinky.oracle.v1; +import "google/api/annotations.proto"; +import "slinky/oracle/v1/genesis.proto"; +import "slinky/types/v1/currency_pair.proto"; + +option go_package = "github.com/skip-mev/slinky/x/oracle/types"; + +// Query is the query service for the x/oracle module. +service Query { + // Get all the currency pairs the x/oracle module is tracking price-data for. + rpc GetAllCurrencyPairs(GetAllCurrencyPairsRequest) + returns (GetAllCurrencyPairsResponse) { + option (google.api.http).get = "/slinky/oracle/v1/get_all_tickers"; + }; + + // Given a CurrencyPair (or its identifier) return the latest QuotePrice for + // that CurrencyPair. + rpc GetPrice(GetPriceRequest) returns (GetPriceResponse) { + option (google.api.http).get = "/slinky/oracle/v1/get_price"; + }; + + rpc GetPrices(GetPricesRequest) returns (GetPricesResponse) { + option (google.api.http).get = "/slinky/oracle/v1/get_prices"; + } + + // Get the mapping of currency pair ID -> currency pair. This is useful for + // indexers that have access to the ID of a currency pair, but no way to get + // the underlying currency pair from it. + rpc GetCurrencyPairMapping(GetCurrencyPairMappingRequest) + returns (GetCurrencyPairMappingResponse) { + option (google.api.http).get = + "/slinky/oracle/v1/get_currency_pair_mapping"; + } +} + +message GetAllCurrencyPairsRequest {} + +// GetAllCurrencyPairsResponse returns all CurrencyPairs that the module is +// currently tracking. +message GetAllCurrencyPairsResponse { + repeated slinky.types.v1.CurrencyPair currency_pairs = 1; +} + +// GetPriceRequest either takes a CurrencyPair, or an identifier for the +// CurrencyPair in the format base/quote. +message GetPriceRequest { + // CurrencyPair represents the pair that the user wishes to query. + slinky.types.v1.CurrencyPair currency_pair = 1; +} + +// GetPriceResponse is the response from the GetPrice grpc method exposed from +// the x/oracle query service. +message GetPriceResponse { + // QuotePrice represents the quote-price for the CurrencyPair given in + // GetPriceRequest (possibly nil if no update has been made) + QuotePrice price = 1; + // nonce represents the nonce for the CurrencyPair if it exists in state + uint64 nonce = 2; + // decimals represents the number of decimals that the quote-price is + // represented in. For Pairs where ETHEREUM is the quote this will be 18, + // otherwise it will be 8. + uint64 decimals = 3; + // ID represents the identifier for the CurrencyPair. + uint64 id = 4; +} + +// GetPricesRequest takes an identifier for the CurrencyPair +// in the format base/quote. +message GetPricesRequest { repeated string currency_pair_ids = 1; } + +// GetPricesResponse is the response from the GetPrices grpc method exposed from +// the x/oracle query service. +message GetPricesResponse { + repeated GetPriceResponse prices = 1; +} + +// GetCurrencyPairMappingRequest is the GetCurrencyPairMapping request type. +message GetCurrencyPairMappingRequest {} + +// GetCurrencyPairMappingResponse is the GetCurrencyPairMapping response type. +message GetCurrencyPairMappingResponse { + // currency_pair_mapping is a mapping of the id representing the currency pair + // to the currency pair itself. + map currency_pair_mapping = 1; +} diff --git a/proto/vendored/slinky/oracle/v1/tx.proto b/proto/vendored/slinky/oracle/v1/tx.proto new file mode 100644 index 0000000000..8ca3295edc --- /dev/null +++ b/proto/vendored/slinky/oracle/v1/tx.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; +package slinky.oracle.v1; + +import "slinky/oracle/v1/genesis.proto"; +import "cosmos_sdk/cosmos_proto/cosmos.proto"; +import "cosmos_sdk/cosmos/msg/v1/msg.proto"; +import "slinky/types/v1/currency_pair.proto"; + +option go_package = "github.com/skip-mev/slinky/x/oracle/types"; + +// Msg is the message service for the x/oracle module. +service Msg { + option (cosmos.msg.v1.service) = true; + + // AddCurrencyPairs will be used only by governance to update the set of + // available CurrencyPairs. Given a set of CurrencyPair objects, update + // the available currency pairs in the module . + rpc AddCurrencyPairs(MsgAddCurrencyPairs) + returns (MsgAddCurrencyPairsResponse); + + // RemoveCurrencyPairs will be used explicitly by governance to remove the + // given set of currency-pairs from the module's state. Thus these + // CurrencyPairs will no longer have price-data available from this module. + rpc RemoveCurrencyPairs(MsgRemoveCurrencyPairs) + returns (MsgRemoveCurrencyPairsResponse); +} + +// Given an authority + a set of CurrencyPairs, the x/oracle module will +// check to see that the authority has permissions to update the set of +// CurrencyPairs tracked in the oracle, and add the given CurrencyPairs to be +// tracked in each VoteExtension +message MsgAddCurrencyPairs { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address of the account that is authorized to update the + // x/oracle's CurrencyPairs + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + // set of CurrencyPairs to be added to the module (+ prices if they are to be + // set) + repeated slinky.types.v1.CurrencyPair currency_pairs = 2; +} + +message MsgAddCurrencyPairsResponse {} + +// Given an authority + a set of CurrencyPairIDs, the x/oracle module's message +// service will remove all of the CurrencyPairs identified by each +// CurrencyPairID in the request from state. Notice, if a given currency-pair +// does not exist in state, the module ignores that currency-pair and continues +// removing the rest. +message MsgRemoveCurrencyPairs { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address of the account that is authorized to update the + // x/oracle's CurrencyPairs + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // currency_pair_ids are the stringified representation of a currency-pairs + // (base/quote) to be removed from the module's state + repeated string currency_pair_ids = 2; +} + +message MsgRemoveCurrencyPairsResponse {} diff --git a/proto/vendored/slinky/service/v1/oracle.proto b/proto/vendored/slinky/service/v1/oracle.proto new file mode 100644 index 0000000000..5d9fdc5d57 --- /dev/null +++ b/proto/vendored/slinky/service/v1/oracle.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; +package slinky.service.v1; + +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "cosmos_sdk/cosmos_proto/cosmos.proto"; +import "slinky/marketmap/v1/market.proto"; + +option go_package = "github.com/skip-mev/slinky/service/servers/oracle/types"; + +// Oracle defines the gRPC oracle service. +service Oracle { + // Prices defines a method for fetching the latest prices. + rpc Prices(QueryPricesRequest) returns (QueryPricesResponse) { + option (google.api.http).get = "/slinky/oracle/v1/prices"; + }; + + // MarketMap defines a method for fetching the latest market map + // configuration. + rpc MarketMap(QueryMarketMapRequest) returns (QueryMarketMapResponse) { + option (google.api.http).get = "/slinky/oracle/v1/marketmap"; + } +} + +// QueryPricesRequest defines the request type for the the Prices method. +message QueryPricesRequest {} + +// QueryPricesResponse defines the response type for the Prices method. +message QueryPricesResponse { + // prices defines the list of prices. + map prices = 1; + google.protobuf.Timestamp timestamp = 2; +} + +// QueryMarketMapRequest defines the request type for the MarketMap method. +message QueryMarketMapRequest {} + +// QueryMarketMapResponse defines the response type for the MarketMap method. +message QueryMarketMapResponse { + // market_map defines the current market map configuration. + slinky.marketmap.v1.MarketMap market_map = 1; +} diff --git a/proto/vendored/slinky/types/v1/currency_pair.proto b/proto/vendored/slinky/types/v1/currency_pair.proto new file mode 100644 index 0000000000..02ee089ea3 --- /dev/null +++ b/proto/vendored/slinky/types/v1/currency_pair.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package slinky.types.v1; + +option go_package = "github.com/skip-mev/slinky/pkg/types"; + +// CurrencyPair is the standard representation of a pair of assets, where one +// (Base) is priced in terms of the other (Quote) +message CurrencyPair { + string Base = 1; + string Quote = 2; +} diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index 9cd50655c8..cb42d78716 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -116,6 +116,7 @@ fn main() { ".celestia", ".cosmos", ".tendermint", + ".slinky", ]) .unwrap(); @@ -153,6 +154,7 @@ fn clean_non_astria_code(generated: &mut ContentMap) { !name.starts_with("astria.") && !name.starts_with("celestia.") && !name.starts_with("cosmos.") + && !name.starts_with("slinky.") && !name.starts_with("tendermint.") }) .cloned() From a18ad267810609f9b332e9c5caa505d1e8773239 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 14:53:25 -0400 Subject: [PATCH 02/89] add slinky grpc service and genesis state --- crates/astria-core/src/generated/mod.rs | 87 +++++++++++++++++++ crates/astria-sequencer/src/genesis.rs | 16 +++- crates/astria-sequencer/src/grpc/mod.rs | 1 + crates/astria-sequencer/src/grpc/slinky.rs | 67 ++++++++++++++ crates/astria-sequencer/src/sequencer.rs | 9 +- .../astria-sequencer/src/service/consensus.rs | 2 + 6 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 crates/astria-sequencer/src/grpc/slinky.rs diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index b9df631015..c5cbb1c0ac 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -242,3 +242,90 @@ pub mod tendermint { } } } + +#[path = ""] +pub mod slinky { + pub mod abci { + pub mod v1 { + include!("slinky.abci.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("slinky.abci.v1.serde.rs"); + } + } + } + + pub mod marketmap { + pub mod module { + pub mod v1 { + include!("slinky.marketmap.module.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("slinky.marketmap.module.v1.serde.rs"); + } + } + } + + pub mod v1 { + include!("slinky.marketmap.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("slinky.marketmap.v1.serde.rs"); + } + } + } + + pub mod oracle { + pub mod module { + pub mod v1 { + include!("slinky.oracle.module.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("slinky.oracle.module.v1.serde.rs"); + } + } + } + + pub mod v1 { + include!("slinky.oracle.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("slinky.oracle.v1.serde.rs"); + } + } + } + + pub mod service { + pub mod v1 { + include!("slinky.service.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("slinky.service.v1.serde.rs"); + } + } + } + + pub mod types { + pub mod v1 { + include!("slinky.types.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("slinky.types.v1.serde.rs"); + } + } + } +} diff --git a/crates/astria-sequencer/src/genesis.rs b/crates/astria-sequencer/src/genesis.rs index e484fc0781..08f24d777e 100644 --- a/crates/astria-sequencer/src/genesis.rs +++ b/crates/astria-sequencer/src/genesis.rs @@ -1,6 +1,9 @@ -use astria_core::primitive::v1::{ - asset, - Address, +use astria_core::{ + generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, + primitive::v1::{ + asset, + Address, + }, }; use penumbra_ibc::params::IBCParameters; use serde::{ @@ -27,6 +30,7 @@ pub(crate) struct GenesisState { pub(crate) ibc_params: IBCParameters, pub(crate) allowed_fee_assets: Vec, pub(crate) fees: Fees, + pub(crate) slinky: SlinkyGenesisState, } #[derive(Debug, thiserror::Error)] @@ -58,6 +62,7 @@ impl TryFrom for GenesisState { ibc_params, allowed_fee_assets, fees, + slinky, } = value; Ok(Self { @@ -70,6 +75,7 @@ impl TryFrom for GenesisState { ibc_params, allowed_fee_assets, fees, + slinky, }) } } @@ -86,6 +92,7 @@ pub(crate) struct UncheckedGenesisState { pub(crate) ibc_params: IBCParameters, pub(crate) allowed_fee_assets: Vec, pub(crate) fees: Fees, + pub(crate) slinky: SlinkyGenesisState, } impl UncheckedGenesisState { @@ -139,6 +146,7 @@ impl From for UncheckedGenesisState { ibc_params, allowed_fee_assets, fees, + slinky, } = value; Self { address_prefixes, @@ -150,6 +158,7 @@ impl From for UncheckedGenesisState { ibc_params, allowed_fee_assets, fees, + slinky, } } } @@ -254,6 +263,7 @@ mod test { bridge_sudo_change_fee: 24, ics20_withdrawal_base_fee: 24, }, + slinky: SlinkyGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/grpc/mod.rs b/crates/astria-sequencer/src/grpc/mod.rs index 253d97a6aa..2fe128a2e7 100644 --- a/crates/astria-sequencer/src/grpc/mod.rs +++ b/crates/astria-sequencer/src/grpc/mod.rs @@ -1 +1,2 @@ pub(crate) mod sequencer; +pub(crate) mod slinky; diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs new file mode 100644 index 0000000000..ef0d000c21 --- /dev/null +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -0,0 +1,67 @@ +use std::sync::Arc; + +use astria_core::generated::slinky::marketmap::v1::{ + query_server::Query as MarketMapQueryService, + LastUpdatedRequest, + LastUpdatedResponse, + MarketMapRequest, + MarketMapResponse, + MarketRequest, + MarketResponse, + ParamsRequest, + ParamsResponse, +}; +use cnidarium::Storage; +use tonic::{ + Request, + Response, + Status, +}; +use tracing::instrument; + +pub(crate) struct SequencerServer { + storage: Storage, +} + +impl SequencerServer { + pub(crate) fn new(storage: Storage) -> Self { + Self { + storage, + } + } +} + +#[async_trait::async_trait] +impl MarketMapQueryService for SequencerServer { + #[instrument(skip_all)] + async fn market_map( + self: Arc, + request: Request, + ) -> Result, Status> { + Ok(Response::new(MarketMapResponse::default())) + } + + #[instrument(skip_all)] + async fn market( + self: Arc, + request: Request, + ) -> Result, Status> { + Ok(Response::new(MarketResponse::default())) + } + + #[instrument(skip_all)] + async fn last_updated( + self: Arc, + request: Request, + ) -> Result, Status> { + Ok(Response::new(LastUpdatedResponse::default())) + } + + #[instrument(skip_all)] + async fn params( + self: Arc, + request: Request, + ) -> Result, Status> { + Ok(Response::new(ParamsResponse::default())) + } +} diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index e78b7585d8..6ccf84e3ff 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -5,7 +5,10 @@ use anyhow::{ Context as _, Result, }; -use astria_core::generated::sequencerblock::v1alpha1::sequencer_service_server::SequencerServiceServer; +use astria_core::generated::{ + sequencerblock::v1alpha1::sequencer_service_server::SequencerServiceServer, + slinky::marketmap::v1::query_server::QueryServer as MarketMapQueryServer, +}; use penumbra_tower_trace::{ trace::request_span, v038::RequestExt as _, @@ -188,6 +191,7 @@ fn start_grpc_server( let ibc = penumbra_ibc::component::rpc::IbcQuery::::new(storage.clone()); let sequencer_api = SequencerServer::new(storage.clone(), mempool); + let slinky_api = crate::grpc::slinky::SequencerServer::new(storage.clone()); let cors_layer: CorsLayer = CorsLayer::permissive(); // TODO: setup HTTPS? @@ -211,7 +215,8 @@ fn start_grpc_server( .add_service(ClientQueryServer::new(ibc.clone())) .add_service(ChannelQueryServer::new(ibc.clone())) .add_service(ConnectionQueryServer::new(ibc.clone())) - .add_service(SequencerServiceServer::new(sequencer_api)); + .add_service(SequencerServiceServer::new(sequencer_api)) + .add_service(MarketMapQueryServer::new(slinky_api)); info!(grpc_addr = grpc_addr.to_string(), "starting grpc server"); tokio::task::spawn( diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 051eb3fe70..e0b63eff86 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -223,6 +223,7 @@ mod test { SigningKey, VerificationKey, }, + generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, primitive::v1::RollupId, protocol::transaction::v1alpha1::{ action::SequenceAction, @@ -479,6 +480,7 @@ mod test { ibc_params: penumbra_ibc::params::IBCParameters::default(), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: default_fees(), + slinky: SlinkyGenesisState::default(), } .try_into() .unwrap(); From b2cc653c56bb90da32c2d52644ae4eeb0e9e6ce3 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 15:21:42 -0400 Subject: [PATCH 03/89] impl most of slinky grpc methods --- crates/astria-sequencer/src/app/test_utils.rs | 2 + .../src/app/tests_breaking_changes.rs | 2 + .../src/app/tests_execute_transaction.rs | 2 + crates/astria-sequencer/src/grpc/slinky.rs | 60 ++++++++-- crates/astria-sequencer/src/lib.rs | 1 + .../astria-sequencer/src/slinky/component.rs | 58 ++++++++++ crates/astria-sequencer/src/slinky/mod.rs | 2 + .../astria-sequencer/src/slinky/state_ext.rs | 105 ++++++++++++++++++ 8 files changed, 225 insertions(+), 7 deletions(-) create mode 100644 crates/astria-sequencer/src/slinky/component.rs create mode 100644 crates/astria-sequencer/src/slinky/mod.rs create mode 100644 crates/astria-sequencer/src/slinky/state_ext.rs diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 3979cbf7f2..0696a6fb8e 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -1,5 +1,6 @@ use astria_core::{ crypto::SigningKey, + generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, primitive::v1::{ Address, RollupId, @@ -107,6 +108,7 @@ pub(crate) fn unchecked_genesis_state() -> UncheckedGenesisState { ibc_params: IBCParameters::default(), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: default_fees(), + slinky: SlinkyGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 1924f257ec..1190c3a138 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -15,6 +15,7 @@ use std::{ }; use astria_core::{ + generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, primitive::v1::RollupId, protocol::transaction::v1alpha1::{ action::{ @@ -79,6 +80,7 @@ fn unchecked_genesis_state() -> UncheckedGenesisState { ibc_params: IBCParameters::default(), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: default_fees(), + slinky: SlinkyGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 35911de417..128a35c682 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use astria_core::{ crypto::SigningKey, + generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, primitive::v1::{ asset, RollupId, @@ -68,6 +69,7 @@ fn unchecked_genesis_state() -> UncheckedGenesisState { ibc_params: IBCParameters::default(), allowed_fee_assets: vec![default_native_asset()], fees: default_fees(), + slinky: SlinkyGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index ef0d000c21..ceb2336cd4 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -19,6 +19,11 @@ use tonic::{ }; use tracing::instrument; +use crate::{ + slinky::state_ext::StateReadExt as _, + state_ext::StateReadExt as _, +}; + pub(crate) struct SequencerServer { storage: Storage, } @@ -36,32 +41,73 @@ impl MarketMapQueryService for SequencerServer { #[instrument(skip_all)] async fn market_map( self: Arc, - request: Request, + _request: Request, ) -> Result, Status> { - Ok(Response::new(MarketMapResponse::default())) + let snapshot = self.storage.latest_snapshot(); + let market_map = snapshot.get_market_map().await.map_err(|e| { + Status::internal(format!("failed to get block market map from storage: {e}")) + })?; + let last_updated = snapshot + .get_market_map_last_updated_height() + .await + .map_err(|e| { + Status::internal(format!( + "failed to get block market map last updated height from storage: {e}" + )) + })?; + let chain_id = snapshot + .get_chain_id() + .await + .map_err(|e| Status::internal(format!("failed to get chain id from storage: {e}")))?; + + Ok(Response::new(MarketMapResponse { + market_map: market_map.map(Into::into), + last_updated, + chain_id: chain_id.to_string(), // TODO: is this the right chain id? + })) } #[instrument(skip_all)] async fn market( self: Arc, - request: Request, + _request: Request, ) -> Result, Status> { + // TODO Ok(Response::new(MarketResponse::default())) } #[instrument(skip_all)] async fn last_updated( self: Arc, - request: Request, + _request: Request, ) -> Result, Status> { - Ok(Response::new(LastUpdatedResponse::default())) + let snapshot = self.storage.latest_snapshot(); + let last_updated = snapshot + .get_market_map_last_updated_height() + .await + .map_err(|e| { + Status::internal(format!( + "failed to get block market map last updated height from storage: {e}" + )) + })?; + + Ok(Response::new(LastUpdatedResponse { + last_updated, + })) } #[instrument(skip_all)] async fn params( self: Arc, - request: Request, + _request: Request, ) -> Result, Status> { - Ok(Response::new(ParamsResponse::default())) + let snapshot = self.storage.latest_snapshot(); + let params = snapshot.get_params().await.map_err(|e| { + Status::internal(format!("failed to get block params from storage: {e}")) + })?; + + Ok(Response::new(ParamsResponse { + params: params.map(Into::into), + })) } } diff --git a/crates/astria-sequencer/src/lib.rs b/crates/astria-sequencer/src/lib.rs index 709ee76abd..2b542b7771 100644 --- a/crates/astria-sequencer/src/lib.rs +++ b/crates/astria-sequencer/src/lib.rs @@ -18,6 +18,7 @@ pub(crate) mod proposal; pub(crate) mod sequence; mod sequencer; pub(crate) mod service; +pub(crate) mod slinky; pub(crate) mod state_ext; pub(crate) mod storage_keys; pub(crate) mod transaction; diff --git a/crates/astria-sequencer/src/slinky/component.rs b/crates/astria-sequencer/src/slinky/component.rs new file mode 100644 index 0000000000..88da058446 --- /dev/null +++ b/crates/astria-sequencer/src/slinky/component.rs @@ -0,0 +1,58 @@ +use std::sync::Arc; + +use anyhow::{ + Context, + Result, +}; +use tendermint::abci::request::{ + BeginBlock, + EndBlock, +}; +use tracing::instrument; + +use super::state_ext::StateWriteExt; +use crate::{ + component::Component, + genesis::GenesisState, +}; + +#[derive(Default)] +pub(crate) struct SlinkyComponent; + +#[async_trait::async_trait] +impl Component for SlinkyComponent { + type AppState = GenesisState; + + #[instrument(name = "SlinkyComponent::init_chain", skip(state))] + async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { + if let Some(market_map) = &app_state.slinky.market_map { + state + .put_market_map(market_map.clone()) + .context("failed to put market map")?; + } + + if let Some(params) = &app_state.slinky.params { + state + .put_params(params.clone()) + .context("failed to put params")?; + } + + Ok(()) + } + + #[instrument(name = "SlinkyComponent::begin_block", skip(_state))] + async fn begin_block( + _state: &mut Arc, + _begin_block: &BeginBlock, + ) -> Result<()> { + Ok(()) + } + + #[instrument(name = "SlinkyComponent::end_block", skip(_state))] + async fn end_block( + _state: &mut Arc, + _end_block: &EndBlock, + ) -> Result<()> { + Ok(()) + } +} diff --git a/crates/astria-sequencer/src/slinky/mod.rs b/crates/astria-sequencer/src/slinky/mod.rs new file mode 100644 index 0000000000..7e016778de --- /dev/null +++ b/crates/astria-sequencer/src/slinky/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod component; +pub(crate) mod state_ext; diff --git a/crates/astria-sequencer/src/slinky/state_ext.rs b/crates/astria-sequencer/src/slinky/state_ext.rs new file mode 100644 index 0000000000..63fe6e424f --- /dev/null +++ b/crates/astria-sequencer/src/slinky/state_ext.rs @@ -0,0 +1,105 @@ +use anyhow::{ + Context, + Result, +}; +use astria_core::generated::slinky::marketmap::v1::{ + MarketMap, + Params, +}; +use async_trait::async_trait; +use borsh::{ + BorshDeserialize, + BorshSerialize, +}; +use cnidarium::{ + StateRead, + StateWrite, +}; +use tracing::instrument; + +const MARKET_MAP_KEY: &str = "slinkymarketmap"; +const PARAMS_KEY: &str = "slinkyparams"; +const MARKET_MAP_LAST_UPDATED_KEY: &[u8] = b"slinkymarketmaplastupdated"; + +/// Newtype wrapper to read and write a u64 from rocksdb. +#[derive(BorshSerialize, BorshDeserialize, Debug)] +struct Height(u64); + +#[async_trait] +pub(crate) trait StateReadExt: StateRead { + #[instrument(skip(self))] + async fn get_market_map(&self) -> Result> { + let bytes = self + .get_raw(MARKET_MAP_KEY) + .await + .context("failed to get market map from state")?; + match bytes { + Some(bytes) => { + let market_map = + serde_json::from_slice(&bytes).context("failed to deserialize market map")?; + Ok(Some(market_map)) + } + None => Ok(None), + } + } + + #[instrument(skip(self))] + async fn get_market_map_last_updated_height(&self) -> Result { + let Some(bytes) = self + .nonverifiable_get_raw(MARKET_MAP_LAST_UPDATED_KEY) + .await + .context("failed reading market map last updated height from state")? + else { + return Ok(0); + }; + let Height(height) = Height::try_from_slice(&bytes).context("invalid height bytes")?; + Ok(height) + } + + #[instrument(skip(self))] + async fn get_params(&self) -> Result> { + let bytes = self + .get_raw(PARAMS_KEY) + .await + .context("failed to get params from state")?; + match bytes { + Some(bytes) => { + let params = + serde_json::from_slice(&bytes).context("failed to deserialize params")?; + Ok(Some(params)) + } + None => Ok(None), + } + } +} + +impl StateReadExt for T {} + +#[async_trait] +pub(crate) trait StateWriteExt: StateWrite { + #[instrument(skip(self))] + fn put_market_map(&mut self, market_map: MarketMap) -> Result<()> { + let bytes = serde_json::to_vec(&market_map).context("failed to serialize market map")?; + self.put_raw(MARKET_MAP_KEY.to_string(), bytes); + Ok(()) + } + + #[instrument(skip(self))] + fn put_market_map_last_updated_height(&mut self, height: u64) -> Result<()> { + // TODO: since this is an optimization, should it be in the nonconsensus store? + self.nonverifiable_put_raw( + MARKET_MAP_LAST_UPDATED_KEY.to_vec(), + borsh::to_vec(&Height(height)).context("failed to serialize height")?, + ); + Ok(()) + } + + #[instrument(skip(self))] + fn put_params(&mut self, params: Params) -> Result<()> { + let bytes = serde_json::to_vec(¶ms).context("failed to serialize params")?; + self.put_raw(PARAMS_KEY.to_string(), bytes); + Ok(()) + } +} + +impl StateWriteExt for T {} From ff618db315dcb549a5769ba5e43538c6a12eea32 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 15:37:38 -0400 Subject: [PATCH 04/89] add oracle option to config and connect on startup --- crates/astria-sequencer/Cargo.toml | 2 +- crates/astria-sequencer/local.env.example | 13 ++++++++- crates/astria-sequencer/src/app/mod.rs | 15 +++++++++- crates/astria-sequencer/src/app/test_utils.rs | 2 +- crates/astria-sequencer/src/config.rs | 7 +++++ crates/astria-sequencer/src/sequencer.rs | 29 ++++++++++++++++++- ...sis__test__genesis_state_is_unchanged.snap | 3 +- 7 files changed, 65 insertions(+), 6 deletions(-) diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index ac70e1028b..af4f8d09ea 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -15,7 +15,7 @@ name = "astria-sequencer" default = [] [dependencies] -astria-core = { path = "../astria-core", features = ["server", "serde"] } +astria-core = { path = "../astria-core", features = ["server", "client", "serde"] } astria-build-info = { path = "../astria-build-info", features = ["runtime"] } config = { package = "astria-config", path = "../astria-config" } merkle = { package = "astria-merkle", path = "../astria-merkle" } diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index 05cb7937a0..cb7e6d3701 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -11,7 +11,7 @@ ASTRIA_SEQUENCER_DB_FILEPATH="/tmp/astria_db" ASTRIA_SEQUENCER_ENABLE_MINT=false # Socket address for gRPC server -ASTRIA_SEQUENCER_GRPC_ADDR="127.0.0.1:8080" +ASTRIA_SEQUENCER_GRPC_ADDR="127.0.0.1:9090" # Log level for the sequencer ASTRIA_SEQUENCER_LOG="astria_sequencer=info" @@ -34,6 +34,16 @@ ASTRIA_SEQUENCER_METRICS_HTTP_LISTENER_ADDR="127.0.0.1:9000" # `ASTRIA_SEQUENCER_FORCE_STDOUT` is set to `true`. ASTRIA_SEQUENCER_PRETTY_PRINT=false +# If the oracle is enabled. If true, the oracle_grpc_addr must be set. +# Should be true for validator nodes and false for non-validator nodes. +ASTRIA_SEQUENCER_ORACLE_ENABLED=false + +# The gRPC endpoint for the oracle sidecar. +ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="127.0.0.1:8080" + +# The timeout for the responses from the oracle sidecar in milliseconds. +ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT=1000 + # If set to any non-empty value removes ANSI escape characters from the pretty # printed output. Note that this does nothing unless `ASTRIA_SEQUENCER_PRETTY_PRINT` # is set to `true`. @@ -56,3 +66,4 @@ OTEL_EXPORTER_OTLP_TRACES_COMPRESSION="gzip" OTEL_EXPORTER_OTLP_HEADERS="key1=value1,key2=value2" # The HTTP headers that will be set when sending gRPC requests. This takes precedence over `OTEL_EXPORTER_OTLP_HEADERS` if set. OTEL_EXPORTER_OTLP_TRACE_HEADERS="key1=value1,key2=value2" + diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 9ecdf35efc..5e7a5b411e 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -18,7 +18,13 @@ use anyhow::{ Context, }; use astria_core::{ - generated::protocol::transaction::v1alpha1 as raw, + generated::{ + protocol::transaction::v1alpha1 as raw, + slinky::service::v1::{ + oracle_client::OracleClient, + QueryPricesRequest, + }, + }, primitive::v1::Address, protocol::{ abci::AbciErrorCode, @@ -53,6 +59,7 @@ use tendermint::{ AppHash, Hash, }; +use tonic::transport::channel::Channel; use tracing::{ debug, info, @@ -167,6 +174,10 @@ pub(crate) struct App { #[allow(clippy::struct_field_names)] app_hash: AppHash, + // gRPC client for the slinky oracle sidecar. + // only set if this is a validator node. + oracle_client: Option>, + metrics: &'static Metrics, } @@ -174,6 +185,7 @@ impl App { pub(crate) async fn new( snapshot: Snapshot, mempool: Mempool, + oracle_client: Option>, metrics: &'static Metrics, ) -> anyhow::Result { debug!("initializing App instance"); @@ -199,6 +211,7 @@ impl App { execution_results: None, write_batch: None, app_hash, + oracle_client, metrics, }) } diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 0696a6fb8e..51a1b4927f 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -126,7 +126,7 @@ pub(crate) async fn initialize_app_with_storage( let snapshot = storage.latest_snapshot(); let mempool = Mempool::new(); let metrics = Box::leak(Box::new(Metrics::new())); - let mut app = App::new(snapshot, mempool, metrics).await.unwrap(); + let mut app = App::new(snapshot, mempool, None, metrics).await.unwrap(); let genesis_state = genesis_state.unwrap_or_else(self::genesis_state); diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index 234a67ca5e..9268764db4 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -31,6 +31,13 @@ pub struct Config { pub metrics_http_listener_addr: String, /// Writes a human readable format to stdout instead of JSON formatted OTEL trace data. pub pretty_print: bool, + /// If the oracle is enabled. If true, the oracle_grpc_addr must be set. + /// Should be true for validator nodes and false for non-validator nodes. + pub oracle_enabed: bool, + /// The gRPC endpoint for the oracle sidecar. + pub oracle_grpc_addr: String, + /// The timeout for the responses from the oracle sidecar in milliseconds. + pub oracle_client_timeout: u64, } impl config::Config for Config { diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index 6ccf84e3ff..f00491e865 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -103,8 +103,35 @@ impl Sequencer { .context("failed to initialize global address base prefix")?; } + let oracle_client = if config.oracle_enabed { + use astria_core::generated::slinky::service::v1::{ + oracle_client::OracleClient, + QueryPricesRequest, + }; + use tonic::transport::{ + Endpoint, + Uri, + }; + + let uri: Uri = config + .oracle_grpc_addr + .parse() + .context("failed parsing oracle grpc address as Uri")?; + let endpoint = Endpoint::from(uri.clone()); + let mut oracle_client = OracleClient::new(endpoint.connect_lazy()); + + // ensure the oracle sidecar is reachable + let _ = oracle_client + .prices(QueryPricesRequest::default()) + .await + .context("failed to get oracle prices")?; + Some(oracle_client) + } else { + None + }; + let mempool = Mempool::new(); - let app = App::new(snapshot, mempool.clone(), metrics) + let app = App::new(snapshot, mempool.clone(), oracle_client, metrics) .await .context("failed to initialize app")?; diff --git a/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap b/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap index 17211f5bed..cf762a8319 100644 --- a/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap +++ b/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap @@ -57,5 +57,6 @@ expression: genesis_state() "bridge_lock_byte_cost_multiplier": 1, "bridge_sudo_change_fee": 24, "ics20_withdrawal_base_fee": 24 - } + }, + "slinky": {} } From 280111dc48586749ba41b130bc3986606588765c Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 16:20:55 -0400 Subject: [PATCH 05/89] begin vote extension handler logic --- crates/astria-sequencer/local.env.example | 2 +- crates/astria-sequencer/src/app/mod.rs | 47 +++++++--- .../src/app/vote_extension.rs | 90 +++++++++++++++++++ crates/astria-sequencer/src/config.rs | 2 +- crates/astria-sequencer/src/sequencer.rs | 29 +++--- .../astria-sequencer/src/service/consensus.rs | 52 +++++++++-- 6 files changed, 188 insertions(+), 34 deletions(-) create mode 100644 crates/astria-sequencer/src/app/vote_extension.rs diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index cb7e6d3701..b9a7047e9f 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -39,7 +39,7 @@ ASTRIA_SEQUENCER_PRETTY_PRINT=false ASTRIA_SEQUENCER_ORACLE_ENABLED=false # The gRPC endpoint for the oracle sidecar. -ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="127.0.0.1:8080" +ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="http://127.0.0.1:8080" # The timeout for the responses from the oracle sidecar in milliseconds. ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT=1000 diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 5e7a5b411e..5619ba5174 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -7,6 +7,8 @@ mod tests_breaking_changes; #[cfg(test)] mod tests_execute_transaction; +pub(crate) mod vote_extension; + use std::{ collections::VecDeque, sync::Arc, @@ -18,13 +20,7 @@ use anyhow::{ Context, }; use astria_core::{ - generated::{ - protocol::transaction::v1alpha1 as raw, - slinky::service::v1::{ - oracle_client::OracleClient, - QueryPricesRequest, - }, - }, + generated::protocol::transaction::v1alpha1 as raw, primitive::v1::Address, protocol::{ abci::AbciErrorCode, @@ -59,7 +55,6 @@ use tendermint::{ AppHash, Hash, }; -use tonic::transport::channel::Channel; use tracing::{ debug, info, @@ -174,9 +169,8 @@ pub(crate) struct App { #[allow(clippy::struct_field_names)] app_hash: AppHash, - // gRPC client for the slinky oracle sidecar. - // only set if this is a validator node. - oracle_client: Option>, + // should be set if this is a validator node. + vote_extension_handler: Option, metrics: &'static Metrics, } @@ -185,7 +179,7 @@ impl App { pub(crate) async fn new( snapshot: Snapshot, mempool: Mempool, - oracle_client: Option>, + vote_extension_handler: Option, metrics: &'static Metrics, ) -> anyhow::Result { debug!("initializing App instance"); @@ -211,7 +205,7 @@ impl App { execution_results: None, write_batch: None, app_hash, - oracle_client, + vote_extension_handler, metrics, }) } @@ -747,6 +741,33 @@ impl App { Ok(()) } + #[instrument(name = "App::extend_vote", skip_all)] + pub(crate) async fn extend_vote( + &mut self, + _extend_vote: abci::request::ExtendVote, + ) -> anyhow::Result { + let Some(handler) = self.vote_extension_handler.as_mut() else { + // we allow validators to *not* use the oracle sidecar currently + // however, if >1/3 of validators are not using the oracle, the prices will not update. + return Ok(abci::response::ExtendVote { + vote_extension: vec![].into(), + }); + }; + handler.extend_vote().await + } + + pub(crate) async fn verify_vote_extension( + &mut self, + vote_extension: abci::request::VerifyVoteExtension, + ) -> anyhow::Result { + let Some(handler) = self.vote_extension_handler.as_mut() else { + // TODO: should we still verify if our own oracle isn't set? + // i think so? + return Ok(abci::response::VerifyVoteExtension::Accept); + }; + handler.verify_vote_extension(vote_extension).await + } + /// Executes the given block, but does not write it to disk. /// /// `commit` must be called after this to write the block to disk. diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs new file mode 100644 index 0000000000..e6ff2f3360 --- /dev/null +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -0,0 +1,90 @@ +use std::collections::HashMap; + +use anyhow::Context as _; +use astria_core::generated::slinky::{ + abci::v1::OracleVoteExtension, + service::v1::{ + oracle_client::OracleClient, + QueryPricesRequest, + QueryPricesResponse, + }, + types::v1::CurrencyPair, +}; +use prost::Message as _; +use tendermint::abci; +use tonic::transport::Channel; + +pub(crate) struct Handler { + // gRPC client for the slinky oracle sidecar. + oracle_client: OracleClient, +} + +impl Handler { + pub(crate) fn new(oracle_client: OracleClient) -> Self { + Self { + oracle_client, + } + } + + pub(crate) async fn extend_vote(&mut self) -> anyhow::Result { + // TODO: use oracle client timeout + let prices = match self.oracle_client.prices(QueryPricesRequest {}).await { + Ok(prices) => prices.into_inner(), + Err(e) => { + tracing::error!( + error = %e, + "failed to get prices from oracle sidecar" + ); + return Ok(abci::response::ExtendVote { + vote_extension: vec![].into(), + }); + } + }; + + let oracle_vote_extension = self + .transform_oracle_service_prices(prices) + .context("failed to transform oracle service prices")?; + Ok(abci::response::ExtendVote { + // TODO: what codec does skip use for this? does it matter here? + // don't think so but good to check + vote_extension: oracle_vote_extension.encode_to_vec().into(), + }) + } + + pub(crate) async fn verify_vote_extension( + &mut self, + vote_extension: abci::request::VerifyVoteExtension, + ) -> anyhow::Result { + // TODO: verify the vote extension based on slinky rules + let _oracle_vote_extension = OracleVoteExtension::decode(vote_extension.vote_extension)?; + Ok(abci::response::VerifyVoteExtension::Accept) + } + + // see https://github.com/skip-mev/slinky/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 + fn transform_oracle_service_prices( + &self, + prices: QueryPricesResponse, + ) -> anyhow::Result { + for (currency_pair_id, price_string) in prices.prices { + let _currency_pair = currency_pair_from_string(¤cy_pair_id)?; + let _price = price_string.parse::()?; + + // TODO: oracle module state + } + + Ok(OracleVoteExtension { + prices: HashMap::default(), + }) + } +} + +fn currency_pair_from_string(s: &str) -> anyhow::Result { + let parts: Vec<&str> = s.split('/').collect(); + if parts.len() != 2 { + anyhow::bail!("invalid currency pair string: {}", s); + } + Ok(CurrencyPair { + base: parts[0].to_string(), + quote: parts[1].to_string(), + }) +} diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index 9268764db4..f03d00db39 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -33,7 +33,7 @@ pub struct Config { pub pretty_print: bool, /// If the oracle is enabled. If true, the oracle_grpc_addr must be set. /// Should be true for validator nodes and false for non-validator nodes. - pub oracle_enabed: bool, + pub oracle_enabled: bool, /// The gRPC endpoint for the oracle sidecar. pub oracle_grpc_addr: String, /// The timeout for the responses from the oracle sidecar in milliseconds. diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index f00491e865..57dbe0f709 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -7,7 +7,13 @@ use anyhow::{ }; use astria_core::generated::{ sequencerblock::v1alpha1::sequencer_service_server::SequencerServiceServer, - slinky::marketmap::v1::query_server::QueryServer as MarketMapQueryServer, + slinky::{ + marketmap::v1::query_server::QueryServer as MarketMapQueryServer, + service::v1::{ + oracle_client::OracleClient, + QueryPricesRequest, + }, + }, }; use penumbra_tower_trace::{ trace::request_span, @@ -26,6 +32,10 @@ use tokio::{ }, task::JoinHandle, }; +use tonic::transport::{ + Endpoint, + Uri, +}; use tower_abci::v038::Server; use tracing::{ error, @@ -103,16 +113,7 @@ impl Sequencer { .context("failed to initialize global address base prefix")?; } - let oracle_client = if config.oracle_enabed { - use astria_core::generated::slinky::service::v1::{ - oracle_client::OracleClient, - QueryPricesRequest, - }; - use tonic::transport::{ - Endpoint, - Uri, - }; - + let vote_extension_handler = if config.oracle_enabled { let uri: Uri = config .oracle_grpc_addr .parse() @@ -121,17 +122,19 @@ impl Sequencer { let mut oracle_client = OracleClient::new(endpoint.connect_lazy()); // ensure the oracle sidecar is reachable + // TODO: allow this to retry in case the oracle sidecar is not ready yet let _ = oracle_client .prices(QueryPricesRequest::default()) .await .context("failed to get oracle prices")?; - Some(oracle_client) + info!("oracle sidecar is reachable"); + Some(crate::app::vote_extension::Handler::new(oracle_client)) } else { None }; let mempool = Mempool::new(); - let app = App::new(snapshot, mempool.clone(), oracle_client, metrics) + let app = App::new(snapshot, mempool.clone(), vote_extension_handler, metrics) .await .context("failed to initialize app")?; diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index e0b63eff86..53043646f9 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -99,13 +99,33 @@ impl Consensus { }, ) } - ConsensusRequest::ExtendVote(_) => { - ConsensusResponse::ExtendVote(response::ExtendVote { - vote_extension: vec![].into(), + ConsensusRequest::ExtendVote(extend_vote) => { + ConsensusResponse::ExtendVote(match self.handle_extend_vote(extend_vote).await { + Ok(response) => response, + Err(e) => { + warn!( + error = AsRef::::as_ref(&e), + "failed to extend vote, returning empty vote extension" + ); + response::ExtendVote { + vote_extension: vec![].into(), + } + } }) } - ConsensusRequest::VerifyVoteExtension(_) => { - ConsensusResponse::VerifyVoteExtension(response::VerifyVoteExtension::Accept) + ConsensusRequest::VerifyVoteExtension(vote_extension) => { + ConsensusResponse::VerifyVoteExtension( + match self.handle_verify_vote_extension(vote_extension).await { + Ok(response) => response, + Err(e) => { + warn!( + error = AsRef::::as_ref(&e), + "rejecting vote extension" + ); + response::VerifyVoteExtension::Reject + } + }, + ) } ConsensusRequest::FinalizeBlock(finalize_block) => ConsensusResponse::FinalizeBlock( self.finalize_block(finalize_block) @@ -186,6 +206,24 @@ impl Consensus { Ok(()) } + #[instrument(skip_all)] + async fn handle_extend_vote( + &mut self, + extend_vote: request::ExtendVote, + ) -> anyhow::Result { + let extend_vote = self.app.extend_vote(extend_vote).await?; + Ok(extend_vote) + } + + #[instrument(skip_all)] + async fn handle_verify_vote_extension( + &mut self, + vote_extension: request::VerifyVoteExtension, + ) -> anyhow::Result { + let result = self.app.verify_vote_extension(vote_extension).await?; + Ok(result) + } + #[instrument(skip_all, fields( hash = %finalize_block.hash, height = %finalize_block.height, @@ -489,7 +527,9 @@ mod test { let snapshot = storage.latest_snapshot(); let mempool = Mempool::new(); let metrics = Box::leak(Box::new(Metrics::new())); - let mut app = App::new(snapshot, mempool.clone(), metrics).await.unwrap(); + let mut app = App::new(snapshot, mempool.clone(), None, metrics) + .await + .unwrap(); app.init_chain(storage.clone(), genesis_state, vec![], "test".to_string()) .await .unwrap(); From 565ef67fa785a02532fe1b9bf090f80690888290 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 16:39:53 -0400 Subject: [PATCH 06/89] remove unused protos --- .../cosmos/app/v1alpha1/module.proto | 81 ---------- .../cosmos_sdk/cosmos/msg/v1/msg.proto | 2 + .../cosmos_sdk/cosmos_proto/cosmos.proto | 148 +++++++++--------- .../slinky/marketmap/module/v1/module.proto | 21 --- .../vendored/slinky/marketmap/v1/market.proto | 4 +- .../vendored/slinky/marketmap/v1/query.proto | 14 +- proto/vendored/slinky/marketmap/v1/tx.proto | 17 +- .../slinky/oracle/module/v1/module.proto | 16 -- proto/vendored/slinky/oracle/v1/genesis.proto | 10 +- proto/vendored/slinky/oracle/v1/query.proto | 18 +-- proto/vendored/slinky/oracle/v1/tx.proto | 14 +- proto/vendored/slinky/service/v1/oracle.proto | 4 +- 12 files changed, 112 insertions(+), 237 deletions(-) delete mode 100644 proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto delete mode 100644 proto/vendored/slinky/marketmap/module/v1/module.proto delete mode 100644 proto/vendored/slinky/oracle/module/v1/module.proto diff --git a/proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto b/proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto deleted file mode 100644 index 745b1fd69e..0000000000 --- a/proto/vendored/cosmos_sdk/cosmos/app/v1alpha1/module.proto +++ /dev/null @@ -1,81 +0,0 @@ -syntax = "proto3"; -package cosmos.app.v1alpha1; -import "google/protobuf/descriptor.proto"; -extend google.protobuf.MessageOptions { - // module indicates that this proto type is a config object for an app module - // and optionally provides other descriptive information about the module. - // It is recommended that a new module config object and go module is versioned - // for every state machine breaking version of a module. The recommended - // pattern for doing this is to put module config objects in a separate proto - // package from the API they expose. Ex: the cosmos.group.v1 API would be - // exposed by module configs cosmos.group.module.v1, cosmos.group.module.v2, etc. - ModuleDescriptor module = 57193479; -} -// ModuleDescriptor describes an app module. -message ModuleDescriptor { - // go_import names the package that should be imported by an app to load the - // module in the runtime module registry. It is required to make debugging - // of configuration errors easier for users. - string go_import = 1; - // use_package refers to a protobuf package that this module - // uses and exposes to the world. In an app, only one module should "use" - // or own a single protobuf package. It is assumed that the module uses - // all of the .proto files in a single package. - repeated PackageReference use_package = 2; - // can_migrate_from defines which module versions this module can migrate - // state from. The framework will check that one module version is able to - // migrate from a previous module version before attempting to update its - // config. It is assumed that modules can transitively migrate from earlier - // versions. For instance if v3 declares it can migrate from v2, and v2 - // declares it can migrate from v1, the framework knows how to migrate - // from v1 to v3, assuming all 3 module versions are registered at runtime. - repeated MigrateFromInfo can_migrate_from = 3; -} -// PackageReference is a reference to a protobuf package used by a module. -message PackageReference { - // name is the fully-qualified name of the package. - string name = 1; - // revision is the optional revision of the package that is being used. - // Protobuf packages used in Cosmos should generally have a major version - // as the last part of the package name, ex. foo.bar.baz.v1. - // The revision of a package can be thought of as the minor version of a - // package which has additional backwards compatible definitions that weren't - // present in a previous version. - // - // A package should indicate its revision with a source code comment - // above the package declaration in one of its files containing the - // text "Revision N" where N is an integer revision. All packages start - // at revision 0 the first time they are released in a module. - // - // When a new version of a module is released and items are added to existing - // .proto files, these definitions should contain comments of the form - // "Since: Revision N" where N is an integer revision. - // - // When the module runtime starts up, it will check the pinned proto - // image and panic if there are runtime protobuf definitions that are not - // in the pinned descriptor which do not have - // a "Since Revision N" comment or have a "Since Revision N" comment where - // N is <= to the revision specified here. This indicates that the protobuf - // files have been updated, but the pinned file descriptor hasn't. - // - // If there are items in the pinned file descriptor with a revision - // greater than the value indicated here, this will also cause a panic - // as it may mean that the pinned descriptor for a legacy module has been - // improperly updated or that there is some other versioning discrepancy. - // Runtime protobuf definitions will also be checked for compatibility - // with pinned file descriptors to make sure there are no incompatible changes. - // - // This behavior ensures that: - // * pinned proto images are up-to-date - // * protobuf files are carefully annotated with revision comments which - // are important good client UX - // * protobuf files are changed in backwards and forwards compatible ways - uint32 revision = 2; -} -// MigrateFromInfo is information on a module version that a newer module -// can migrate from. -message MigrateFromInfo { - // module is the fully-qualified protobuf name of the module config object - // for the previous module version, ex: "cosmos.group.module.v1.Module". - string module = 1; -} diff --git a/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto b/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto index 32be62827e..7e88f27d3a 100644 --- a/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto +++ b/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto @@ -1,6 +1,8 @@ syntax = "proto3"; package cosmos.msg.v1; + import "google/protobuf/descriptor.proto"; + // TODO(fdymylja): once we fully migrate to protov2 the go_package needs to be updated. // We need this right now because gogoproto codegen needs to import the extension. option go_package = "github.com/cosmos/cosmos-sdk/types/msgservice"; diff --git a/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto b/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto index d827478af9..d3f331ac6e 100644 --- a/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto +++ b/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto @@ -5,77 +5,72 @@ import "google/protobuf/descriptor.proto"; option go_package = "github.com/cosmos/cosmos-proto;cosmos_proto"; -extend google.protobuf.MethodOptions { - - // method_added_in is used to indicate from which version the method was added. - string method_added_in = 93001; +extend google.protobuf.MethodOptions { + // method_added_in is used to indicate from which version the method was added. + string method_added_in = 93001; } extend google.protobuf.MessageOptions { - - // implements_interface is used to indicate the type name of the interface - // that a message implements so that it can be used in google.protobuf.Any - // fields that accept that interface. A message can implement multiple - // interfaces. Interfaces should be declared using a declare_interface - // file option. - repeated string implements_interface = 93001; - - // message_added_in is used to indicate from which version the message was added. - string message_added_in = 93002; + // implements_interface is used to indicate the type name of the interface + // that a message implements so that it can be used in google.protobuf.Any + // fields that accept that interface. A message can implement multiple + // interfaces. Interfaces should be declared using a declare_interface + // file option. + repeated string implements_interface = 93001; + + // message_added_in is used to indicate from which version the message was added. + string message_added_in = 93002; } extend google.protobuf.FieldOptions { - - // accepts_interface is used to annotate that a google.protobuf.Any - // field accepts messages that implement the specified interface. - // Interfaces should be declared using a declare_interface file option. - string accepts_interface = 93001; - - // scalar is used to indicate that this field follows the formatting defined - // by the named scalar which should be declared with declare_scalar. Code - // generators may choose to use this information to map this field to a - // language-specific type representing the scalar. - string scalar = 93002; - - // field_added_in is used to indicate from which version the field was added. - string field_added_in = 93003; + // accepts_interface is used to annotate that a google.protobuf.Any + // field accepts messages that implement the specified interface. + // Interfaces should be declared using a declare_interface file option. + string accepts_interface = 93001; + + // scalar is used to indicate that this field follows the formatting defined + // by the named scalar which should be declared with declare_scalar. Code + // generators may choose to use this information to map this field to a + // language-specific type representing the scalar. + string scalar = 93002; + + // field_added_in is used to indicate from which version the field was added. + string field_added_in = 93003; } extend google.protobuf.FileOptions { - - // declare_interface declares an interface type to be used with - // accepts_interface and implements_interface. Interface names are - // expected to follow the following convention such that their declaration - // can be discovered by tools: for a given interface type a.b.C, it is - // expected that the declaration will be found in a protobuf file named - // a/b/interfaces.proto in the file descriptor set. - repeated InterfaceDescriptor declare_interface = 793021; - - // declare_scalar declares a scalar type to be used with - // the scalar field option. Scalar names are - // expected to follow the following convention such that their declaration - // can be discovered by tools: for a given scalar type a.b.C, it is - // expected that the declaration will be found in a protobuf file named - // a/b/scalars.proto in the file descriptor set. - repeated ScalarDescriptor declare_scalar = 793022; - - // file_added_in is used to indicate from which the version the file was added. - string file_added_in = 793023; + // declare_interface declares an interface type to be used with + // accepts_interface and implements_interface. Interface names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given interface type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/interfaces.proto in the file descriptor set. + repeated InterfaceDescriptor declare_interface = 793021; + + // declare_scalar declares a scalar type to be used with + // the scalar field option. Scalar names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given scalar type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/scalars.proto in the file descriptor set. + repeated ScalarDescriptor declare_scalar = 793022; + + // file_added_in is used to indicate from which the version the file was added. + string file_added_in = 793023; } // InterfaceDescriptor describes an interface type to be used with // accepts_interface and implements_interface and declared by declare_interface. message InterfaceDescriptor { - - // name is the name of the interface. It should be a short-name (without - // a period) such that the fully qualified name of the interface will be - // package.name, ex. for the package a.b and interface named C, the - // fully-qualified name will be a.b.C. - string name = 1; - - // description is a human-readable description of the interface and its - // purpose. - string description = 2; + // name is the name of the interface. It should be a short-name (without + // a period) such that the fully qualified name of the interface will be + // package.name, ex. for the package a.b and interface named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the interface and its + // purpose. + string description = 2; } // ScalarDescriptor describes an scalar type to be used with @@ -86,27 +81,26 @@ message InterfaceDescriptor { // valid syntactical representation for a given semantic meaning, // i.e. the encoding should be deterministic. message ScalarDescriptor { - - // name is the name of the scalar. It should be a short-name (without - // a period) such that the fully qualified name of the scalar will be - // package.name, ex. for the package a.b and scalar named C, the - // fully-qualified name will be a.b.C. - string name = 1; - - // description is a human-readable description of the scalar and its - // encoding format. For instance a big integer or decimal scalar should - // specify precisely the expected encoding format. - string description = 2; - - // field_type is the type of field with which this scalar can be used. - // Scalars can be used with one and only one type of field so that - // encoding standards and simple and clear. Currently only string and - // bytes fields are supported for scalars. - repeated ScalarType field_type = 3; + // name is the name of the scalar. It should be a short-name (without + // a period) such that the fully qualified name of the scalar will be + // package.name, ex. for the package a.b and scalar named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the scalar and its + // encoding format. For instance a big integer or decimal scalar should + // specify precisely the expected encoding format. + string description = 2; + + // field_type is the type of field with which this scalar can be used. + // Scalars can be used with one and only one type of field so that + // encoding standards and simple and clear. Currently only string and + // bytes fields are supported for scalars. + repeated ScalarType field_type = 3; } enum ScalarType { - SCALAR_TYPE_UNSPECIFIED = 0; - SCALAR_TYPE_STRING = 1; - SCALAR_TYPE_BYTES = 2; + SCALAR_TYPE_UNSPECIFIED = 0; + SCALAR_TYPE_STRING = 1; + SCALAR_TYPE_BYTES = 2; } diff --git a/proto/vendored/slinky/marketmap/module/v1/module.proto b/proto/vendored/slinky/marketmap/module/v1/module.proto deleted file mode 100644 index 57d3e91492..0000000000 --- a/proto/vendored/slinky/marketmap/module/v1/module.proto +++ /dev/null @@ -1,21 +0,0 @@ -syntax = "proto3"; - -package slinky.marketmap.module.v1; - -import "cosmos_sdk/cosmos/app/v1alpha1/module.proto"; - -// Module is the config object of the builder module. -message Module { - option (cosmos.app.v1alpha1.module) = { - go_import : "github.com/skip-mev/slinky/x/marketmap" - }; - - // Authority defines the custom module authority. If not set, defaults to the - // governance module. - string authority = 1; - - // HooksOrder specifies the order of marketmap hooks and should be a list - // of module names which provide a marketmap hooks instance. If no order is - // provided, then hooks will be applied in alphabetical order of module names. - repeated string hooks_order = 2; -} diff --git a/proto/vendored/slinky/marketmap/v1/market.proto b/proto/vendored/slinky/marketmap/v1/market.proto index db69c16dc1..b884e48b4d 100644 --- a/proto/vendored/slinky/marketmap/v1/market.proto +++ b/proto/vendored/slinky/marketmap/v1/market.proto @@ -1,10 +1,10 @@ syntax = "proto3"; package slinky.marketmap.v1; -option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; - import "slinky/types/v1/currency_pair.proto"; +option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; + // Market encapsulates a Ticker and its provider-specific configuration. message Market { // Ticker represents a price feed for a given asset pair i.e. BTC/USD. The diff --git a/proto/vendored/slinky/marketmap/v1/query.proto b/proto/vendored/slinky/marketmap/v1/query.proto index 002255f790..9db9113c0e 100644 --- a/proto/vendored/slinky/marketmap/v1/query.proto +++ b/proto/vendored/slinky/marketmap/v1/query.proto @@ -2,9 +2,9 @@ syntax = "proto3"; package slinky.marketmap.v1; import "google/api/annotations.proto"; -import "slinky/types/v1/currency_pair.proto"; import "slinky/marketmap/v1/market.proto"; import "slinky/marketmap/v1/params.proto"; +import "slinky/types/v1/currency_pair.proto"; option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; @@ -29,9 +29,7 @@ service Query { // Params returns the current x/marketmap module parameters. rpc Params(ParamsRequest) returns (ParamsResponse) { - option (google.api.http) = { - get : "/slinky/marketmap/v1/params" - }; + option (google.api.http) = {get: "/slinky/marketmap/v1/params"}; } } @@ -72,7 +70,9 @@ message MarketResponse { message ParamsRequest {} // ParamsResponse is the response type for the Query/Params RPC method. -message ParamsResponse { Params params = 1; } +message ParamsResponse { + Params params = 1; +} // LastUpdatedRequest is the request type for the Query/LastUpdated RPC // method. @@ -80,4 +80,6 @@ message LastUpdatedRequest {} // LastUpdatedResponse is the response type for the Query/LastUpdated RPC // method. -message LastUpdatedResponse { uint64 last_updated = 1; } +message LastUpdatedResponse { + uint64 last_updated = 1; +} diff --git a/proto/vendored/slinky/marketmap/v1/tx.proto b/proto/vendored/slinky/marketmap/v1/tx.proto index e8932361d2..3877a3e295 100644 --- a/proto/vendored/slinky/marketmap/v1/tx.proto +++ b/proto/vendored/slinky/marketmap/v1/tx.proto @@ -24,8 +24,7 @@ service Msg { // RemoveMarketAuthorities defines a method for removing market authorities // from the x/marketmap module. the signer must be the admin. - rpc RemoveMarketAuthorities(MsgRemoveMarketAuthorities) - returns (MsgRemoveMarketAuthoritiesResponse); + rpc RemoveMarketAuthorities(MsgRemoveMarketAuthorities) returns (MsgRemoveMarketAuthoritiesResponse); // UpsertMarkets wraps both Create / Update markets into a single message. Specifically // if a market does not exist it will be created, otherwise it will be updated. The response @@ -33,18 +32,18 @@ service Msg { rpc UpsertMarkets(MsgUpsertMarkets) returns (MsgUpsertMarketsResponse); } -// MsgUpsertMarkets defines a message carrying a payload for performing market upserts (update or +// MsgUpsertMarkets defines a message carrying a payload for performing market upserts (update or // create if does not exist) in the x/marketmap module. message MsgUpsertMarkets { option (cosmos.msg.v1.signer) = "authority"; // Authority is the signer of this transaction. This authority must be // authorized by the module to execute the message. - string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // CreateMarkets is the list of all markets to be created for the given // transaction. - repeated Market markets = 2; + repeated Market markets = 2; } // MsgUpsertMarketsResponse is the response from the UpsertMarkets API in the x/marketmap module. @@ -60,7 +59,7 @@ message MsgCreateMarkets { // Authority is the signer of this transaction. This authority must be // authorized by the module to execute the message. - string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // CreateMarkets is the list of all markets to be created for the given // transaction. @@ -77,7 +76,7 @@ message MsgUpdateMarkets { // Authority is the signer of this transaction. This authority must be // authorized by the module to execute the message. - string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // UpdateMarkets is the list of all markets to be updated for the given // transaction. @@ -96,7 +95,7 @@ message MsgParams { Params params = 1; // Authority defines the authority that is updating the x/marketmap module // parameters. - string authority = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string authority = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } // MsgParamsResponse defines the Msg/Params response type. @@ -113,7 +112,7 @@ message MsgRemoveMarketAuthorities { // Admin defines the authority that is the x/marketmap // Admin account. This account is set in the module parameters. - string admin = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string admin = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } // MsgRemoveMarketAuthoritiesResponse defines the diff --git a/proto/vendored/slinky/oracle/module/v1/module.proto b/proto/vendored/slinky/oracle/module/v1/module.proto deleted file mode 100644 index 448d31c024..0000000000 --- a/proto/vendored/slinky/oracle/module/v1/module.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -package slinky.oracle.module.v1; - -import "cosmos_sdk/cosmos/app/v1alpha1/module.proto"; - -// Module is the config object of the builder module. -message Module { - option (cosmos.app.v1alpha1.module) = { - go_import : "github.com/skip-mev/slinky/x/oracle" - }; - - // Authority defines the custom module authority. If not set, defaults to the - // governance module. - string authority = 1; -} diff --git a/proto/vendored/slinky/oracle/v1/genesis.proto b/proto/vendored/slinky/oracle/v1/genesis.proto index f3fd1f187e..468ebebf29 100644 --- a/proto/vendored/slinky/oracle/v1/genesis.proto +++ b/proto/vendored/slinky/oracle/v1/genesis.proto @@ -1,18 +1,16 @@ syntax = "proto3"; package slinky.oracle.v1; -option go_package = "github.com/skip-mev/slinky/x/oracle/types"; - -import "google/protobuf/timestamp.proto"; import "cosmos_sdk/cosmos_proto/cosmos.proto"; +import "google/protobuf/timestamp.proto"; import "slinky/types/v1/currency_pair.proto"; +option go_package = "github.com/skip-mev/slinky/x/oracle/types"; + // QuotePrice is the representation of the aggregated prices for a CurrencyPair, // where price represents the price of Base in terms of Quote message QuotePrice { - string price = 1 [ - (cosmos_proto.scalar) = "cosmos.Int" - ]; + string price = 1 [(cosmos_proto.scalar) = "cosmos.Int"]; // BlockTimestamp tracks the block height associated with this price update. // We include block timestamp alongside the price to ensure that smart diff --git a/proto/vendored/slinky/oracle/v1/query.proto b/proto/vendored/slinky/oracle/v1/query.proto index 993e7dc01e..412585b430 100644 --- a/proto/vendored/slinky/oracle/v1/query.proto +++ b/proto/vendored/slinky/oracle/v1/query.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package slinky.oracle.v1; + import "google/api/annotations.proto"; import "slinky/oracle/v1/genesis.proto"; import "slinky/types/v1/currency_pair.proto"; @@ -9,16 +10,15 @@ option go_package = "github.com/skip-mev/slinky/x/oracle/types"; // Query is the query service for the x/oracle module. service Query { // Get all the currency pairs the x/oracle module is tracking price-data for. - rpc GetAllCurrencyPairs(GetAllCurrencyPairsRequest) - returns (GetAllCurrencyPairsResponse) { + rpc GetAllCurrencyPairs(GetAllCurrencyPairsRequest) returns (GetAllCurrencyPairsResponse) { option (google.api.http).get = "/slinky/oracle/v1/get_all_tickers"; - }; + } // Given a CurrencyPair (or its identifier) return the latest QuotePrice for // that CurrencyPair. rpc GetPrice(GetPriceRequest) returns (GetPriceResponse) { option (google.api.http).get = "/slinky/oracle/v1/get_price"; - }; + } rpc GetPrices(GetPricesRequest) returns (GetPricesResponse) { option (google.api.http).get = "/slinky/oracle/v1/get_prices"; @@ -27,10 +27,8 @@ service Query { // Get the mapping of currency pair ID -> currency pair. This is useful for // indexers that have access to the ID of a currency pair, but no way to get // the underlying currency pair from it. - rpc GetCurrencyPairMapping(GetCurrencyPairMappingRequest) - returns (GetCurrencyPairMappingResponse) { - option (google.api.http).get = - "/slinky/oracle/v1/get_currency_pair_mapping"; + rpc GetCurrencyPairMapping(GetCurrencyPairMappingRequest) returns (GetCurrencyPairMappingResponse) { + option (google.api.http).get = "/slinky/oracle/v1/get_currency_pair_mapping"; } } @@ -67,7 +65,9 @@ message GetPriceResponse { // GetPricesRequest takes an identifier for the CurrencyPair // in the format base/quote. -message GetPricesRequest { repeated string currency_pair_ids = 1; } +message GetPricesRequest { + repeated string currency_pair_ids = 1; +} // GetPricesResponse is the response from the GetPrices grpc method exposed from // the x/oracle query service. diff --git a/proto/vendored/slinky/oracle/v1/tx.proto b/proto/vendored/slinky/oracle/v1/tx.proto index 8ca3295edc..8a4c937d87 100644 --- a/proto/vendored/slinky/oracle/v1/tx.proto +++ b/proto/vendored/slinky/oracle/v1/tx.proto @@ -1,9 +1,9 @@ syntax = "proto3"; package slinky.oracle.v1; -import "slinky/oracle/v1/genesis.proto"; -import "cosmos_sdk/cosmos_proto/cosmos.proto"; import "cosmos_sdk/cosmos/msg/v1/msg.proto"; +import "cosmos_sdk/cosmos_proto/cosmos.proto"; +import "slinky/oracle/v1/genesis.proto"; import "slinky/types/v1/currency_pair.proto"; option go_package = "github.com/skip-mev/slinky/x/oracle/types"; @@ -15,14 +15,12 @@ service Msg { // AddCurrencyPairs will be used only by governance to update the set of // available CurrencyPairs. Given a set of CurrencyPair objects, update // the available currency pairs in the module . - rpc AddCurrencyPairs(MsgAddCurrencyPairs) - returns (MsgAddCurrencyPairsResponse); + rpc AddCurrencyPairs(MsgAddCurrencyPairs) returns (MsgAddCurrencyPairsResponse); // RemoveCurrencyPairs will be used explicitly by governance to remove the // given set of currency-pairs from the module's state. Thus these // CurrencyPairs will no longer have price-data available from this module. - rpc RemoveCurrencyPairs(MsgRemoveCurrencyPairs) - returns (MsgRemoveCurrencyPairsResponse); + rpc RemoveCurrencyPairs(MsgRemoveCurrencyPairs) returns (MsgRemoveCurrencyPairsResponse); } // Given an authority + a set of CurrencyPairs, the x/oracle module will @@ -34,7 +32,7 @@ message MsgAddCurrencyPairs { // authority is the address of the account that is authorized to update the // x/oracle's CurrencyPairs - string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // set of CurrencyPairs to be added to the module (+ prices if they are to be // set) repeated slinky.types.v1.CurrencyPair currency_pairs = 2; @@ -52,7 +50,7 @@ message MsgRemoveCurrencyPairs { // authority is the address of the account that is authorized to update the // x/oracle's CurrencyPairs - string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // currency_pair_ids are the stringified representation of a currency-pairs // (base/quote) to be removed from the module's state diff --git a/proto/vendored/slinky/service/v1/oracle.proto b/proto/vendored/slinky/service/v1/oracle.proto index 5d9fdc5d57..55f7d25eba 100644 --- a/proto/vendored/slinky/service/v1/oracle.proto +++ b/proto/vendored/slinky/service/v1/oracle.proto @@ -1,9 +1,9 @@ syntax = "proto3"; package slinky.service.v1; +import "cosmos_sdk/cosmos_proto/cosmos.proto"; import "google/api/annotations.proto"; import "google/protobuf/timestamp.proto"; -import "cosmos_sdk/cosmos_proto/cosmos.proto"; import "slinky/marketmap/v1/market.proto"; option go_package = "github.com/skip-mev/slinky/service/servers/oracle/types"; @@ -13,7 +13,7 @@ service Oracle { // Prices defines a method for fetching the latest prices. rpc Prices(QueryPricesRequest) returns (QueryPricesResponse) { option (google.api.http).get = "/slinky/oracle/v1/prices"; - }; + } // MarketMap defines a method for fetching the latest market map // configuration. From 4635f404ebc2ee4f37fb7176a04aa661fb719245 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 20:59:47 -0400 Subject: [PATCH 07/89] begin oracle component --- crates/astria-sequencer/src/app/mod.rs | 2 +- .../src/app/vote_extension.rs | 30 +++- crates/astria-sequencer/src/lib.rs | 1 + .../astria-sequencer/src/oracle/component.rs | 1 + .../src/oracle/currency_pair_strategy.rs | 61 ++++++++ crates/astria-sequencer/src/oracle/mod.rs | 3 + .../astria-sequencer/src/oracle/state_ext.rs | 145 ++++++++++++++++++ 7 files changed, 235 insertions(+), 8 deletions(-) create mode 100644 crates/astria-sequencer/src/oracle/component.rs create mode 100644 crates/astria-sequencer/src/oracle/currency_pair_strategy.rs create mode 100644 crates/astria-sequencer/src/oracle/mod.rs create mode 100644 crates/astria-sequencer/src/oracle/state_ext.rs diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 5619ba5174..9685a5f804 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -753,7 +753,7 @@ impl App { vote_extension: vec![].into(), }); }; - handler.extend_vote().await + handler.extend_vote(&self.state).await } pub(crate) async fn verify_vote_extension( diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index e6ff2f3360..3a1fd31a9f 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -14,6 +14,11 @@ use prost::Message as _; use tendermint::abci; use tonic::transport::Channel; +use crate::{ + oracle::currency_pair_strategy::DefaultCurrencyPairStrategy, + state_ext::StateReadExt, +}; + pub(crate) struct Handler { // gRPC client for the slinky oracle sidecar. oracle_client: OracleClient, @@ -26,7 +31,10 @@ impl Handler { } } - pub(crate) async fn extend_vote(&mut self) -> anyhow::Result { + pub(crate) async fn extend_vote( + &mut self, + state: &S, + ) -> anyhow::Result { // TODO: use oracle client timeout let prices = match self.oracle_client.prices(QueryPricesRequest {}).await { Ok(prices) => prices.into_inner(), @@ -42,7 +50,8 @@ impl Handler { }; let oracle_vote_extension = self - .transform_oracle_service_prices(prices) + .transform_oracle_service_prices(state, prices) + .await .context("failed to transform oracle service prices")?; Ok(abci::response::ExtendVote { // TODO: what codec does skip use for this? does it matter here? @@ -61,19 +70,26 @@ impl Handler { } // see https://github.com/skip-mev/slinky/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 - fn transform_oracle_service_prices( + async fn transform_oracle_service_prices( &self, + state: &S, prices: QueryPricesResponse, ) -> anyhow::Result { + let mut strategy_prices = HashMap::new(); for (currency_pair_id, price_string) in prices.prices { - let _currency_pair = currency_pair_from_string(¤cy_pair_id)?; - let _price = price_string.parse::()?; + let currency_pair = currency_pair_from_string(¤cy_pair_id)?; + let price = price_string.parse::()?; - // TODO: oracle module state + let id = DefaultCurrencyPairStrategy::id(state, ¤cy_pair) + .await + .context("failed to get id for currency pair")?; + let encoded_price = + DefaultCurrencyPairStrategy::get_encoded_price(state, ¤cy_pair, price).await; + strategy_prices.insert(id, encoded_price); } Ok(OracleVoteExtension { - prices: HashMap::default(), + prices: strategy_prices, }) } } diff --git a/crates/astria-sequencer/src/lib.rs b/crates/astria-sequencer/src/lib.rs index 2b542b7771..405e80d98f 100644 --- a/crates/astria-sequencer/src/lib.rs +++ b/crates/astria-sequencer/src/lib.rs @@ -14,6 +14,7 @@ pub(crate) mod grpc; pub(crate) mod ibc; mod mempool; pub(crate) mod metrics; +pub(crate) mod oracle; pub(crate) mod proposal; pub(crate) mod sequence; mod sequencer; diff --git a/crates/astria-sequencer/src/oracle/component.rs b/crates/astria-sequencer/src/oracle/component.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/astria-sequencer/src/oracle/component.rs @@ -0,0 +1 @@ + diff --git a/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs new file mode 100644 index 0000000000..a3b89cf701 --- /dev/null +++ b/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs @@ -0,0 +1,61 @@ +use anyhow::Context as _; +use astria_core::generated::slinky::types::v1::CurrencyPair; + +use crate::oracle::state_ext::StateReadExt; + +/// see https://github.com/skip-mev/slinky/blob/793b2e874d6e720bd288e82e782502e41cf06f8c/abci/strategies/currencypair/default.go +pub(crate) struct DefaultCurrencyPairStrategy; + +impl DefaultCurrencyPairStrategy { + pub(crate) async fn id( + state: &S, + currency_pair: &CurrencyPair, + ) -> anyhow::Result { + state.get_currency_pair_id(currency_pair).await + } + + pub(crate) async fn from_id( + state: &S, + id: u64, + ) -> anyhow::Result> { + state.get_currency_pair(id).await + } + + pub(crate) async fn get_encoded_price( + state: &S, + _: &CurrencyPair, + price: u128, + ) -> Vec { + price.to_be_bytes().to_vec() + } + + pub(crate) async fn get_decoded_price( + state: &S, + _: &CurrencyPair, + encoded_price: &[u8], + ) -> anyhow::Result { + let mut bytes = [0; 16]; + bytes.copy_from_slice(encoded_price); + Ok(u128::from_be_bytes(bytes)) + } + + pub(crate) async fn get_max_num_currency_pairs( + state: &S, + is_proposal_phase: bool, + ) -> anyhow::Result { + let current = state + .get_num_currency_pairs() + .await + .context("failed to get number of currency pairs")?; + + if is_proposal_phase { + let removed = state + .get_num_removed_currency_pairs() + .await + .context("failed to get number of removed currency pairs")?; + Ok(current + removed) + } else { + Ok(current) + } + } +} diff --git a/crates/astria-sequencer/src/oracle/mod.rs b/crates/astria-sequencer/src/oracle/mod.rs new file mode 100644 index 0000000000..bdd1fd2402 --- /dev/null +++ b/crates/astria-sequencer/src/oracle/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod component; +pub(crate) mod currency_pair_strategy; +pub(crate) mod state_ext; diff --git a/crates/astria-sequencer/src/oracle/state_ext.rs b/crates/astria-sequencer/src/oracle/state_ext.rs new file mode 100644 index 0000000000..cd900d0ae0 --- /dev/null +++ b/crates/astria-sequencer/src/oracle/state_ext.rs @@ -0,0 +1,145 @@ +use anyhow::{ + Context, + Result, +}; +use astria_core::generated::slinky::{ + marketmap::v1::{ + MarketMap, + Params, + }, + types::v1::CurrencyPair, +}; +use async_trait::async_trait; +use borsh::{ + BorshDeserialize, + BorshSerialize, +}; +use cnidarium::{ + StateRead, + StateWrite, +}; +use tracing::instrument; + +const CURRENCY_PAIR_TO_ID_PREFIX: &str = "oraclecpid"; +const ID_TO_CURRENCY_PAIR_PREFIX: &str = "oracleidcp"; + +// TODO: should these values be in nonverifiable storage? +const NUM_CURRENCY_PAIRS_KEY: &str = "oraclenumcps"; +const NUM_REMOVED_CURRENCY_PAIRS_KEY: &str = "oraclenumremovedcps"; + +fn currency_pair_to_id_storage_key(currency_pair: &CurrencyPair) -> String { + format!( + "{}/{}/{}", + CURRENCY_PAIR_TO_ID_PREFIX, currency_pair.base, currency_pair.quote + ) +} + +fn id_to_currency_pair_storage_key(id: u64) -> String { + format!("{}/{}", ID_TO_CURRENCY_PAIR_PREFIX, id) +} + +/// Newtype wrapper to read and write a u64 from rocksdb. +#[derive(BorshSerialize, BorshDeserialize, Debug)] +struct Id(u64); + +/// Newtype wrapper to read and write a u64 from rocksdb. +#[derive(BorshSerialize, BorshDeserialize, Debug)] +struct Count(u64); + +#[async_trait] +pub(crate) trait StateReadExt: StateRead { + #[instrument(skip(self))] + async fn get_currency_pair_id(&self, currency_pair: &CurrencyPair) -> Result { + let Some(bytes) = self + .get_raw(¤cy_pair_to_id_storage_key(currency_pair)) + .await + .context("failed reading currency pair id from state")? + else { + return Ok(0); + }; + let Id(id) = Id::try_from_slice(&bytes).context("invalid currency pair id bytes")?; + Ok(id) + } + + #[instrument(skip(self))] + async fn get_currency_pair(&self, id: u64) -> Result> { + let bytes = self + .get_raw(&id_to_currency_pair_storage_key(id)) + .await + .context("failed to get currency pair from state")?; + match bytes { + Some(bytes) => { + let currency_pair = serde_json::from_slice(&bytes) + .context("failed to deserialize currency pair")?; + Ok(Some(currency_pair)) + } + None => Ok(None), + } + } + + #[instrument(skip(self))] + async fn get_num_currency_pairs(&self) -> Result { + let Some(bytes) = self + .get_raw(NUM_CURRENCY_PAIRS_KEY) + .await + .context("failed reading number of currency pairs from state")? + else { + return Ok(0); + }; + let Count(num_currency_pairs) = + Count::try_from_slice(&bytes).context("invalid number of currency pairs bytes")?; + Ok(num_currency_pairs) + } + + #[instrument(skip(self))] + async fn get_num_removed_currency_pairs(&self) -> Result { + let Some(bytes) = self + .get_raw(NUM_REMOVED_CURRENCY_PAIRS_KEY) + .await + .context("failed reading number of removed currency pairs from state")? + else { + return Ok(0); + }; + let Count(num_removed_currency_pairs) = Count::try_from_slice(&bytes) + .context("invalid number of removed currency pairs bytes")?; + Ok(num_removed_currency_pairs) + } +} + +impl StateReadExt for T {} + +#[async_trait] +pub(crate) trait StateWriteExt: StateWrite { + #[instrument(skip(self))] + fn put_currency_pair_id(&mut self, currency_pair: &CurrencyPair, id: u64) -> Result<()> { + let bytes = borsh::to_vec(&Id(id)).context("failed to serialize currency pair id")?; + self.put_raw(currency_pair_to_id_storage_key(currency_pair), bytes); + Ok(()) + } + + #[instrument(skip(self))] + fn put_currency_pair(&mut self, id: u64, currency_pair: CurrencyPair) -> Result<()> { + let bytes = + serde_json::to_vec(¤cy_pair).context("failed to serialize currency pair")?; + self.put_raw(id_to_currency_pair_storage_key(id), bytes); + Ok(()) + } + + #[instrument(skip(self))] + fn put_num_currency_pairs(&mut self, num_currency_pairs: u64) -> Result<()> { + let bytes = borsh::to_vec(&Count(num_currency_pairs)) + .context("failed to serialize number of currency pairs")?; + self.put_raw(NUM_CURRENCY_PAIRS_KEY.to_string(), bytes); + Ok(()) + } + + #[instrument(skip(self))] + fn put_num_removed_currency_pairs(&mut self, num_removed_currency_pairs: u64) -> Result<()> { + let bytes = borsh::to_vec(&Count(num_removed_currency_pairs)) + .context("failed to serialize number of removed currency pairs")?; + self.put_raw(NUM_REMOVED_CURRENCY_PAIRS_KEY.to_string(), bytes); + Ok(()) + } +} + +impl StateWriteExt for T {} From 70df249a9651644d631ddfc8e68ac8d02475057a Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 21:28:14 -0400 Subject: [PATCH 08/89] finish extend vote logic --- crates/astria-sequencer/src/app/mod.rs | 7 +- crates/astria-sequencer/src/app/test_utils.rs | 8 +- .../src/app/tests_breaking_changes.rs | 8 +- .../src/app/tests_execute_transaction.rs | 8 +- .../src/app/vote_extension.rs | 35 +++++++-- crates/astria-sequencer/src/genesis.rs | 33 +++++++-- .../astria-sequencer/src/oracle/component.rs | 74 +++++++++++++++++++ .../src/oracle/currency_pair_strategy.rs | 4 +- .../astria-sequencer/src/oracle/state_ext.rs | 67 ++++++++++++++++- .../astria-sequencer/src/service/consensus.rs | 8 +- .../astria-sequencer/src/slinky/component.rs | 6 +- 11 files changed, 226 insertions(+), 32 deletions(-) diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 9685a5f804..68849d01fb 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -761,11 +761,12 @@ impl App { vote_extension: abci::request::VerifyVoteExtension, ) -> anyhow::Result { let Some(handler) = self.vote_extension_handler.as_mut() else { - // TODO: should we still verify if our own oracle isn't set? - // i think so? + // TODO: we should still verify if our own oracle isn't set return Ok(abci::response::VerifyVoteExtension::Accept); }; - handler.verify_vote_extension(vote_extension).await + handler + .verify_vote_extension(&self.state, vote_extension, false) + .await } /// Executes the given block, but does not write it to disk. diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 51a1b4927f..3fa9b98978 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -1,6 +1,9 @@ use astria_core::{ crypto::SigningKey, - generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, + generated::slinky::{ + marketmap::v1::GenesisState as MarketMapGenesisState, + oracle::v1::GenesisState as OracleGenesisState, + }, primitive::v1::{ Address, RollupId, @@ -108,7 +111,8 @@ pub(crate) fn unchecked_genesis_state() -> UncheckedGenesisState { ibc_params: IBCParameters::default(), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: default_fees(), - slinky: SlinkyGenesisState::default(), + market_map: MarketMapGenesisState::default(), + oracle: OracleGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 1190c3a138..5690d0672a 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -15,7 +15,10 @@ use std::{ }; use astria_core::{ - generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, + generated::slinky::{ + marketmap::v1::GenesisState as MarketMapGenesisState, + oracle::v1::GenesisState as OracleGenesisState, + }, primitive::v1::RollupId, protocol::transaction::v1alpha1::{ action::{ @@ -80,7 +83,8 @@ fn unchecked_genesis_state() -> UncheckedGenesisState { ibc_params: IBCParameters::default(), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: default_fees(), - slinky: SlinkyGenesisState::default(), + market_map: MarketMapGenesisState::default(), + oracle: OracleGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 128a35c682..f4e25ba56f 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -2,7 +2,10 @@ use std::sync::Arc; use astria_core::{ crypto::SigningKey, - generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, + generated::slinky::{ + marketmap::v1::GenesisState as MarketMapGenesisState, + oracle::v1::GenesisState as OracleGenesisState, + }, primitive::v1::{ asset, RollupId, @@ -69,7 +72,8 @@ fn unchecked_genesis_state() -> UncheckedGenesisState { ibc_params: IBCParameters::default(), allowed_fee_assets: vec![default_native_asset()], fees: default_fees(), - slinky: SlinkyGenesisState::default(), + market_map: MarketMapGenesisState::default(), + oracle: OracleGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 3a1fd31a9f..2776733dcb 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; -use anyhow::Context as _; +use anyhow::{ + ensure, + Context as _, +}; use astria_core::generated::slinky::{ abci::v1::OracleVoteExtension, service::v1::{ @@ -19,6 +22,9 @@ use crate::{ state_ext::StateReadExt, }; +// https://github.com/skip-mev/slinky/blob/793b2e874d6e720bd288e82e782502e41cf06f8c/abci/types/constants.go#L6 +const MAXIMUM_PRICE_BYTE_LEN: usize = 33; + pub(crate) struct Handler { // gRPC client for the slinky oracle sidecar. oracle_client: OracleClient, @@ -53,19 +59,38 @@ impl Handler { .transform_oracle_service_prices(state, prices) .await .context("failed to transform oracle service prices")?; + Ok(abci::response::ExtendVote { - // TODO: what codec does skip use for this? does it matter here? - // don't think so but good to check vote_extension: oracle_vote_extension.encode_to_vec().into(), }) } - pub(crate) async fn verify_vote_extension( + pub(crate) async fn verify_vote_extension( &mut self, + state: &S, vote_extension: abci::request::VerifyVoteExtension, + is_proposal_phase: bool, ) -> anyhow::Result { // TODO: verify the vote extension based on slinky rules - let _oracle_vote_extension = OracleVoteExtension::decode(vote_extension.vote_extension)?; + let oracle_vote_extension = OracleVoteExtension::decode(vote_extension.vote_extension)?; + + let max_num_currency_pairs = + DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, is_proposal_phase) + .await + .context("failed to get max number of currency pairs")?; + + ensure!( + oracle_vote_extension.prices.len() as u64 <= max_num_currency_pairs, + "number of oracle vote extension prices exceeds max expected number of currency pairs" + ); + + for prices in oracle_vote_extension.prices.values() { + ensure!( + prices.len() <= MAXIMUM_PRICE_BYTE_LEN, + "encoded price length exceeded {MAXIMUM_PRICE_BYTE_LEN}" + ); + } + Ok(abci::response::VerifyVoteExtension::Accept) } diff --git a/crates/astria-sequencer/src/genesis.rs b/crates/astria-sequencer/src/genesis.rs index 08f24d777e..3c1a21c483 100644 --- a/crates/astria-sequencer/src/genesis.rs +++ b/crates/astria-sequencer/src/genesis.rs @@ -1,5 +1,8 @@ use astria_core::{ - generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, + generated::slinky::{ + marketmap::v1::GenesisState as MarketMapGenesisState, + oracle::v1::GenesisState as OracleGenesisState, + }, primitive::v1::{ asset, Address, @@ -30,7 +33,8 @@ pub(crate) struct GenesisState { pub(crate) ibc_params: IBCParameters, pub(crate) allowed_fee_assets: Vec, pub(crate) fees: Fees, - pub(crate) slinky: SlinkyGenesisState, + pub(crate) market_map: MarketMapGenesisState, + pub(crate) oracle: OracleGenesisState, } #[derive(Debug, thiserror::Error)] @@ -62,7 +66,8 @@ impl TryFrom for GenesisState { ibc_params, allowed_fee_assets, fees, - slinky, + market_map, + oracle, } = value; Ok(Self { @@ -75,7 +80,8 @@ impl TryFrom for GenesisState { ibc_params, allowed_fee_assets, fees, - slinky, + market_map, + oracle, }) } } @@ -92,7 +98,8 @@ pub(crate) struct UncheckedGenesisState { pub(crate) ibc_params: IBCParameters, pub(crate) allowed_fee_assets: Vec, pub(crate) fees: Fees, - pub(crate) slinky: SlinkyGenesisState, + pub(crate) market_map: MarketMapGenesisState, + pub(crate) oracle: OracleGenesisState, } impl UncheckedGenesisState { @@ -130,6 +137,13 @@ impl UncheckedGenesisState { for (i, address) in self.ibc_relayer_addresses.iter().enumerate() { self.ensure_address_has_base_prefix(address, &format!(".ibc_relayer_addresses[{i}]"))?; } + // TODO: make native types for these + // if let Some(params) = self.market_map.params { + // for (i, address) in params.market_authorities.iter().enumerate() { + // self.ensure_address_has_base_prefix(address, + // &format!(".market_map.params.market_authorities[{i}]"))?; } + // self.ensure_address_has_base_prefix(params.admin, + // &format!(".market_map.params.admin"))?; } Ok(()) } } @@ -146,7 +160,8 @@ impl From for UncheckedGenesisState { ibc_params, allowed_fee_assets, fees, - slinky, + market_map, + oracle, } = value; Self { address_prefixes, @@ -158,7 +173,8 @@ impl From for UncheckedGenesisState { ibc_params, allowed_fee_assets, fees, - slinky, + market_map, + oracle, } } } @@ -263,7 +279,8 @@ mod test { bridge_sudo_change_fee: 24, ics20_withdrawal_base_fee: 24, }, - slinky: SlinkyGenesisState::default(), + market_map: MarketMapGenesisState::default(), + oracle: OracleGenesisState::default(), } } diff --git a/crates/astria-sequencer/src/oracle/component.rs b/crates/astria-sequencer/src/oracle/component.rs index 8b13789179..699e8389eb 100644 --- a/crates/astria-sequencer/src/oracle/component.rs +++ b/crates/astria-sequencer/src/oracle/component.rs @@ -1 +1,75 @@ +use std::sync::Arc; +use anyhow::{ + Context, + Result, +}; +use astria_core::generated::slinky::oracle::v1::CurrencyPairState; +use tendermint::abci::request::{ + BeginBlock, + EndBlock, +}; +use tracing::instrument; + +use super::state_ext::StateWriteExt; +use crate::{ + component::Component, + genesis::GenesisState, +}; + +// TODO do we want to put all slinky stuff in one component? +#[derive(Default)] +pub(crate) struct OracleComponent; + +#[async_trait::async_trait] +impl Component for OracleComponent { + type AppState = GenesisState; + + #[instrument(name = "OracleComponent::init_chain", skip(state))] + async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { + for currency_pair in &app_state.oracle.currency_pair_genesis { + // TODO: make actual native types for this + let currency_pair_state = CurrencyPairState { + id: currency_pair.id, + nonce: currency_pair.nonce, + price: currency_pair.currency_pair_price.clone(), + }; + state + .put_currency_pair_state( + ¤cy_pair + .currency_pair + .clone() + .expect("currency pair must be set"), + currency_pair_state, + ) + .context("failed to put currency pair")?; + } + + state + .put_next_currency_pair_id(app_state.oracle.next_id) + .context("failed to put next currency pair id")?; + state + .put_num_currency_pairs(app_state.oracle.currency_pair_genesis.len() as u64) + .context("failed to put number of currency pairs")?; + state + .put_num_removed_currency_pairs(0) + .context("failed to put number of removed currency pairs")?; + Ok(()) + } + + #[instrument(name = "OracleComponent::begin_block", skip(_state))] + async fn begin_block( + _state: &mut Arc, + _begin_block: &BeginBlock, + ) -> Result<()> { + Ok(()) + } + + #[instrument(name = "OracleComponent::end_block", skip(_state))] + async fn end_block( + _state: &mut Arc, + _end_block: &EndBlock, + ) -> Result<()> { + Ok(()) + } +} diff --git a/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs index a3b89cf701..503a760fcc 100644 --- a/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs +++ b/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs @@ -22,7 +22,7 @@ impl DefaultCurrencyPairStrategy { } pub(crate) async fn get_encoded_price( - state: &S, + _state: &S, _: &CurrencyPair, price: u128, ) -> Vec { @@ -30,7 +30,7 @@ impl DefaultCurrencyPairStrategy { } pub(crate) async fn get_decoded_price( - state: &S, + _state: &S, _: &CurrencyPair, encoded_price: &[u8], ) -> anyhow::Result { diff --git a/crates/astria-sequencer/src/oracle/state_ext.rs b/crates/astria-sequencer/src/oracle/state_ext.rs index cd900d0ae0..ebaa576bcc 100644 --- a/crates/astria-sequencer/src/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/oracle/state_ext.rs @@ -3,10 +3,7 @@ use anyhow::{ Result, }; use astria_core::generated::slinky::{ - marketmap::v1::{ - MarketMap, - Params, - }, + oracle::v1::CurrencyPairState, types::v1::CurrencyPair, }; use async_trait::async_trait; @@ -22,10 +19,12 @@ use tracing::instrument; const CURRENCY_PAIR_TO_ID_PREFIX: &str = "oraclecpid"; const ID_TO_CURRENCY_PAIR_PREFIX: &str = "oracleidcp"; +const CURRENCY_PAIR_STATE_PREFIX: &str = "oraclecpstate"; // TODO: should these values be in nonverifiable storage? const NUM_CURRENCY_PAIRS_KEY: &str = "oraclenumcps"; const NUM_REMOVED_CURRENCY_PAIRS_KEY: &str = "oraclenumremovedcps"; +const NEXT_CURRENCY_PAIR_ID_KEY: &str = "oraclenextcpid"; fn currency_pair_to_id_storage_key(currency_pair: &CurrencyPair) -> String { format!( @@ -38,6 +37,13 @@ fn id_to_currency_pair_storage_key(id: u64) -> String { format!("{}/{}", ID_TO_CURRENCY_PAIR_PREFIX, id) } +fn currency_pair_state_storage_key(currency_pair: &CurrencyPair) -> String { + format!( + "{}/{}/{}", + CURRENCY_PAIR_STATE_PREFIX, currency_pair.base, currency_pair.quote + ) +} + /// Newtype wrapper to read and write a u64 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Id(u64); @@ -104,6 +110,39 @@ pub(crate) trait StateReadExt: StateRead { .context("invalid number of removed currency pairs bytes")?; Ok(num_removed_currency_pairs) } + + #[instrument(skip(self))] + async fn get_currency_pair_state( + &self, + currency_pair: &CurrencyPair, + ) -> Result> { + let bytes = self + .get_raw(¤cy_pair_state_storage_key(currency_pair)) + .await + .context("failed to get currency pair state from state")?; + match bytes { + Some(bytes) => { + let currency_pair_state = serde_json::from_slice(&bytes) + .context("failed to deserialize currency pair state")?; + Ok(Some(currency_pair_state)) + } + None => Ok(None), + } + } + + #[instrument(skip(self))] + async fn get_next_currency_pair_id(&self) -> Result { + let Some(bytes) = self + .get_raw(NEXT_CURRENCY_PAIR_ID_KEY) + .await + .context("failed reading next currency pair id from state")? + else { + return Ok(0); + }; + let Id(next_currency_pair_id) = + Id::try_from_slice(&bytes).context("invalid next currency pair id bytes")?; + Ok(next_currency_pair_id) + } } impl StateReadExt for T {} @@ -140,6 +179,26 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_raw(NUM_REMOVED_CURRENCY_PAIRS_KEY.to_string(), bytes); Ok(()) } + + #[instrument(skip(self))] + fn put_currency_pair_state( + &mut self, + currency_pair: &CurrencyPair, + currency_pair_state: CurrencyPairState, + ) -> Result<()> { + let bytes = serde_json::to_vec(¤cy_pair_state) + .context("failed to serialize currency pair state")?; + self.put_raw(currency_pair_state_storage_key(currency_pair), bytes); + Ok(()) + } + + #[instrument(skip(self))] + fn put_next_currency_pair_id(&mut self, next_currency_pair_id: u64) -> Result<()> { + let bytes = borsh::to_vec(&Id(next_currency_pair_id)) + .context("failed to serialize next currency pair id")?; + self.put_raw(NEXT_CURRENCY_PAIR_ID_KEY.to_string(), bytes); + Ok(()) + } } impl StateWriteExt for T {} diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 53043646f9..cf3094a0f5 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -261,7 +261,10 @@ mod test { SigningKey, VerificationKey, }, - generated::slinky::marketmap::v1::GenesisState as SlinkyGenesisState, + generated::slinky::{ + marketmap::v1::GenesisState as MarketMapGenesisState, + oracle::v1::GenesisState as OracleGenesisState, + }, primitive::v1::RollupId, protocol::transaction::v1alpha1::{ action::SequenceAction, @@ -518,7 +521,8 @@ mod test { ibc_params: penumbra_ibc::params::IBCParameters::default(), allowed_fee_assets: vec!["nria".parse().unwrap()], fees: default_fees(), - slinky: SlinkyGenesisState::default(), + market_map: MarketMapGenesisState::default(), + oracle: OracleGenesisState::default(), } .try_into() .unwrap(); diff --git a/crates/astria-sequencer/src/slinky/component.rs b/crates/astria-sequencer/src/slinky/component.rs index 88da058446..a4c83460e7 100644 --- a/crates/astria-sequencer/src/slinky/component.rs +++ b/crates/astria-sequencer/src/slinky/component.rs @@ -16,6 +16,8 @@ use crate::{ genesis::GenesisState, }; +// TODO rename to MarketMapComponent +// or do we want to put all slinky stuff in one component? #[derive(Default)] pub(crate) struct SlinkyComponent; @@ -25,13 +27,13 @@ impl Component for SlinkyComponent { #[instrument(name = "SlinkyComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { - if let Some(market_map) = &app_state.slinky.market_map { + if let Some(market_map) = &app_state.market_map.market_map { state .put_market_map(market_map.clone()) .context("failed to put market map")?; } - if let Some(params) = &app_state.slinky.params { + if let Some(params) = &app_state.market_map.params { state .put_params(params.clone()) .context("failed to put params")?; From f3045b3828887be5b034abdc9f3d411df4dbbcdb Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 2 Jul 2024 22:04:53 -0400 Subject: [PATCH 09/89] finish vote extension validation in verify_vote_extension --- crates/astria-sequencer/src/app/mod.rs | 21 +++--------- crates/astria-sequencer/src/app/test_utils.rs | 14 ++++++-- .../src/app/vote_extension.rs | 33 +++++++++++++++---- crates/astria-sequencer/src/sequencer.rs | 15 ++++++--- .../astria-sequencer/src/service/consensus.rs | 2 +- ...sis__test__genesis_state_is_unchanged.snap | 3 +- 6 files changed, 56 insertions(+), 32 deletions(-) diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 68849d01fb..49d4cd2251 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -169,8 +169,8 @@ pub(crate) struct App { #[allow(clippy::struct_field_names)] app_hash: AppHash, - // should be set if this is a validator node. - vote_extension_handler: Option, + // used to create and verify vote extensions, if this is a validator node. + vote_extension_handler: vote_extension::Handler, metrics: &'static Metrics, } @@ -179,7 +179,7 @@ impl App { pub(crate) async fn new( snapshot: Snapshot, mempool: Mempool, - vote_extension_handler: Option, + vote_extension_handler: vote_extension::Handler, metrics: &'static Metrics, ) -> anyhow::Result { debug!("initializing App instance"); @@ -746,25 +746,14 @@ impl App { &mut self, _extend_vote: abci::request::ExtendVote, ) -> anyhow::Result { - let Some(handler) = self.vote_extension_handler.as_mut() else { - // we allow validators to *not* use the oracle sidecar currently - // however, if >1/3 of validators are not using the oracle, the prices will not update. - return Ok(abci::response::ExtendVote { - vote_extension: vec![].into(), - }); - }; - handler.extend_vote(&self.state).await + self.vote_extension_handler.extend_vote(&self.state).await } pub(crate) async fn verify_vote_extension( &mut self, vote_extension: abci::request::VerifyVoteExtension, ) -> anyhow::Result { - let Some(handler) = self.vote_extension_handler.as_mut() else { - // TODO: we should still verify if our own oracle isn't set - return Ok(abci::response::VerifyVoteExtension::Accept); - }; - handler + self.vote_extension_handler .verify_vote_extension(&self.state, vote_extension, false) .await } diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 3fa9b98978..dfb441a028 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -20,7 +20,10 @@ use cnidarium::Storage; use penumbra_ibc::params::IBCParameters; use crate::{ - app::App, + app::{ + vote_extension, + App, + }, genesis::{ self, Account, @@ -130,7 +133,14 @@ pub(crate) async fn initialize_app_with_storage( let snapshot = storage.latest_snapshot(); let mempool = Mempool::new(); let metrics = Box::leak(Box::new(Metrics::new())); - let mut app = App::new(snapshot, mempool, None, metrics).await.unwrap(); + let mut app = App::new( + snapshot, + mempool, + vote_extension::Handler::new(None, 100), + metrics, + ) + .await + .unwrap(); let genesis_state = genesis_state.unwrap_or_else(self::genesis_state); diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 2776733dcb..8bccd309dc 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -27,13 +27,15 @@ const MAXIMUM_PRICE_BYTE_LEN: usize = 33; pub(crate) struct Handler { // gRPC client for the slinky oracle sidecar. - oracle_client: OracleClient, + oracle_client: Option>, + oracle_client_timeout: tokio::time::Duration, } impl Handler { - pub(crate) fn new(oracle_client: OracleClient) -> Self { + pub(crate) fn new(oracle_client: Option>, oracle_client_timeout: u64) -> Self { Self { oracle_client, + oracle_client_timeout: tokio::time::Duration::from_millis(oracle_client_timeout), } } @@ -41,10 +43,19 @@ impl Handler { &mut self, state: &S, ) -> anyhow::Result { - // TODO: use oracle client timeout - let prices = match self.oracle_client.prices(QueryPricesRequest {}).await { - Ok(prices) => prices.into_inner(), - Err(e) => { + let Some(oracle_client) = self.oracle_client.as_mut() else { + // we allow validators to *not* use the oracle sidecar currently + // however, if >1/3 of validators are not using the oracle, the prices will not update. + return Ok(abci::response::ExtendVote { + vote_extension: vec![].into(), + }); + }; + + // if we fail to get prices within the timeout duration, we will return an empty vote extension + // to ensure liveness. + let prices = match tokio::time::timeout(self.oracle_client_timeout, oracle_client.prices(QueryPricesRequest {})).await { + Ok(Ok(prices)) => prices.into_inner(), + Ok(Err(e)) => { tracing::error!( error = %e, "failed to get prices from oracle sidecar" @@ -53,6 +64,15 @@ impl Handler { vote_extension: vec![].into(), }); } + Err(e) => { + tracing::error!( + error = %e, + "failed to get prices from oracle sidecar within timeout duration" + ); + return Ok(abci::response::ExtendVote { + vote_extension: vec![].into(), + }); + } }; let oracle_vote_extension = self @@ -71,7 +91,6 @@ impl Handler { vote_extension: abci::request::VerifyVoteExtension, is_proposal_phase: bool, ) -> anyhow::Result { - // TODO: verify the vote extension based on slinky rules let oracle_vote_extension = OracleVoteExtension::decode(vote_extension.vote_extension)?; let max_num_currency_pairs = diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index 57dbe0f709..e5e0697f8c 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -113,7 +113,7 @@ impl Sequencer { .context("failed to initialize global address base prefix")?; } - let vote_extension_handler = if config.oracle_enabled { + let oracle_client = if config.oracle_enabled { let uri: Uri = config .oracle_grpc_addr .parse() @@ -128,15 +128,20 @@ impl Sequencer { .await .context("failed to get oracle prices")?; info!("oracle sidecar is reachable"); - Some(crate::app::vote_extension::Handler::new(oracle_client)) + Some(oracle_client) } else { None }; let mempool = Mempool::new(); - let app = App::new(snapshot, mempool.clone(), vote_extension_handler, metrics) - .await - .context("failed to initialize app")?; + let app = App::new( + snapshot, + mempool.clone(), + crate::app::vote_extension::Handler::new(oracle_client, config.oracle_client_timeout), + metrics, + ) + .await + .context("failed to initialize app")?; let consensus_service = tower::ServiceBuilder::new() .layer(request_span::layer(|req: &ConsensusRequest| { diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index cf3094a0f5..deab5b28e4 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -531,7 +531,7 @@ mod test { let snapshot = storage.latest_snapshot(); let mempool = Mempool::new(); let metrics = Box::leak(Box::new(Metrics::new())); - let mut app = App::new(snapshot, mempool.clone(), None, metrics) + let mut app = App::new(snapshot, mempool.clone(), crate::app::vote_extension::Handler::new(None, 100), metrics) .await .unwrap(); app.init_chain(storage.clone(), genesis_state, vec![], "test".to_string()) diff --git a/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap b/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap index cf762a8319..35ff11de83 100644 --- a/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap +++ b/crates/astria-sequencer/src/snapshots/astria_sequencer__genesis__test__genesis_state_is_unchanged.snap @@ -58,5 +58,6 @@ expression: genesis_state() "bridge_sudo_change_fee": 24, "ics20_withdrawal_base_fee": 24 }, - "slinky": {} + "market_map": {}, + "oracle": {} } From 82763639c5844ae23b6e3dad2aaa19af6f49c5bc Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 3 Jul 2024 13:15:18 -0400 Subject: [PATCH 10/89] add native types for slinky proto types --- crates/astria-core/src/lib.rs | 1 + crates/astria-core/src/slinky.rs | 718 ++++++++++++++++++ .../src/app/vote_extension.rs | 64 +- crates/astria-sequencer/src/sequencer.rs | 4 +- .../astria-sequencer/src/service/consensus.rs | 11 +- .../test-genesis-app-state.json | 51 +- 6 files changed, 814 insertions(+), 35 deletions(-) create mode 100644 crates/astria-core/src/slinky.rs diff --git a/crates/astria-core/src/lib.rs b/crates/astria-core/src/lib.rs index 81733f10aa..c4c4fa0c1f 100644 --- a/crates/astria-core/src/lib.rs +++ b/crates/astria-core/src/lib.rs @@ -12,6 +12,7 @@ pub mod execution; pub mod primitive; pub mod protocol; pub mod sequencerblock; +pub mod slinky; #[cfg(feature = "brotli")] pub mod brotli; diff --git a/crates/astria-core/src/slinky.rs b/crates/astria-core/src/slinky.rs new file mode 100644 index 0000000000..330e93c1c4 --- /dev/null +++ b/crates/astria-core/src/slinky.rs @@ -0,0 +1,718 @@ +pub mod abci { + pub mod v1 { + use std::collections::HashMap; + + use crate::generated::slinky::abci::v1 as raw; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct OracleVoteExtension { + prices: HashMap>, + } + + impl OracleVoteExtension { + #[must_use] + pub fn from_raw(raw: raw::OracleVoteExtension) -> Self { + Self { + prices: raw.prices, + } + } + + #[must_use] + pub fn into_raw(self) -> raw::OracleVoteExtension { + raw::OracleVoteExtension { + prices: self.prices, + } + } + } + } +} + +pub mod types { + pub mod v1 { + use crate::generated::slinky::types::v1 as raw; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct CurrencyPair { + base: String, + quote: String, + } + + impl CurrencyPair { + #[must_use] + pub fn from_raw(raw: raw::CurrencyPair) -> Self { + Self { + base: raw.base, + quote: raw.quote, + } + } + + #[must_use] + pub fn into_raw(self) -> raw::CurrencyPair { + raw::CurrencyPair { + base: self.base, + quote: self.quote, + } + } + } + + impl std::fmt::Display for CurrencyPair { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}/{}", self.base, self.quote) + } + } + + impl std::str::FromStr for CurrencyPair { + type Err = CurrencyPairParseError; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split('/').collect(); + if parts.len() != 2 { + return Err(CurrencyPairParseError::invalid_currency_pair_string(s)); + } + + Ok(Self { + base: parts[0].to_string(), + quote: parts[1].to_string(), + }) + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct CurrencyPairParseError(CurrencyPairParseErrorKind); + + #[derive(Debug, thiserror::Error)] + pub enum CurrencyPairParseErrorKind { + #[error("invalid currency pair string: {0}")] + InvalidCurrencyPairString(String), + } + + impl CurrencyPairParseError { + pub fn invalid_currency_pair_string(s: &str) -> Self { + Self(CurrencyPairParseErrorKind::InvalidCurrencyPairString( + s.to_string(), + )) + } + } + } +} + +pub mod market_map { + pub mod v1 { + use std::{ + collections::HashMap, + str::FromStr, + }; + + use crate::{ + generated::slinky::marketmap::v1 as raw, + primitive::v1::{ + Address, + AddressError, + }, + slinky::types::v1::CurrencyPair, + }; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct GenesisState { + market_map: MarketMap, + last_updated: u64, + params: Params, + } + + impl GenesisState { + pub fn try_from_raw(raw: raw::GenesisState) -> Result { + let Some(market_map) = raw + .market_map + .map(MarketMap::try_from_raw) + .transpose() + .map_err(GenesisStateError::invalid_market_map)? + else { + return Err(GenesisStateError::missing_market_map()); + }; + let last_updated = raw.last_updated; + let Some(params) = raw + .params + .map(Params::try_from_raw) + .transpose() + .map_err(GenesisStateError::invalid_params)? + else { + return Err(GenesisStateError::missing_params()); + }; + Ok(Self { + market_map, + last_updated, + params, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::GenesisState { + raw::GenesisState { + market_map: Some(self.market_map.into_raw()), + last_updated: self.last_updated, + params: Some(self.params.into_raw()), + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct GenesisStateError(GenesisStateErrorKind); + + impl GenesisStateError { + pub fn missing_market_map() -> Self { + Self(GenesisStateErrorKind::MissingMarketMap) + } + + pub fn invalid_market_map(err: MarketMapError) -> Self { + Self(GenesisStateErrorKind::MarketMapParseError(err)) + } + + pub fn missing_params() -> Self { + Self(GenesisStateErrorKind::MissingParams) + } + + pub fn invalid_params(err: ParamsError) -> Self { + Self(GenesisStateErrorKind::ParamsParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum GenesisStateErrorKind { + #[error("missing market map")] + MissingMarketMap, + #[error(transparent)] + MarketMapParseError(#[from] MarketMapError), + #[error("missing params")] + MissingParams, + #[error(transparent)] + ParamsParseError(#[from] ParamsError), + } + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Params { + market_authorities: Vec
, + admin: Address, + } + + impl Params { + pub fn try_from_raw(raw: raw::Params) -> Result { + let market_authorities = raw + .market_authorities + .into_iter() + .map(|s| Address::from_str(&s)) + .collect::, _>>() + .map_err(ParamsError::market_authority_parse_error)?; + let admin = raw.admin.parse().map_err(ParamsError::admin_parse_error)?; + Ok(Self { + market_authorities, + admin, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::Params { + raw::Params { + market_authorities: self + .market_authorities + .into_iter() + .map(|a| a.to_string()) + .collect(), + admin: self.admin.to_string(), + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct ParamsError(ParamsErrorKind); + + impl ParamsError { + pub fn market_authority_parse_error(err: AddressError) -> Self { + Self(ParamsErrorKind::MarketAuthorityParseError(err)) + } + + pub fn admin_parse_error(err: AddressError) -> Self { + Self(ParamsErrorKind::AdminParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + pub enum ParamsErrorKind { + #[error("failed to parse market authority address")] + MarketAuthorityParseError(#[source] AddressError), + #[error("failed to parse admin address")] + AdminParseError(#[source] AddressError), + } + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Market { + ticker: Ticker, + provider_configs: Vec, + } + + impl Market { + pub fn try_from_raw(raw: raw::Market) -> Result { + let Some(ticker) = raw + .ticker + .map(Ticker::try_from_raw) + .transpose() + .map_err(MarketError::invalid_ticker)? + else { + return Err(MarketError::missing_ticker()); + }; + + let provider_configs = raw + .provider_configs + .into_iter() + .map(ProviderConfig::try_from_raw) + .collect::, _>>() + .map_err(MarketError::invalid_provider_config)?; + Ok(Self { + ticker, + provider_configs, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::Market { + raw::Market { + ticker: Some(self.ticker.into_raw()), + provider_configs: self + .provider_configs + .into_iter() + .map(ProviderConfig::into_raw) + .collect(), + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct MarketError(MarketErrorKind); + + impl MarketError { + pub fn missing_ticker() -> Self { + Self(MarketErrorKind::MissingTicker) + } + + pub fn invalid_ticker(err: TickerError) -> Self { + Self(MarketErrorKind::TickerParseError(err)) + } + + pub fn invalid_provider_config(err: ProviderConfigError) -> Self { + Self(MarketErrorKind::ProviderConfigParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum MarketErrorKind { + #[error("missing ticker")] + MissingTicker, + #[error(transparent)] + TickerParseError(#[from] TickerError), + #[error(transparent)] + ProviderConfigParseError(#[from] ProviderConfigError), + } + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Ticker { + currency_pair: CurrencyPair, + decimals: u64, + min_provider_count: u64, + enabled: bool, + metadata_json: String, + } + + impl Ticker { + pub fn try_from_raw(raw: raw::Ticker) -> Result { + let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { + return Err(TickerError::missing_currency_pair()); + }; + Ok(Self { + currency_pair, + decimals: raw.decimals, + min_provider_count: raw.min_provider_count, + enabled: raw.enabled, + metadata_json: raw.metadata_json, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::Ticker { + raw::Ticker { + currency_pair: Some(self.currency_pair.into_raw()), + decimals: self.decimals, + min_provider_count: self.min_provider_count, + enabled: self.enabled, + metadata_json: self.metadata_json, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct TickerError(TickerErrorKind); + + impl TickerError { + pub fn missing_currency_pair() -> Self { + Self(TickerErrorKind::MissingCurrencyPair) + } + } + + #[derive(Debug, thiserror::Error)] + enum TickerErrorKind { + #[error("missing currency pair")] + MissingCurrencyPair, + } + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct ProviderConfig { + name: String, + off_chain_ticker: String, + normalize_by_pair: CurrencyPair, + invert: bool, + metadata_json: String, + } + + impl ProviderConfig { + pub fn try_from_raw(raw: raw::ProviderConfig) -> Result { + let Some(normalize_by_pair) = raw.normalize_by_pair.map(CurrencyPair::from_raw) + else { + return Err(ProviderConfigError::missing_normalize_by_pair()); + }; + Ok(Self { + name: raw.name, + off_chain_ticker: raw.off_chain_ticker, + normalize_by_pair, + invert: raw.invert, + metadata_json: raw.metadata_json, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::ProviderConfig { + raw::ProviderConfig { + name: self.name, + off_chain_ticker: self.off_chain_ticker, + normalize_by_pair: Some(self.normalize_by_pair.into_raw()), + invert: self.invert, + metadata_json: self.metadata_json, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct ProviderConfigError(ProviderConfigErrorKind); + + impl ProviderConfigError { + pub fn missing_normalize_by_pair() -> Self { + Self(ProviderConfigErrorKind::MissingNormalizeByPair) + } + } + + #[derive(Debug, thiserror::Error)] + enum ProviderConfigErrorKind { + #[error("missing normalize by pair")] + MissingNormalizeByPair, + } + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct MarketMap { + pub markets: HashMap, + } + + impl MarketMap { + pub fn try_from_raw(raw: raw::MarketMap) -> Result { + let mut markets = HashMap::new(); + for (k, v) in raw.markets { + let market = Market::try_from_raw(v) + .map_err(|e| MarketMapError::invalid_market(k.clone(), e))?; + markets.insert(k, market); + } + Ok(Self { + markets, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::MarketMap { + let markets = self + .markets + .into_iter() + .map(|(k, v)| (k, v.into_raw())) + .collect(); + raw::MarketMap { + markets, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct MarketMapError(MarketMapErrorKind); + + impl MarketMapError { + pub fn invalid_market(name: String, err: MarketError) -> Self { + Self(MarketMapErrorKind::InvalidMarket(name, err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum MarketMapErrorKind { + #[error("invalid market {0}")] + InvalidMarket(String, MarketError), + } + } +} + +pub mod oracle { + pub mod v1 { + use pbjson_types::Timestamp; + + use crate::{ + generated::slinky::oracle::v1 as raw, + slinky::types::v1::CurrencyPair, + }; + + #[derive(Debug, Clone)] + pub struct QuotePrice { + price: u128, + block_timestamp: Timestamp, + block_height: u64, + } + + impl QuotePrice { + pub fn try_from_raw(raw: raw::QuotePrice) -> Result { + let price = raw + .price + .parse() + .map_err(QuotePriceError::price_parse_error)?; + let Some(block_timestamp) = raw.block_timestamp.clone() else { + return Err(QuotePriceError::missing_block_timestamp()); + }; + let block_height = raw.block_height; + Ok(Self { + price, + block_timestamp, + block_height, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::QuotePrice { + raw::QuotePrice { + price: self.price.to_string(), + block_timestamp: Some(self.block_timestamp), + block_height: self.block_height, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct QuotePriceError(QuotePriceErrorKind); + + impl QuotePriceError { + pub fn price_parse_error(err: std::num::ParseIntError) -> Self { + Self(QuotePriceErrorKind::PriceParseError(err)) + } + + pub fn missing_block_timestamp() -> Self { + Self(QuotePriceErrorKind::MissingBlockTimestamp) + } + } + + #[derive(Debug, thiserror::Error)] + enum QuotePriceErrorKind { + #[error(transparent)] + PriceParseError(#[from] std::num::ParseIntError), + #[error("missing block timestamp")] + MissingBlockTimestamp, + } + + #[derive(Debug, Clone)] + pub struct CurrencyPairState { + price: QuotePrice, + nonce: u64, + id: u64, + } + + impl CurrencyPairState { + pub fn try_from_raw( + raw: raw::CurrencyPairState, + ) -> Result { + let Some(price) = raw + .price + .map(QuotePrice::try_from_raw) + .transpose() + .map_err(CurrencyPairStateError::quote_price_parse_error)? + else { + return Err(CurrencyPairStateError::missing_price()); + }; + let nonce = raw.nonce; + let id = raw.id; + Ok(Self { + price, + nonce, + id, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::CurrencyPairState { + raw::CurrencyPairState { + price: Some(self.price.into_raw()), + nonce: self.nonce, + id: self.id, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct CurrencyPairStateError(CurrencyPairStateErrorKind); + + impl CurrencyPairStateError { + pub fn missing_price() -> Self { + Self(CurrencyPairStateErrorKind::MissingPrice) + } + + pub fn quote_price_parse_error(err: QuotePriceError) -> Self { + Self(CurrencyPairStateErrorKind::QuotePriceParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum CurrencyPairStateErrorKind { + #[error("missing price")] + MissingPrice, + #[error(transparent)] + QuotePriceParseError(QuotePriceError), + } + + #[derive(Debug, Clone)] + pub struct CurrencyPairGenesis { + currency_pair: CurrencyPair, + currency_pair_price: QuotePrice, + id: u64, + nonce: u64, + } + + impl CurrencyPairGenesis { + pub fn try_from_raw( + raw: raw::CurrencyPairGenesis, + ) -> Result { + let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { + return Err(CurrencyPairGenesisError::missing_currency_pair()); + }; + let Some(currency_pair_price) = raw + .currency_pair_price + .map(QuotePrice::try_from_raw) + .transpose() + .map_err(CurrencyPairGenesisError::quote_price_parse_error)? + else { + return Err(CurrencyPairGenesisError::missing_currency_pair_price()); + }; + let id = raw.id; + let nonce = raw.nonce; + Ok(Self { + currency_pair, + currency_pair_price, + id, + nonce, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::CurrencyPairGenesis { + raw::CurrencyPairGenesis { + currency_pair: Some(self.currency_pair.into_raw()), + currency_pair_price: Some(self.currency_pair_price.into_raw()), + id: self.id, + nonce: self.nonce, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct CurrencyPairGenesisError(CurrencyPairGenesisErrorKind); + + impl CurrencyPairGenesisError { + pub fn missing_currency_pair() -> Self { + Self(CurrencyPairGenesisErrorKind::MissingCurrencyPair) + } + + pub fn missing_currency_pair_price() -> Self { + Self(CurrencyPairGenesisErrorKind::MissingCurrencyPairPrice) + } + + pub fn quote_price_parse_error(err: QuotePriceError) -> Self { + Self(CurrencyPairGenesisErrorKind::QuotePriceParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum CurrencyPairGenesisErrorKind { + #[error("missing currency pair")] + MissingCurrencyPair, + #[error("missing currency pair price")] + MissingCurrencyPairPrice, + #[error(transparent)] + QuotePriceParseError(QuotePriceError), + } + + #[derive(Debug, Clone)] + pub struct GenesisState { + pub currency_pair_genesis: Vec, + pub next_id: u64, + } + + impl GenesisState { + pub fn try_from_raw(raw: raw::GenesisState) -> Result { + let currency_pair_genesis = raw + .currency_pair_genesis + .into_iter() + .map(CurrencyPairGenesis::try_from_raw) + .collect::, _>>() + .map_err(GenesisStateError::currency_pair_genesis_parse_error)?; + let next_id = raw.next_id; + Ok(Self { + currency_pair_genesis, + next_id, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::GenesisState { + raw::GenesisState { + currency_pair_genesis: self + .currency_pair_genesis + .into_iter() + .map(CurrencyPairGenesis::into_raw) + .collect(), + next_id: self.next_id, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct GenesisStateError(GenesisStateErrorKind); + + impl GenesisStateError { + pub fn currency_pair_genesis_parse_error(err: CurrencyPairGenesisError) -> Self { + Self(GenesisStateErrorKind::CurrencyPairGenesisParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum GenesisStateErrorKind { + #[error(transparent)] + CurrencyPairGenesisParseError(CurrencyPairGenesisError), + } + } +} diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 8bccd309dc..8a0112d875 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -32,7 +32,10 @@ pub(crate) struct Handler { } impl Handler { - pub(crate) fn new(oracle_client: Option>, oracle_client_timeout: u64) -> Self { + pub(crate) fn new( + oracle_client: Option>, + oracle_client_timeout: u64, + ) -> Self { Self { oracle_client, oracle_client_timeout: tokio::time::Duration::from_millis(oracle_client_timeout), @@ -51,9 +54,14 @@ impl Handler { }); }; - // if we fail to get prices within the timeout duration, we will return an empty vote extension - // to ensure liveness. - let prices = match tokio::time::timeout(self.oracle_client_timeout, oracle_client.prices(QueryPricesRequest {})).await { + // if we fail to get prices within the timeout duration, we will return an empty vote + // extension to ensure liveness. + let prices = match tokio::time::timeout( + self.oracle_client_timeout, + oracle_client.prices(QueryPricesRequest {}), + ) + .await + { Ok(Ok(prices)) => prices.into_inner(), Ok(Err(e)) => { tracing::error!( @@ -75,8 +83,7 @@ impl Handler { } }; - let oracle_vote_extension = self - .transform_oracle_service_prices(state, prices) + let oracle_vote_extension = transform_oracle_service_prices(state, prices) .await .context("failed to transform oracle service prices")?; @@ -86,7 +93,7 @@ impl Handler { } pub(crate) async fn verify_vote_extension( - &mut self, + &self, state: &S, vote_extension: abci::request::VerifyVoteExtension, is_proposal_phase: bool, @@ -112,30 +119,29 @@ impl Handler { Ok(abci::response::VerifyVoteExtension::Accept) } +} - // see https://github.com/skip-mev/slinky/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 - async fn transform_oracle_service_prices( - &self, - state: &S, - prices: QueryPricesResponse, - ) -> anyhow::Result { - let mut strategy_prices = HashMap::new(); - for (currency_pair_id, price_string) in prices.prices { - let currency_pair = currency_pair_from_string(¤cy_pair_id)?; - let price = price_string.parse::()?; - - let id = DefaultCurrencyPairStrategy::id(state, ¤cy_pair) - .await - .context("failed to get id for currency pair")?; - let encoded_price = - DefaultCurrencyPairStrategy::get_encoded_price(state, ¤cy_pair, price).await; - strategy_prices.insert(id, encoded_price); - } - - Ok(OracleVoteExtension { - prices: strategy_prices, - }) +// see https://github.com/skip-mev/slinky/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 +async fn transform_oracle_service_prices( + state: &S, + prices: QueryPricesResponse, +) -> anyhow::Result { + let mut strategy_prices = HashMap::new(); + for (currency_pair_id, price_string) in prices.prices { + let currency_pair = currency_pair_from_string(¤cy_pair_id)?; + let price = price_string.parse::()?; + + let id = DefaultCurrencyPairStrategy::id(state, ¤cy_pair) + .await + .context("failed to get id for currency pair")?; + let encoded_price = + DefaultCurrencyPairStrategy::get_encoded_price(state, ¤cy_pair, price).await; + strategy_prices.insert(id, encoded_price); } + + Ok(OracleVoteExtension { + prices: strategy_prices, + }) } fn currency_pair_from_string(s: &str) -> anyhow::Result { diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index e5e0697f8c..4dbb3f9424 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -123,11 +123,11 @@ impl Sequencer { // ensure the oracle sidecar is reachable // TODO: allow this to retry in case the oracle sidecar is not ready yet - let _ = oracle_client + let prices = oracle_client .prices(QueryPricesRequest::default()) .await .context("failed to get oracle prices")?; - info!("oracle sidecar is reachable"); + info!(prices = ?prices.into_inner(), "oracle sidecar is reachable"); Some(oracle_client) } else { None diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index deab5b28e4..9d5a8ae747 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -531,9 +531,14 @@ mod test { let snapshot = storage.latest_snapshot(); let mempool = Mempool::new(); let metrics = Box::leak(Box::new(Metrics::new())); - let mut app = App::new(snapshot, mempool.clone(), crate::app::vote_extension::Handler::new(None, 100), metrics) - .await - .unwrap(); + let mut app = App::new( + snapshot, + mempool.clone(), + crate::app::vote_extension::Handler::new(None, 100), + metrics, + ) + .await + .unwrap(); app.init_chain(storage.clone(), genesis_state, vec![], "test".to_string()) .await .unwrap(); diff --git a/crates/astria-sequencer/test-genesis-app-state.json b/crates/astria-sequencer/test-genesis-app-state.json index 87f6879ecb..3de0c7bcb7 100644 --- a/crates/astria-sequencer/test-genesis-app-state.json +++ b/crates/astria-sequencer/test-genesis-app-state.json @@ -31,5 +31,54 @@ "ics20_withdrawal_base_fee": 24 }, "native_asset_base_denomination": "nria", - "allowed_fee_assets": ["nria"] + "allowed_fee_assets": ["nria"], + "market_map": { + "marketMap": { + "tickers": { + "BITCOIN/USD": { + "currencyPair": { + "Base": "BITCOIN", + "Quote": "USD" + }, + "decimals": "8", + "minProviderCount": "3" + } + }, + "paths": { + "BITCOIN/USD": { + "paths": [ + { + "operations": [ + { + "currencyPair": { + "Base": "BITCOIN", + "Quote": "USD" + } + } + ] + } + ] + } + }, + "providers": { + "BITCOIN/USD": { + "providers": [ + { + "name": "kucoin", + "offChainTicker": "btc_usd" + }, + { + "name": "mexc", + "offChainTicker": "btc-usd" + }, + { + "name": "binance", + "offChainTicker": "BTCUSD" + } + ] + } + } + }, + "lastUpdated": "1" + } } From 27267e257001b25efe9e639dabad3ad81f5b9163 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 3 Jul 2024 14:27:03 -0400 Subject: [PATCH 11/89] update genesis example to incude market map --- crates/astria-core/src/slinky.rs | 24 +++++----- .../src/genesis_example.rs | 45 ++++++++++++++++++- .../src/genesis_parser.rs | 8 ++++ .../src/app/vote_extension.rs | 5 +++ 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/crates/astria-core/src/slinky.rs b/crates/astria-core/src/slinky.rs index 1b5b0b4b2c..aa04a36326 100644 --- a/crates/astria-core/src/slinky.rs +++ b/crates/astria-core/src/slinky.rs @@ -270,8 +270,8 @@ pub mod market_map { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct Market { - ticker: Ticker, - provider_configs: Vec, + pub ticker: Ticker, + pub provider_configs: Vec, } impl Market { @@ -341,11 +341,11 @@ pub mod market_map { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct Ticker { - currency_pair: CurrencyPair, - decimals: u64, - min_provider_count: u64, - enabled: bool, - metadata_json: String, + pub currency_pair: CurrencyPair, + pub decimals: u64, + pub min_provider_count: u64, + pub enabled: bool, + pub metadata_json: String, } impl Ticker { @@ -393,11 +393,11 @@ pub mod market_map { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProviderConfig { - name: String, - off_chain_ticker: String, - normalize_by_pair: CurrencyPair, - invert: bool, - metadata_json: String, + pub name: String, + pub off_chain_ticker: String, + pub normalize_by_pair: CurrencyPair, + pub invert: bool, + pub metadata_json: String, } impl ProviderConfig { diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index cf15941216..166cff3a42 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -19,8 +19,10 @@ use astria_core::{ GenesisState as MarketMapGenesisState, MarketMap, Params, + ProviderConfig, }, oracle::v1::GenesisState as OracleGenesisState, + types::v1::CurrencyPair, }, }; use astria_eyre::eyre::{ @@ -55,6 +57,47 @@ fn charlie() -> Address { } fn genesis_state() -> GenesisState { + use astria_core::slinky::market_map::v1::{ + Market, + Ticker, + }; + + let mut markets = std::collections::HashMap::new(); + markets.insert( + "BITCOIN/USD".to_string(), + Market { + ticker: Ticker { + currency_pair: CurrencyPair::new("BITCOIN".to_string(), "USD".to_string()), + decimals: 8, + min_provider_count: 3, + enabled: true, + metadata_json: "".to_string(), + }, + provider_configs: vec![ + ProviderConfig { + name: "kucoin_ws".to_string(), + off_chain_ticker: "btc_usd".to_string(), + normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), + invert: false, + metadata_json: "".to_string(), + }, + ProviderConfig { + name: "binance".to_string(), + off_chain_ticker: "BTCUSD".to_string(), + normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), + invert: false, + metadata_json: "".to_string(), + }, + ProviderConfig { + name: "mexc".to_string(), + off_chain_ticker: "btc-usd".to_string(), + normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), + invert: false, + metadata_json: "".to_string(), + }, + ], + }, + ); UncheckedGenesisState { accounts: vec![ Account { @@ -94,7 +137,7 @@ fn genesis_state() -> GenesisState { }, market_map: MarketMapGenesisState { market_map: MarketMap { - markets: std::collections::HashMap::new(), + markets, }, last_updated: 0, params: Params { diff --git a/crates/astria-sequencer-utils/src/genesis_parser.rs b/crates/astria-sequencer-utils/src/genesis_parser.rs index a61d67b681..5a43b6c144 100644 --- a/crates/astria-sequencer-utils/src/genesis_parser.rs +++ b/crates/astria-sequencer-utils/src/genesis_parser.rs @@ -80,6 +80,14 @@ fn insert_app_state_and_chain_id(dst: &mut Value, app_state: &Value, chain_id: S let Value::Object(dst) = dst else { panic!("dst is not an object"); }; + dst.get_mut("consensus_params") + .expect("consensus_params field exists in cometbft genesis") + .as_object_mut() + .expect("consensus_params field is an object") + .insert( + "abci".to_string(), + serde_json::json!({ "vote_extensions_enable_height": "1" }), + ); dst.insert("app_state".to_string(), app_state.clone()); dst.insert("chain_id".to_string(), chain_id.into()); } diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index eaf6756abe..4b0c2881f1 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -48,6 +48,7 @@ impl Handler { &mut self, state: &S, ) -> anyhow::Result { + tracing::info!("extending vote"); let Some(oracle_client) = self.oracle_client.as_mut() else { // we allow validators to *not* use the oracle sidecar currently // however, if >1/3 of validators are not using the oracle, the prices will not update. @@ -56,6 +57,8 @@ impl Handler { }); }; + tracing::info!("extending vote; getting prices from oracle sidecar"); + // if we fail to get prices within the timeout duration, we will return an empty vote // extension to ensure liveness. let prices = match tokio::time::timeout( @@ -85,6 +88,8 @@ impl Handler { } }; + tracing::info!(prices = ?prices, "got prices from oracle sidecar; transforming prices"); + let oracle_vote_extension = transform_oracle_service_prices(state, prices) .await .context("failed to transform oracle service prices")?; From 536fd2c4845eef0a2bd423d13887909ba38d16c9 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 3 Jul 2024 17:02:14 -0400 Subject: [PATCH 12/89] implement prepare/process proposal vote extension logic --- crates/astria-core/src/slinky.rs | 6 + crates/astria-sequencer/src/address/mod.rs | 6 +- crates/astria-sequencer/src/app/mod.rs | 89 +++++- .../src/app/vote_extension.rs | 289 ++++++++++++++++-- crates/astria-sequencer/src/grpc/slinky.rs | 2 +- crates/astria-sequencer/src/lib.rs | 3 +- .../astria-sequencer/src/service/consensus.rs | 16 +- .../src/slinky/{ => marketmap}/component.rs | 12 +- .../src/slinky/marketmap/mod.rs | 2 + .../src/slinky/{ => marketmap}/state_ext.rs | 0 crates/astria-sequencer/src/slinky/mod.rs | 4 +- .../src/{ => slinky}/oracle/component.rs | 0 .../oracle/currency_pair_strategy.rs | 2 +- .../src/{ => slinky}/oracle/mod.rs | 0 .../src/{ => slinky}/oracle/state_ext.rs | 0 15 files changed, 378 insertions(+), 53 deletions(-) rename crates/astria-sequencer/src/slinky/{ => marketmap}/component.rs (73%) create mode 100644 crates/astria-sequencer/src/slinky/marketmap/mod.rs rename crates/astria-sequencer/src/slinky/{ => marketmap}/state_ext.rs (100%) rename crates/astria-sequencer/src/{ => slinky}/oracle/component.rs (100%) rename crates/astria-sequencer/src/{ => slinky}/oracle/currency_pair_strategy.rs (97%) rename crates/astria-sequencer/src/{ => slinky}/oracle/mod.rs (100%) rename crates/astria-sequencer/src/{ => slinky}/oracle/state_ext.rs (100%) diff --git a/crates/astria-core/src/slinky.rs b/crates/astria-core/src/slinky.rs index aa04a36326..8368f35134 100644 --- a/crates/astria-core/src/slinky.rs +++ b/crates/astria-core/src/slinky.rs @@ -24,6 +24,12 @@ pub mod abci { } } } + + impl From for OracleVoteExtension { + fn from(raw: raw::OracleVoteExtension) -> Self { + Self::from_raw(raw) + } + } } } diff --git a/crates/astria-sequencer/src/address/mod.rs b/crates/astria-sequencer/src/address/mod.rs index 65a9b00009..6cfb2dd735 100644 --- a/crates/astria-sequencer/src/address/mod.rs +++ b/crates/astria-sequencer/src/address/mod.rs @@ -56,8 +56,10 @@ mod regular { .context("failed constructing a dummy address from the provided prefix")?; BASE_PREFIX.set(base_prefix.to_string()).expect( - "THIS IS A BUG: attempted to set the base prefix more than once; it should only be set - once when serving the `InitChain` consensus request, or immediately after Sequencer is + "THIS IS A BUG: attempted to set the base prefix more than once; it should only be \ + set + once when serving the `InitChain` consensus request, or immediately after Sequencer \ + is restarted. It cannot be initialized twice or concurrently from more than one task or \ thread.", ); diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 8f32ead6f9..aca5fc8b5a 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -55,6 +55,7 @@ use tendermint::{ AppHash, Hash, }; +use tendermint_proto::abci::ExtendedCommitInfo; use tracing::{ debug, info, @@ -71,6 +72,7 @@ use crate::{ }, address::StateWriteExt as _, api_state_ext::StateWriteExt as _, + app::vote_extension::ProposalHandler, authority::{ component::{ AuthorityComponent, @@ -103,6 +105,10 @@ use crate::{ }, }, sequence::component::SequenceComponent, + slinky::{ + marketmap::component::MarketMapComponent, + oracle::component::OracleComponent, + }, state_ext::{ StateReadExt as _, StateWriteExt as _, @@ -257,6 +263,12 @@ impl App { SequenceComponent::init_chain(&mut state_tx, &genesis_state) .await .context("failed to call init_chain on SequenceComponent")?; + MarketMapComponent::init_chain(&mut state_tx, &genesis_state) + .await + .context("failed to call init_chain on MarketMapComponent")?; + OracleComponent::init_chain(&mut state_tx, &genesis_state) + .await + .context("failed to call init_chain on OracleComponent")?; state_tx.apply(); @@ -297,11 +309,37 @@ impl App { self.validator_address = Some(prepare_proposal.proposer_address); self.update_state_for_new_round(&storage); - let mut block_size_constraints = BlockSizeConstraints::new( - usize::try_from(prepare_proposal.max_tx_bytes) - .context("failed to convert max_tx_bytes to usize")?, + // create the extended commit info from the local last commit + let Some(last_commit) = prepare_proposal.local_last_commit else { + anyhow::bail!("local last commit is empty; this should not occur") + }; + + // TODO: if this fails, we shouldn't return an error, but instead leave + // the vote extensions empty in this block for liveness. + // it's not a critical error if the oracle values are not updated for a block. + let extended_commit_info = ProposalHandler::prune_and_validate_extended_commit_info( + &self.state, + prepare_proposal.height.into(), + last_commit, ) - .context("failed to create block size constraints")?; + .await + .context("failed to prune and validate extended commit info")?; + + let extended_commit_info_bytes = + ExtendedCommitInfo::from(extended_commit_info).encode_to_vec(); + let max_tx_bytes = usize::try_from(prepare_proposal.max_tx_bytes) + .context("failed to convert max_tx_bytes to usize")?; + + // TODO: just zero this if it's too large + ensure!( + extended_commit_info_bytes.len() <= max_tx_bytes, + "extended commit info is too large to fit in block" + ); + + // adjust max block size to account for extended commit info + let mut block_size_constraints = + BlockSizeConstraints::new(max_tx_bytes - extended_commit_info_bytes.len()) + .context("failed to create block size constraints")?; let block_data = BlockData { misbehavior: prepare_proposal.misbehavior, @@ -334,8 +372,11 @@ impl App { // included in the block let res = generate_rollup_datas_commitment(&signed_txs_included, deposits); + // inject the extended commit info into the start of the block's txs + let mut txs = vec![extended_commit_info_bytes.into()]; + txs.extend(res.into_transactions(included_tx_bytes)); Ok(abci::response::PrepareProposal { - txs: res.into_transactions(included_tx_bytes), + txs, }) } @@ -371,6 +412,30 @@ impl App { self.update_state_for_new_round(&storage); let mut txs = VecDeque::from(process_proposal.txs); + + // the first transaction in the block should be the extended commit info + let extended_commit_info_bytes = txs + .pop_front() + .context("no extended commit info in proposal")?; + + // decode the extended commit info and validate it + let extended_commit_info = ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) + .context("failed to decode extended commit info")?; + let extended_commit_info = extended_commit_info + .try_into() + .context("failed to convert extended commit info from proto to native")?; + let Some(last_commit) = process_proposal.proposed_last_commit else { + anyhow::bail!("proposed last commit is empty; this should not occur") + }; + ProposalHandler::validate_extended_commit_info( + &self.state, + process_proposal.height.value(), + &last_commit, + &extended_commit_info, + ) + .await + .context("failed to validate extended commit info")?; + let received_rollup_datas_root: [u8; 32] = txs .pop_front() .context("no transaction commitment in proposal")? @@ -751,7 +816,7 @@ impl App { pub(crate) async fn verify_vote_extension( &mut self, vote_extension: abci::request::VerifyVoteExtension, - ) -> anyhow::Result { + ) -> abci::response::VerifyVoteExtension { self.vote_extension_handler .verify_vote_extension(&self.state, vote_extension, false) .await @@ -985,6 +1050,12 @@ impl App { SequenceComponent::begin_block(&mut arc_state_tx, begin_block) .await .context("failed to call begin_block on SequenceComponent")?; + MarketMapComponent::begin_block(&mut arc_state_tx, begin_block) + .await + .context("failed to call begin_block on MarketMapComponent")?; + OracleComponent::begin_block(&mut arc_state_tx, begin_block) + .await + .context("failed to call begin_block on OracleComponent")?; let state_tx = Arc::try_unwrap(arc_state_tx) .expect("components should not retain copies of shared state"); @@ -1064,6 +1135,12 @@ impl App { SequenceComponent::end_block(&mut arc_state_tx, &end_block) .await .context("failed to call end_block on SequenceComponent")?; + MarketMapComponent::end_block(&mut arc_state_tx, &end_block) + .await + .context("failed to call end_block on MarketMapComponent")?; + OracleComponent::end_block(&mut arc_state_tx, &end_block) + .await + .context("failed to call end_block on OracleComponent")?; let mut state_tx = Arc::try_unwrap(arc_state_tx) .expect("components should not retain copies of shared state"); diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 4b0c2881f1..1948e7145d 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -5,6 +5,10 @@ use anyhow::{ Context as _, }; use astria_core::{ + crypto::{ + Signature, + VerificationKey, + }, generated::slinky::{ abci::v1::OracleVoteExtension as RawOracleVoteExtension, service::v1::{ @@ -16,11 +20,20 @@ use astria_core::{ slinky::abci::v1::OracleVoteExtension, }; use prost::Message as _; -use tendermint::abci; +use tendermint::{ + abci, + abci::types::{ + BlockSignatureInfo::Flag, + CommitInfo, + ExtendedCommitInfo, + }, +}; use tonic::transport::Channel; +use tracing::debug; use crate::{ - oracle::currency_pair_strategy::DefaultCurrencyPairStrategy, + authority::state_ext::StateReadExt as _, + slinky::oracle::currency_pair_strategy::DefaultCurrencyPairStrategy, state_ext::StateReadExt, }; @@ -102,30 +115,50 @@ impl Handler { pub(crate) async fn verify_vote_extension( &self, state: &S, - vote_extension: abci::request::VerifyVoteExtension, + vote: abci::request::VerifyVoteExtension, is_proposal_phase: bool, - ) -> anyhow::Result { - let oracle_vote_extension = RawOracleVoteExtension::decode(vote_extension.vote_extension)?; + ) -> abci::response::VerifyVoteExtension { + let oracle_vote_extension = match RawOracleVoteExtension::decode(vote.vote_extension) { + Ok(oracle_vote_extension) => oracle_vote_extension.into(), + Err(e) => { + tracing::error!(error = %e, "failed to decode oracle vote extension"); + return abci::response::VerifyVoteExtension::Reject; + } + }; + match verify_vote_extension(state, oracle_vote_extension, is_proposal_phase).await { + Ok(()) => abci::response::VerifyVoteExtension::Accept, + Err(e) => { + tracing::error!(error = %e, "failed to verify vote extension"); + abci::response::VerifyVoteExtension::Reject + } + } + } +} - let max_num_currency_pairs = - DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, is_proposal_phase) - .await - .context("failed to get max number of currency pairs")?; +// see https://github.com/skip-mev/slinky/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L24 +pub(crate) async fn verify_vote_extension( + state: &S, + oracle_vote_extension: OracleVoteExtension, + is_proposal_phase: bool, +) -> anyhow::Result<()> { + let max_num_currency_pairs = + DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, is_proposal_phase) + .await + .context("failed to get max number of currency pairs")?; + + ensure!( + oracle_vote_extension.prices.len() as u64 <= max_num_currency_pairs, + "number of oracle vote extension prices exceeds max expected number of currency pairs" + ); + for prices in oracle_vote_extension.prices.values() { ensure!( - oracle_vote_extension.prices.len() as u64 <= max_num_currency_pairs, - "number of oracle vote extension prices exceeds max expected number of currency pairs" + prices.len() <= MAXIMUM_PRICE_BYTE_LEN, + "encoded price length exceeded {MAXIMUM_PRICE_BYTE_LEN}" ); - - for prices in oracle_vote_extension.prices.values() { - ensure!( - prices.len() <= MAXIMUM_PRICE_BYTE_LEN, - "encoded price length exceeded {MAXIMUM_PRICE_BYTE_LEN}" - ); - } - - Ok(abci::response::VerifyVoteExtension::Accept) } + + Ok(()) } // see https://github.com/skip-mev/slinky/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 @@ -152,3 +185,219 @@ async fn transform_oracle_service_prices( prices: strategy_prices, }) } + +pub(crate) struct ProposalHandler; + +impl ProposalHandler { + // called during prepare_proposal + pub(crate) async fn prune_and_validate_extended_commit_info( + state: &S, + height: u64, + mut extended_commit_info: ExtendedCommitInfo, + ) -> anyhow::Result { + for vote in extended_commit_info.votes.iter_mut() { + let oracle_vote_extension = + RawOracleVoteExtension::decode(vote.vote_extension.clone())?.into(); + if let Err(e) = verify_vote_extension(state, oracle_vote_extension, true).await { + debug!( + error = AsRef::::as_ref(&e), + validator = crate::address::base_prefixed(vote.validator.address).to_string(), + "failed to verify vote extension; pruning from proposal" + ); + vote.sig_info = Flag(tendermint::block::BlockIdFlag::Absent); + vote.extension_signature = None; + vote.vote_extension = vec![].into(); + } + } + validate_vote_extensions(state, height, &extended_commit_info) + .await + .context("failed to validate vote extensions in prepare_proposal")?; + Ok(extended_commit_info) + } + + // called during process_proposal + pub(crate) async fn validate_extended_commit_info( + state: &S, + height: u64, + last_commit: &CommitInfo, + extended_commit_info: &ExtendedCommitInfo, + ) -> anyhow::Result<()> { + // inside process_proposal, we must validate the vote extensions proposed against the last + // commit proposed + validate_extended_commit_against_last_commit(last_commit, extended_commit_info)?; + + validate_vote_extensions(state, height, extended_commit_info) + .await + .context("failed to validate vote extensions in validate_extended_commit_info")?; + Ok(()) + } +} + +// see https://github.com/skip-mev/slinky/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L111 +async fn validate_vote_extensions( + state: &S, + height: u64, + extended_commit_info: &ExtendedCommitInfo, +) -> anyhow::Result<()> { + use tendermint_proto::v0_38::types::CanonicalVoteExtension; + + let chain_id = state + .get_chain_id() + .await + .context("failed to get chain id")?; + + // total validator voting power + let mut total_voting_power: u64 = 0; + // the total voting power of all validators which submitted vote extensions + let mut submitted_voting_power: u64 = 0; + + let validator_set = state + .get_validator_set() + .await + .context("failed to get validator set")?; + + for vote in &extended_commit_info.votes { + total_voting_power = total_voting_power.saturating_add(vote.validator.power.value()); + + if vote.sig_info == Flag(tendermint::block::BlockIdFlag::Commit) + && vote.extension_signature.is_none() + { + anyhow::bail!( + "vote extension signature is missing for validator {}", + crate::address::base_prefixed(vote.validator.address) + ); + } + + if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) + && vote.vote_extension.len() > 0 + { + anyhow::bail!( + "non-commit vote extension present for validator {}", + crate::address::base_prefixed(vote.validator.address) + ); + } + + if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) + && vote.extension_signature.is_some() + { + anyhow::bail!( + "non-commit extension signature present for validator {}", + crate::address::base_prefixed(vote.validator.address) + ); + } + + if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) { + continue; + } + + submitted_voting_power = + submitted_voting_power.saturating_add(vote.validator.power.value()); + + let pubkey = validator_set + .get( + &vote + .validator + .address + .to_vec() + .try_into() + .expect("can always convert 20 bytes to account::Id"), + ) + .context("validator not found")? + .pub_key; + let verification_key = VerificationKey::try_from(pubkey.to_bytes().as_slice()) + .context("failed to create verification key")?; + + let vote_extension = CanonicalVoteExtension { + extension: vote.vote_extension.to_vec(), + height: (height - 1) as i64, + round: extended_commit_info.round.value() as i64, + chain_id: chain_id.to_string(), + }; + + // TODO: double check that it's length-delimited + let message = vote_extension.encode_length_delimited_to_vec(); + let signature = Signature::try_from( + vote.extension_signature + .as_ref() + .expect("extension signature is some, as it was checked above") + .as_bytes(), + ) + .context("failed to create signature")?; + verification_key + .verify(&signature, &message) + .context("failed to verify signature for vote extension")?; + } + + // this shouldn't happen, but good to check anyways + if total_voting_power == 0 { + anyhow::bail!("total voting power is zero"); + } + + let required_voting_power = total_voting_power + .checked_mul(2) + .context("failed to multiply total voting power by 2")? + .checked_div(3) + .context("failed to divide total voting power by 3")? + .checked_sub(1) + .context("failed to subtract 1 from total voting power")?; + ensure!( + submitted_voting_power >= required_voting_power, + "submitted voting power is less than required voting power", + ); + + Ok(()) +} + +fn validate_extended_commit_against_last_commit( + last_commit: &CommitInfo, + extended_commit_info: &ExtendedCommitInfo, +) -> anyhow::Result<()> { + ensure!( + last_commit.round == extended_commit_info.round, + "last commit round does not match extended commit round" + ); + + ensure!( + last_commit.votes.len() == extended_commit_info.votes.len(), + "last commit votes length does not match extended commit votes length" + ); + + ensure!( + extended_commit_info.votes.is_sorted_by(|a, b| { + if a.validator.power == b.validator.power { + // addresses sorted in ascending order, if the powers are the same + a.validator.address < b.validator.address + } else { + a.validator.power > b.validator.power + } + }), + "extended commit votes are not sorted by voting power", + ); + + for (i, vote) in extended_commit_info.votes.iter().enumerate() { + let last_commit_vote = &last_commit.votes[i]; + ensure!( + last_commit_vote.validator.address == vote.validator.address, + "last commit vote address does not match extended commit vote address" + ); + ensure!( + last_commit_vote.validator.power == vote.validator.power, + "last commit vote power does not match extended commit vote power" + ); + + // vote is absent; no need to check for the block id flag matching the last commit + if vote.sig_info == Flag(tendermint::block::BlockIdFlag::Absent) + && vote.vote_extension.len() == 0 + && vote.extension_signature.is_none() + { + continue; + } + + ensure!( + vote.sig_info == last_commit_vote.sig_info, + "last commit vote sig info does not match extended commit vote sig info" + ); + } + + Ok(()) +} diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index 7978cf0446..14751db71e 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -20,7 +20,7 @@ use tonic::{ use tracing::instrument; use crate::{ - slinky::state_ext::StateReadExt as _, + slinky::marketmap::state_ext::StateReadExt as _, state_ext::StateReadExt as _, }; diff --git a/crates/astria-sequencer/src/lib.rs b/crates/astria-sequencer/src/lib.rs index 015f50bc3a..37efb0dbab 100644 --- a/crates/astria-sequencer/src/lib.rs +++ b/crates/astria-sequencer/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(is_sorted)] + pub(crate) mod accounts; pub(crate) mod address; mod api_state_ext; @@ -13,7 +15,6 @@ pub(crate) mod grpc; pub(crate) mod ibc; mod mempool; pub(crate) mod metrics; -pub(crate) mod oracle; pub(crate) mod proposal; pub(crate) mod sequence; mod sequencer; diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 25c6733343..ca8fb043f4 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -112,16 +112,7 @@ impl Consensus { } ConsensusRequest::VerifyVoteExtension(vote_extension) => { ConsensusResponse::VerifyVoteExtension( - match self.handle_verify_vote_extension(vote_extension).await { - Ok(response) => response, - Err(e) => { - warn!( - error = AsRef::::as_ref(&e), - "rejecting vote extension" - ); - response::VerifyVoteExtension::Reject - } - }, + self.handle_verify_vote_extension(vote_extension).await, ) } ConsensusRequest::FinalizeBlock(finalize_block) => ConsensusResponse::FinalizeBlock( @@ -217,9 +208,8 @@ impl Consensus { async fn handle_verify_vote_extension( &mut self, vote_extension: request::VerifyVoteExtension, - ) -> anyhow::Result { - let result = self.app.verify_vote_extension(vote_extension).await?; - Ok(result) + ) -> response::VerifyVoteExtension { + self.app.verify_vote_extension(vote_extension).await } #[instrument(skip_all, fields( diff --git a/crates/astria-sequencer/src/slinky/component.rs b/crates/astria-sequencer/src/slinky/marketmap/component.rs similarity index 73% rename from crates/astria-sequencer/src/slinky/component.rs rename to crates/astria-sequencer/src/slinky/marketmap/component.rs index d16c4a264d..64838797af 100644 --- a/crates/astria-sequencer/src/slinky/component.rs +++ b/crates/astria-sequencer/src/slinky/marketmap/component.rs @@ -13,16 +13,14 @@ use tracing::instrument; use super::state_ext::StateWriteExt; use crate::component::Component; -// TODO rename to MarketMapComponent -// or do we want to put all slinky stuff in one component? #[derive(Default)] -pub(crate) struct SlinkyComponent; +pub(crate) struct MarketMapComponent; #[async_trait::async_trait] -impl Component for SlinkyComponent { +impl Component for MarketMapComponent { type AppState = astria_core::sequencer::GenesisState; - #[instrument(name = "SlinkyComponent::init_chain", skip(state))] + #[instrument(name = "MarketMapComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { state .put_market_map(app_state.market_map().market_map.clone()) @@ -33,7 +31,7 @@ impl Component for SlinkyComponent { Ok(()) } - #[instrument(name = "SlinkyComponent::begin_block", skip(_state))] + #[instrument(name = "MarketMapComponent::begin_block", skip(_state))] async fn begin_block( _state: &mut Arc, _begin_block: &BeginBlock, @@ -41,7 +39,7 @@ impl Component for SlinkyComponent { Ok(()) } - #[instrument(name = "SlinkyComponent::end_block", skip(_state))] + #[instrument(name = "MarketMapComponent::end_block", skip(_state))] async fn end_block( _state: &mut Arc, _end_block: &EndBlock, diff --git a/crates/astria-sequencer/src/slinky/marketmap/mod.rs b/crates/astria-sequencer/src/slinky/marketmap/mod.rs new file mode 100644 index 0000000000..7e016778de --- /dev/null +++ b/crates/astria-sequencer/src/slinky/marketmap/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod component; +pub(crate) mod state_ext; diff --git a/crates/astria-sequencer/src/slinky/state_ext.rs b/crates/astria-sequencer/src/slinky/marketmap/state_ext.rs similarity index 100% rename from crates/astria-sequencer/src/slinky/state_ext.rs rename to crates/astria-sequencer/src/slinky/marketmap/state_ext.rs diff --git a/crates/astria-sequencer/src/slinky/mod.rs b/crates/astria-sequencer/src/slinky/mod.rs index 7e016778de..8313d335c1 100644 --- a/crates/astria-sequencer/src/slinky/mod.rs +++ b/crates/astria-sequencer/src/slinky/mod.rs @@ -1,2 +1,2 @@ -pub(crate) mod component; -pub(crate) mod state_ext; +pub(crate) mod marketmap; +pub(crate) mod oracle; diff --git a/crates/astria-sequencer/src/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs similarity index 100% rename from crates/astria-sequencer/src/oracle/component.rs rename to crates/astria-sequencer/src/slinky/oracle/component.rs diff --git a/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs similarity index 97% rename from crates/astria-sequencer/src/oracle/currency_pair_strategy.rs rename to crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs index ddbefdc96f..c09d7e7751 100644 --- a/crates/astria-sequencer/src/oracle/currency_pair_strategy.rs +++ b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs @@ -1,7 +1,7 @@ use anyhow::Context as _; use astria_core::slinky::types::v1::CurrencyPair; -use crate::oracle::state_ext::StateReadExt; +use crate::slinky::oracle::state_ext::StateReadExt; /// see https://github.com/skip-mev/slinky/blob/793b2e874d6e720bd288e82e782502e41cf06f8c/abci/strategies/currencypair/default.go pub(crate) struct DefaultCurrencyPairStrategy; diff --git a/crates/astria-sequencer/src/oracle/mod.rs b/crates/astria-sequencer/src/slinky/oracle/mod.rs similarity index 100% rename from crates/astria-sequencer/src/oracle/mod.rs rename to crates/astria-sequencer/src/slinky/oracle/mod.rs diff --git a/crates/astria-sequencer/src/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs similarity index 100% rename from crates/astria-sequencer/src/oracle/state_ext.rs rename to crates/astria-sequencer/src/slinky/oracle/state_ext.rs From d3ba1fad6d425d4874a274da844c47c0ec5dc5a7 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 3 Jul 2024 17:20:22 -0400 Subject: [PATCH 13/89] fix sequencer block construction --- .../src/sequencerblock/v1alpha1/block.rs | 14 ++++++++++++++ crates/astria-sequencer/src/app/mod.rs | 11 ++++++----- crates/astria-sequencer/src/app/vote_extension.rs | 15 +++++++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs index 8653961f16..48a2745eac 100644 --- a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs @@ -192,6 +192,10 @@ impl SequencerBlockError { Self(SequencerBlockErrorKind::IdProofInvalid(source)) } + fn no_extended_commit_info() -> Self { + Self(SequencerBlockErrorKind::NoExtendedCommitInfo) + } + fn no_rollup_transactions_root() -> Self { Self(SequencerBlockErrorKind::NoRollupTransactionsRoot) } @@ -262,6 +266,10 @@ enum SequencerBlockErrorKind { TransactionProofInvalid(#[source] merkle::audit::InvalidProof), #[error("failed constructing a rollup ID proof from the raw protobuf rollup ID proof")] IdProofInvalid(#[source] merkle::audit::InvalidProof), + #[error( + "the cometbft block.data field was too short and did not contain the extended commit info" + )] + NoExtendedCommitInfo, #[error( "the cometbft block.data field was too short and did not contain the rollup transaction \ root" @@ -713,6 +721,12 @@ impl SequencerBlock { let data_hash = tree.root(); let mut data_list = data.into_iter(); + + // TODO: this needs to go into the block header + let _extended_commit_info = data_list + .next() + .ok_or(SequencerBlockError::no_extended_commit_info())?; + let rollup_transactions_root: [u8; 32] = data_list .next() .ok_or(SequencerBlockError::no_rollup_transactions_root())? diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index aca5fc8b5a..a2bb8ed481 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -862,15 +862,16 @@ impl App { } ensure!( - finalize_block.txs.len() >= 2, - "block must contain at least two transactions: the rollup transactions commitment and + finalize_block.txs.len() >= 3, + "block must contain at least three transactions: the extended commit info, the rollup \ + transactions commitment and rollup IDs commitment" ); // cometbft expects a result for every tx in the block, so we need to return a // tx result for the commitments, even though they're not actually user txs. let mut tx_results: Vec = Vec::with_capacity(finalize_block.txs.len()); - tx_results.extend(std::iter::repeat(ExecTxResult::default()).take(2)); + tx_results.extend(std::iter::repeat(ExecTxResult::default()).take(3)); // When the hash is not empty, we have already executed and cached the results if self.executed_proposal_hash.is_empty() { @@ -887,8 +888,8 @@ impl App { .await .context("failed to execute block")?; - // skip the first two transactions, as they are the rollup data commitments - for tx in finalize_block.txs.iter().skip(2) { + // skip the first three transactions, as they are injected transactions + for tx in finalize_block.txs.iter().skip(3) { // remove any included txs from the mempool let tx_hash = Sha256::digest(tx).into(); self.mempool.remove(tx_hash).await; diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 1948e7145d..4c294b3383 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -195,6 +195,11 @@ impl ProposalHandler { height: u64, mut extended_commit_info: ExtendedCommitInfo, ) -> anyhow::Result { + if height == 1 { + // we're proposing block 1, so nothing to validate + return Ok(extended_commit_info); + } + for vote in extended_commit_info.votes.iter_mut() { let oracle_vote_extension = RawOracleVoteExtension::decode(vote.vote_extension.clone())?.into(); @@ -212,6 +217,7 @@ impl ProposalHandler { validate_vote_extensions(state, height, &extended_commit_info) .await .context("failed to validate vote extensions in prepare_proposal")?; + Ok(extended_commit_info) } @@ -222,6 +228,11 @@ impl ProposalHandler { last_commit: &CommitInfo, extended_commit_info: &ExtendedCommitInfo, ) -> anyhow::Result<()> { + if height == 1 { + // we're processing block 1, so nothing to validate (no last commit yet) + return Ok(()); + } + // inside process_proposal, we must validate the vote extensions proposed against the last // commit proposed validate_extended_commit_against_last_commit(last_commit, extended_commit_info)?; @@ -345,6 +356,10 @@ async fn validate_vote_extensions( "submitted voting power is less than required voting power", ); + debug!( + submitted_voting_power, + total_voting_power, "validated extended commit info" + ); Ok(()) } From 8d031caf57f8b480c8abda60b7bc6138329bad20 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 3 Jul 2024 17:43:20 -0400 Subject: [PATCH 14/89] no more unstable is_sorted --- Cargo.lock | 7 + .../src/generated/cosmos.app.v1alpha1.rs | 101 ------ .../generated/cosmos.app.v1alpha1.serde.rs | 329 ------------------ crates/astria-core/src/sequencer.rs | 2 +- crates/astria-sequencer/Cargo.toml | 8 +- .../astria-sequencer/app-genesis-state.json | 125 +++++++ .../src/app/vote_extension.rs | 10 +- crates/astria-sequencer/src/lib.rs | 2 - 8 files changed, 147 insertions(+), 437 deletions(-) delete mode 100644 crates/astria-core/src/generated/cosmos.app.v1alpha1.rs delete mode 100644 crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs create mode 100644 crates/astria-sequencer/app-genesis-state.json diff --git a/Cargo.lock b/Cargo.lock index 255c2d638b..ade02e202f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -777,6 +777,7 @@ dependencies = [ "ibc-proto", "ibc-types", "insta", + "is_sorted", "matchit", "metrics", "penumbra-ibc", @@ -4264,6 +4265,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_sorted" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357376465c37db3372ef6a00585d336ed3d0f11d4345eef77ebcb05865392b21" + [[package]] name = "itertools" version = "0.10.5" diff --git a/crates/astria-core/src/generated/cosmos.app.v1alpha1.rs b/crates/astria-core/src/generated/cosmos.app.v1alpha1.rs deleted file mode 100644 index ec85bb0b92..0000000000 --- a/crates/astria-core/src/generated/cosmos.app.v1alpha1.rs +++ /dev/null @@ -1,101 +0,0 @@ -/// ModuleDescriptor describes an app module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ModuleDescriptor { - /// go_import names the package that should be imported by an app to load the - /// module in the runtime module registry. It is required to make debugging - /// of configuration errors easier for users. - #[prost(string, tag = "1")] - pub go_import: ::prost::alloc::string::String, - /// use_package refers to a protobuf package that this module - /// uses and exposes to the world. In an app, only one module should "use" - /// or own a single protobuf package. It is assumed that the module uses - /// all of the .proto files in a single package. - #[prost(message, repeated, tag = "2")] - pub use_package: ::prost::alloc::vec::Vec, - /// can_migrate_from defines which module versions this module can migrate - /// state from. The framework will check that one module version is able to - /// migrate from a previous module version before attempting to update its - /// config. It is assumed that modules can transitively migrate from earlier - /// versions. For instance if v3 declares it can migrate from v2, and v2 - /// declares it can migrate from v1, the framework knows how to migrate - /// from v1 to v3, assuming all 3 module versions are registered at runtime. - #[prost(message, repeated, tag = "3")] - pub can_migrate_from: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for ModuleDescriptor { - const NAME: &'static str = "ModuleDescriptor"; - const PACKAGE: &'static str = "cosmos.app.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("cosmos.app.v1alpha1.{}", Self::NAME) - } -} -/// PackageReference is a reference to a protobuf package used by a module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PackageReference { - /// name is the fully-qualified name of the package. - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - /// revision is the optional revision of the package that is being used. - /// Protobuf packages used in Cosmos should generally have a major version - /// as the last part of the package name, ex. foo.bar.baz.v1. - /// The revision of a package can be thought of as the minor version of a - /// package which has additional backwards compatible definitions that weren't - /// present in a previous version. - /// - /// A package should indicate its revision with a source code comment - /// above the package declaration in one of its files containing the - /// text "Revision N" where N is an integer revision. All packages start - /// at revision 0 the first time they are released in a module. - /// - /// When a new version of a module is released and items are added to existing - /// .proto files, these definitions should contain comments of the form - /// "Since: Revision N" where N is an integer revision. - /// - /// When the module runtime starts up, it will check the pinned proto - /// image and panic if there are runtime protobuf definitions that are not - /// in the pinned descriptor which do not have - /// a "Since Revision N" comment or have a "Since Revision N" comment where - /// N is <= to the revision specified here. This indicates that the protobuf - /// files have been updated, but the pinned file descriptor hasn't. - /// - /// If there are items in the pinned file descriptor with a revision - /// greater than the value indicated here, this will also cause a panic - /// as it may mean that the pinned descriptor for a legacy module has been - /// improperly updated or that there is some other versioning discrepancy. - /// Runtime protobuf definitions will also be checked for compatibility - /// with pinned file descriptors to make sure there are no incompatible changes. - /// - /// This behavior ensures that: - /// * pinned proto images are up-to-date - /// * protobuf files are carefully annotated with revision comments which - /// are important good client UX - /// * protobuf files are changed in backwards and forwards compatible ways - #[prost(uint32, tag = "2")] - pub revision: u32, -} -impl ::prost::Name for PackageReference { - const NAME: &'static str = "PackageReference"; - const PACKAGE: &'static str = "cosmos.app.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("cosmos.app.v1alpha1.{}", Self::NAME) - } -} -/// MigrateFromInfo is information on a module version that a newer module -/// can migrate from. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MigrateFromInfo { - /// module is the fully-qualified protobuf name of the module config object - /// for the previous module version, ex: "cosmos.group.module.v1.Module". - #[prost(string, tag = "1")] - pub module: ::prost::alloc::string::String, -} -impl ::prost::Name for MigrateFromInfo { - const NAME: &'static str = "MigrateFromInfo"; - const PACKAGE: &'static str = "cosmos.app.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("cosmos.app.v1alpha1.{}", Self::NAME) - } -} diff --git a/crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs b/crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs deleted file mode 100644 index a1f82e3385..0000000000 --- a/crates/astria-core/src/generated/cosmos.app.v1alpha1.serde.rs +++ /dev/null @@ -1,329 +0,0 @@ -impl serde::Serialize for MigrateFromInfo { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.module.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("cosmos.app.v1alpha1.MigrateFromInfo", len)?; - if !self.module.is_empty() { - struct_ser.serialize_field("module", &self.module)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MigrateFromInfo { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "module", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Module, - } - 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 { - "module" => Ok(GeneratedField::Module), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MigrateFromInfo; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct cosmos.app.v1alpha1.MigrateFromInfo") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut module__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Module => { - if module__.is_some() { - return Err(serde::de::Error::duplicate_field("module")); - } - module__ = Some(map_.next_value()?); - } - } - } - Ok(MigrateFromInfo { - module: module__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("cosmos.app.v1alpha1.MigrateFromInfo", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for ModuleDescriptor { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.go_import.is_empty() { - len += 1; - } - if !self.use_package.is_empty() { - len += 1; - } - if !self.can_migrate_from.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("cosmos.app.v1alpha1.ModuleDescriptor", len)?; - if !self.go_import.is_empty() { - struct_ser.serialize_field("go_import", &self.go_import)?; - } - if !self.use_package.is_empty() { - struct_ser.serialize_field("use_package", &self.use_package)?; - } - if !self.can_migrate_from.is_empty() { - struct_ser.serialize_field("can_migrate_from", &self.can_migrate_from)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for ModuleDescriptor { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "go_import", - "goImport", - "use_package", - "usePackage", - "can_migrate_from", - "canMigrateFrom", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - GoImport, - UsePackage, - CanMigrateFrom, - } - 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 { - "goImport" | "go_import" => Ok(GeneratedField::GoImport), - "usePackage" | "use_package" => Ok(GeneratedField::UsePackage), - "canMigrateFrom" | "can_migrate_from" => Ok(GeneratedField::CanMigrateFrom), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ModuleDescriptor; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct cosmos.app.v1alpha1.ModuleDescriptor") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut go_import__ = None; - let mut use_package__ = None; - let mut can_migrate_from__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::GoImport => { - if go_import__.is_some() { - return Err(serde::de::Error::duplicate_field("goImport")); - } - go_import__ = Some(map_.next_value()?); - } - GeneratedField::UsePackage => { - if use_package__.is_some() { - return Err(serde::de::Error::duplicate_field("usePackage")); - } - use_package__ = Some(map_.next_value()?); - } - GeneratedField::CanMigrateFrom => { - if can_migrate_from__.is_some() { - return Err(serde::de::Error::duplicate_field("canMigrateFrom")); - } - can_migrate_from__ = Some(map_.next_value()?); - } - } - } - Ok(ModuleDescriptor { - go_import: go_import__.unwrap_or_default(), - use_package: use_package__.unwrap_or_default(), - can_migrate_from: can_migrate_from__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("cosmos.app.v1alpha1.ModuleDescriptor", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for PackageReference { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.name.is_empty() { - len += 1; - } - if self.revision != 0 { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("cosmos.app.v1alpha1.PackageReference", len)?; - if !self.name.is_empty() { - struct_ser.serialize_field("name", &self.name)?; - } - if self.revision != 0 { - struct_ser.serialize_field("revision", &self.revision)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for PackageReference { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "name", - "revision", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Name, - Revision, - } - 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 { - "name" => Ok(GeneratedField::Name), - "revision" => Ok(GeneratedField::Revision), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = PackageReference; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct cosmos.app.v1alpha1.PackageReference") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut name__ = None; - let mut revision__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Name => { - if name__.is_some() { - return Err(serde::de::Error::duplicate_field("name")); - } - name__ = Some(map_.next_value()?); - } - GeneratedField::Revision => { - if revision__.is_some() { - return Err(serde::de::Error::duplicate_field("revision")); - } - revision__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - } - } - Ok(PackageReference { - name: name__.unwrap_or_default(), - revision: revision__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("cosmos.app.v1alpha1.PackageReference", FIELDS, GeneratedVisitor) - } -} diff --git a/crates/astria-core/src/sequencer.rs b/crates/astria-core/src/sequencer.rs index e577cbb682..5ddc498697 100644 --- a/crates/astria-core/src/sequencer.rs +++ b/crates/astria-core/src/sequencer.rs @@ -1,4 +1,4 @@ -/// specific types that are needed outside of it. +//! Sequencer specific types that are needed outside of it. pub use penumbra_ibc::params::IBCParameters; use crate::{ diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index 531aae1a32..e775092218 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -15,7 +15,11 @@ name = "astria-sequencer" default = [] [dependencies] -astria-core = { path = "../astria-core", features = ["server", "client", "serde"] } +astria-core = { path = "../astria-core", features = [ + "server", + "client", + "serde", +] } astria-build-info = { path = "../astria-build-info", features = ["runtime"] } config = { package = "astria-config", path = "../astria-config" } merkle = { package = "astria-merkle", path = "../astria-merkle" } @@ -25,6 +29,8 @@ telemetry = { package = "astria-telemetry", path = "../astria-telemetry", featur anyhow = "1" borsh = { version = "1", features = ["derive"] } +# `is_sorted_by` is available in rust 1.81.0, but we haven't updated or msrv yet +is_sorted = "0.1.1" matchit = "0.7.2" priority-queue = "2.0.2" tower = "0.4" diff --git a/crates/astria-sequencer/app-genesis-state.json b/crates/astria-sequencer/app-genesis-state.json new file mode 100644 index 0000000000..562c16ee34 --- /dev/null +++ b/crates/astria-sequencer/app-genesis-state.json @@ -0,0 +1,125 @@ +{ + "address_prefixes": { + "base": "astria" + }, + "accounts": [ + { + "address": { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + "balance": 1000000000000000000 + }, + { + "address": { + "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" + }, + "balance": 1000000000000000000 + }, + { + "address": { + "bech32m": "astria1vpcfutferpjtwv457r63uwr6hdm8gwr3pxt5ny" + }, + "balance": 1000000000000000000 + } + ], + "authority_sudo_address": { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + "ibc_sudo_address": { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + "ibc_relayer_addresses": [ + { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + { + "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" + } + ], + "native_asset_base_denomination": "nria", + "ibc_params": { + "ibcEnabled": true, + "inboundIcs20TransfersEnabled": true, + "outboundIcs20TransfersEnabled": true + }, + "allowed_fee_assets": [ + "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 + }, + "market_map": { + "market_map": { + "markets": { + "BITCOIN/USD": { + "ticker": { + "currency_pair": { + "base": "BITCOIN", + "quote": "USD" + }, + "decimals": 8, + "min_provider_count": 3, + "enabled": true, + "metadata_json": "" + }, + "provider_configs": [ + { + "name": "kucoin_ws", + "off_chain_ticker": "btc_usd", + "normalize_by_pair": { + "base": "USDT", + "quote": "USD" + }, + "invert": false, + "metadata_json": "" + }, + { + "name": "binance", + "off_chain_ticker": "BTCUSD", + "normalize_by_pair": { + "base": "USDT", + "quote": "USD" + }, + "invert": false, + "metadata_json": "" + }, + { + "name": "mexc", + "off_chain_ticker": "btc-usd", + "normalize_by_pair": { + "base": "USDT", + "quote": "USD" + }, + "invert": false, + "metadata_json": "" + } + ] + } + } + }, + "last_updated": 0, + "params": { + "market_authorities": [ + { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + { + "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" + } + ], + "admin": { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + } + } + }, + "oracle": { + "currency_pair_genesis": [], + "next_id": 0 + } +} \ No newline at end of file diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 4c294b3383..575392415b 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -378,12 +378,16 @@ fn validate_extended_commit_against_last_commit( ); ensure!( - extended_commit_info.votes.is_sorted_by(|a, b| { + is_sorted::IsSorted::is_sorted_by(&mut extended_commit_info.votes.iter(), |a, b| { if a.validator.power == b.validator.power { // addresses sorted in ascending order, if the powers are the same - a.validator.address < b.validator.address + a.validator.address.partial_cmp(&b.validator.address) } else { - a.validator.power > b.validator.power + // powers sorted in descending order + a.validator + .power + .partial_cmp(&b.validator.power) + .map(|v| v.reverse()) } }), "extended commit votes are not sorted by voting power", diff --git a/crates/astria-sequencer/src/lib.rs b/crates/astria-sequencer/src/lib.rs index 37efb0dbab..a9106927f7 100644 --- a/crates/astria-sequencer/src/lib.rs +++ b/crates/astria-sequencer/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(is_sorted)] - pub(crate) mod accounts; pub(crate) mod address; mod api_state_ext; From b41a7195ebcc2f123143ef812fa59f765bb370cd Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 3 Jul 2024 18:38:15 -0400 Subject: [PATCH 15/89] implement finalize_block price aggregation and storing logic --- Cargo.lock | 1 + crates/astria-core/src/slinky.rs | 8 +- crates/astria-sequencer/Cargo.toml | 1 + crates/astria-sequencer/src/app/mod.rs | 21 +++- ...ransaction_with_every_action_snapshot.snap | 60 ++++----- ...reaking_changes__app_genesis_snapshot.snap | 58 ++++----- .../src/app/vote_extension.rs | 115 +++++++++++++++++- .../src/slinky/oracle/state_ext.rs | 41 ++++++- 8 files changed, 237 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ade02e202f..10fd74d684 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -780,6 +780,7 @@ dependencies = [ "is_sorted", "matchit", "metrics", + "pbjson-types", "penumbra-ibc", "penumbra-proto", "penumbra-tower-trace", diff --git a/crates/astria-core/src/slinky.rs b/crates/astria-core/src/slinky.rs index 8368f35134..de1200d9fb 100644 --- a/crates/astria-core/src/slinky.rs +++ b/crates/astria-core/src/slinky.rs @@ -38,7 +38,7 @@ pub mod types { use crate::generated::slinky::types::v1 as raw; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CurrencyPair { base: String, quote: String, @@ -511,9 +511,9 @@ pub mod oracle { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone)] pub struct QuotePrice { - price: u128, - block_timestamp: Timestamp, - block_height: u64, + pub price: u128, + pub block_timestamp: Timestamp, + pub block_height: u64, } impl QuotePrice { diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index e775092218..9fbb786771 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -46,6 +46,7 @@ hex = { workspace = true, features = ["serde"] } ibc-types = { workspace = true, features = ["with_serde"] } penumbra-ibc = { workspace = true, features = ["component", "rpc"] } metrics = { workspace = true } +pbjson-types = { workspace = true } penumbra-proto = { workspace = true } penumbra-tower-trace = { workspace = true } prost = { workspace = true } diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index a2bb8ed481..cf3ee1e20a 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -868,6 +868,24 @@ impl App { rollup IDs commitment" ); + let extended_commit_info_bytes = &finalize_block.txs[0]; + let extended_commit_info = ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) + .context("failed to decode extended commit info")?; + let extended_commit_info = extended_commit_info + .try_into() + .context("failed to convert extended commit info from proto to native")?; + let mut state_tx: StateDelta>> = + StateDelta::new(self.state.clone()); + crate::app::vote_extension::apply_prices_from_vote_extensions( + &mut state_tx, + extended_commit_info, + finalize_block.time.into(), + finalize_block.height.value(), + ) + .await + .context("failed to apply prices from vote extensions")?; + let _ = self.apply(state_tx); + // cometbft expects a result for every tx in the block, so we need to return a // tx result for the commitments, even though they're not actually user txs. let mut tx_results: Vec = Vec::with_capacity(finalize_block.txs.len()); @@ -1027,7 +1045,8 @@ impl App { &mut self, begin_block: &abci::request::BeginBlock, ) -> anyhow::Result> { - let mut state_tx = StateDelta::new(self.state.clone()); + let mut state_tx: StateDelta>> = + StateDelta::new(self.state.clone()); // store the block height state_tx.put_block_height(begin_block.header.height.into()); diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index edafad96cf..bf165e2b6c 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 220, - 23, - 112, - 98, - 180, - 104, - 39, - 254, - 107, - 65, - 159, - 49, - 59, - 7, - 177, - 110, - 184, - 141, - 73, - 165, - 204, + 11, + 85, + 45, + 146, + 253, + 119, + 85, 144, - 102, - 28, + 23, 247, - 177, - 145, - 116, - 114, - 230, - 27, - 186 + 91, + 214, + 78, + 12, + 244, + 119, + 199, + 174, + 73, + 254, + 135, + 14, + 171, + 124, + 201, + 163, + 196, + 113, + 195, + 133, + 87, + 162 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap index 41ab56cbc9..34572ee57e 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 65, - 219, - 201, + 246, + 18, + 144, 82, - 255, - 77, - 62, + 41, + 213, + 38, + 231, + 98, 112, - 134, - 219, - 249, - 17, - 143, - 126, - 194, - 58, - 86, - 113, - 222, - 141, - 69, - 59, - 118, - 127, - 121, - 44, - 27, - 128, - 120, + 140, + 244, + 13, + 152, + 152, + 46, + 111, + 39, + 148, + 77, + 208, + 109, + 231, + 178, + 24, + 199, 30, - 110, - 160 + 191, + 164, + 250, + 141, + 119 ] diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 575392415b..49c130e62f 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -17,7 +17,11 @@ use astria_core::{ QueryPricesResponse, }, }, - slinky::abci::v1::OracleVoteExtension, + slinky::{ + abci::v1::OracleVoteExtension, + oracle::v1::QuotePrice, + types::v1::CurrencyPair, + }, }; use prost::Message as _; use tendermint::{ @@ -28,12 +32,16 @@ use tendermint::{ ExtendedCommitInfo, }, }; +use tendermint_proto::google::protobuf::Timestamp; use tonic::transport::Channel; use tracing::debug; use crate::{ authority::state_ext::StateReadExt as _, - slinky::oracle::currency_pair_strategy::DefaultCurrencyPairStrategy, + slinky::oracle::{ + currency_pair_strategy::DefaultCurrencyPairStrategy, + state_ext::StateWriteExt, + }, state_ext::StateReadExt, }; @@ -171,7 +179,13 @@ async fn transform_oracle_service_prices( let currency_pair = currency_pair_id .parse() .context("failed to parse currency pair")?; - let price = price_string.parse::()?; + + // TODO: how are the prices encoded into strings in the sidecar?? + let encoded_price = price_string.as_bytes(); + let price = + DefaultCurrencyPairStrategy::get_decoded_price(state, ¤cy_pair, encoded_price) + .await + .context("failed to get decoded price")?; let id = DefaultCurrencyPairStrategy::id(state, ¤cy_pair) .await @@ -420,3 +434,98 @@ fn validate_extended_commit_against_last_commit( Ok(()) } + +pub(crate) async fn apply_prices_from_vote_extensions( + state: &mut S, + extended_commit_info: ExtendedCommitInfo, + timestamp: Timestamp, + height: u64, +) -> anyhow::Result<()> { + let votes = extended_commit_info + .votes + .iter() + .map(|vote| { + let raw = RawOracleVoteExtension::decode(vote.vote_extension.clone()) + .context("failed to decode oracle vote extension")?; + Ok(OracleVoteExtension::from_raw(raw)) + }) + .collect::>>()?; + + let prices = aggregate_oracle_votes(state, votes) + .await + .context("failed to aggregate oracle votes")?; + + // TODO: if a currency pair exists in the state, but isn't in the prices, + // is it considered "removed"? + for (currency_pair, price) in prices { + let price = QuotePrice { + price, + block_timestamp: pbjson_types::Timestamp { + seconds: timestamp.seconds, + nanos: timestamp.nanos, + }, + block_height: height, + }; + + state + .put_price_for_currency_pair(¤cy_pair, price) + .context("failed to put price")?; + } + + Ok(()) +} + +async fn aggregate_oracle_votes( + state: &S, + votes: Vec, +) -> anyhow::Result> { + // validators are not weighted right now, so we just take the median price for each currency + // pair + // + // skip uses a stake-weighted median: https://github.com/skip-mev/slinky/blob/19a916122110cfd0e98d93978107d7ada1586918/pkg/math/voteweighted/voteweighted.go#L59 + // we can implement this later, when we have stake weighting. + let mut currency_pair_to_price_list = HashMap::new(); + for vote in votes { + for (id, price_bytes) in vote.prices { + if price_bytes.len() > MAXIMUM_PRICE_BYTE_LEN { + continue; + } + + let Some(currency_pair) = DefaultCurrencyPairStrategy::from_id(state, id) + .await + .context("failed to get currency pair from id")? + else { + continue; + }; + + let price = + DefaultCurrencyPairStrategy::get_decoded_price(state, ¤cy_pair, &price_bytes) + .await + .context("failed to get decoded price")?; + currency_pair_to_price_list + .entry(currency_pair) + .and_modify(|prices: &mut Vec| prices.push(price)) + .or_insert(vec![price]); + } + } + + let mut prices = HashMap::new(); + for (currency_pair, mut price_list) in currency_pair_to_price_list { + let median_price = if price_list.is_empty() { + // price list should not ever be empty, + // as it was only inserted if it had a price + 0 + } else { + price_list.sort_unstable(); + let mid = price_list.len() / 2; + if price_list.len() % 2 == 0 { + (price_list[mid - 1] + price_list[mid]) / 2 + } else { + price_list[mid] + } + }; + prices.insert(currency_pair, median_price); + } + + Ok(prices) +} diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs index 335e3059ea..eac5bdaa86 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs @@ -3,7 +3,10 @@ use anyhow::{ Result, }; use astria_core::slinky::{ - oracle::v1::CurrencyPairState, + oracle::v1::{ + CurrencyPairState, + QuotePrice, + }, types::v1::CurrencyPair, }; use async_trait::async_trait; @@ -20,6 +23,7 @@ use tracing::instrument; const CURRENCY_PAIR_TO_ID_PREFIX: &str = "oraclecpid"; const ID_TO_CURRENCY_PAIR_PREFIX: &str = "oracleidcp"; const CURRENCY_PAIR_STATE_PREFIX: &str = "oraclecpstate"; +const CURRENCY_PAIR_PRICE_PREFIX: &str = "oraclecpprice"; // TODO: should these values be in nonverifiable storage? const NUM_CURRENCY_PAIRS_KEY: &str = "oraclenumcps"; @@ -38,6 +42,10 @@ fn currency_pair_state_storage_key(currency_pair: &CurrencyPair) -> String { format!("{CURRENCY_PAIR_STATE_PREFIX}/{currency_pair}",) } +fn currency_pair_price_storage_key(currency_pair: &CurrencyPair) -> String { + format!("{CURRENCY_PAIR_PRICE_PREFIX}/{currency_pair}",) +} + /// Newtype wrapper to read and write a u64 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Id(u64); @@ -137,6 +145,25 @@ pub(crate) trait StateReadExt: StateRead { Id::try_from_slice(&bytes).context("invalid next currency pair id bytes")?; Ok(next_currency_pair_id) } + + #[instrument(skip(self))] + async fn get_price_for_currency_pair( + &self, + currency_pair: &CurrencyPair, + ) -> Result> { + let bytes = self + .get_raw(¤cy_pair_price_storage_key(currency_pair)) + .await + .context("failed to get price for currency pair from state")?; + match bytes { + Some(bytes) => { + let price = + serde_json::from_slice(&bytes).context("failed to deserialize price")?; + Ok(Some(price)) + } + None => Ok(None), + } + } } impl StateReadExt for T {} @@ -193,6 +220,18 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_raw(NEXT_CURRENCY_PAIR_ID_KEY.to_string(), bytes); Ok(()) } + + #[instrument(skip(self))] + fn put_price_for_currency_pair( + &mut self, + currency_pair: &CurrencyPair, + price: QuotePrice, + ) -> Result<()> { + // TODO: also `put_currency_pair` if it didn't exist? + let bytes = serde_json::to_vec(&price).context("failed to serialize price")?; + self.put_raw(currency_pair_price_storage_key(currency_pair), bytes); + Ok(()) + } } impl StateWriteExt for T {} From f197a1752ac75e15107684e354d20d72a4820c3b Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 15:34:39 -0400 Subject: [PATCH 16/89] implement oracle module query service --- crates/astria-sequencer/src/grpc/slinky.rs | 160 ++++++++++++++++-- crates/astria-sequencer/src/sequencer.rs | 7 +- .../src/slinky/oracle/component.rs | 5 +- .../src/slinky/oracle/state_ext.rs | 52 +++++- 4 files changed, 206 insertions(+), 18 deletions(-) diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index 14751db71e..1249ea4b28 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -1,15 +1,34 @@ -use std::sync::Arc; - -use astria_core::generated::slinky::marketmap::v1::{ - query_server::Query as MarketMapQueryService, - LastUpdatedRequest, - LastUpdatedResponse, - MarketMapRequest, - MarketMapResponse, - MarketRequest, - MarketResponse, - ParamsRequest, - ParamsResponse, +use std::{ + str::FromStr, + sync::Arc, +}; + +use astria_core::{ + generated::slinky::{ + marketmap::v1::{ + query_server::Query as MarketMapQueryService, + LastUpdatedRequest, + LastUpdatedResponse, + MarketMapRequest, + MarketMapResponse, + MarketRequest, + MarketResponse, + ParamsRequest, + ParamsResponse, + }, + oracle::v1::{ + query_server::Query as OracleService, + GetAllCurrencyPairsRequest, + GetAllCurrencyPairsResponse, + GetCurrencyPairMappingRequest, + GetCurrencyPairMappingResponse, + GetPriceRequest, + GetPriceResponse, + GetPricesRequest, + GetPricesResponse, + }, + }, + slinky::types::v1::CurrencyPair, }; use cnidarium::Storage; use tonic::{ @@ -20,7 +39,10 @@ use tonic::{ use tracing::instrument; use crate::{ - slinky::marketmap::state_ext::StateReadExt as _, + slinky::{ + marketmap::state_ext::StateReadExt as _, + oracle::state_ext::StateReadExt as _, + }, state_ext::StateReadExt as _, }; @@ -111,3 +133,115 @@ impl MarketMapQueryService for SequencerServer { })) } } + +#[async_trait::async_trait] +impl OracleService for SequencerServer { + #[instrument(skip_all)] + async fn get_all_currency_pairs( + self: Arc, + _request: Request, + ) -> Result, Status> { + let snapshot = self.storage.latest_snapshot(); + let currency_pairs = snapshot.get_all_currency_pairs().await.map_err(|e| { + Status::internal(format!( + "failed to get all currency pairs from storage: {e}" + )) + })?; + Ok(Response::new(GetAllCurrencyPairsResponse { + currency_pairs: currency_pairs + .into_iter() + .map(CurrencyPair::into_raw) + .collect(), + })) + } + + #[instrument(skip_all)] + async fn get_price( + self: Arc, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + let Some(currency_pair) = request.currency_pair else { + return Err(Status::invalid_argument("currency pair is required")); + }; + let currency_pair = CurrencyPair::from_raw(currency_pair); + let snapshot = self.storage.latest_snapshot(); + let Some(state) = snapshot + .get_currency_pair_state(¤cy_pair) + .await + .map_err(|e| Status::internal(format!("failed to get state from storage: {e}")))? + else { + return Err(Status::not_found("currency pair state not found")); + }; + + Ok(Response::new(GetPriceResponse { + price: Some(state.price.into_raw()), + nonce: state.nonce, + id: state.id, + decimals: 0, // TODO: where to get this? + })) + } + + #[instrument(skip_all)] + async fn get_prices( + self: Arc, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + let currency_pairs = match request + .currency_pair_ids + .into_iter() + .map(|s| CurrencyPair::from_str(&s)) + .collect::, _>>() + { + Ok(currency_pairs) => currency_pairs, + Err(e) => { + return Err(Status::invalid_argument(format!( + "invalid currency pair id: {e}" + ))); + } + }; + + let snapshot = self.storage.latest_snapshot(); + let mut prices = Vec::new(); + for currency_pair in currency_pairs { + let Some(state) = snapshot + .get_currency_pair_state(¤cy_pair) + .await + .map_err(|e| Status::internal(format!("failed to get state from storage: {e}")))? + else { + return Err(Status::not_found("currency pair state not found")); + }; + prices.push(GetPriceResponse { + price: Some(state.price.into_raw()), + nonce: state.nonce, + id: state.id, + decimals: 0, // TODO: where to get this? + }) + } + Ok(Response::new(GetPricesResponse { + prices, + })) + } + + #[instrument(skip_all)] + async fn get_currency_pair_mapping( + self: Arc, + _request: Request, + ) -> Result, Status> { + let snapshot = self.storage.latest_snapshot(); + let currency_pair_mapping = snapshot.get_currency_pair_mapping().await.map_err(|e| { + Status::internal(format!( + "failed to get currency pair mapping from storage: {e}" + )) + })?; + let currency_pair_mapping = currency_pair_mapping + .into_iter() + .map(|(k, v)| (k, v.into_raw())) + .collect(); + + Ok(Response::new(GetCurrencyPairMappingResponse { + currency_pair_mapping, + })) + } +} diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index 4dbb3f9424..fff26467b4 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -9,6 +9,7 @@ use astria_core::generated::{ sequencerblock::v1alpha1::sequencer_service_server::SequencerServiceServer, slinky::{ marketmap::v1::query_server::QueryServer as MarketMapQueryServer, + oracle::v1::query_server::QueryServer as OracleQueryServer, service::v1::{ oracle_client::OracleClient, QueryPricesRequest, @@ -226,7 +227,8 @@ fn start_grpc_server( let ibc = penumbra_ibc::component::rpc::IbcQuery::::new(storage.clone()); let sequencer_api = SequencerServer::new(storage.clone(), mempool); - let slinky_api = crate::grpc::slinky::SequencerServer::new(storage.clone()); + let market_map_api = crate::grpc::slinky::SequencerServer::new(storage.clone()); + let oracle_api = crate::grpc::slinky::SequencerServer::new(storage.clone()); let cors_layer: CorsLayer = CorsLayer::permissive(); // TODO: setup HTTPS? @@ -251,7 +253,8 @@ fn start_grpc_server( .add_service(ChannelQueryServer::new(ibc.clone())) .add_service(ConnectionQueryServer::new(ibc.clone())) .add_service(SequencerServiceServer::new(sequencer_api)) - .add_service(MarketMapQueryServer::new(slinky_api)); + .add_service(MarketMapQueryServer::new(market_map_api)) + .add_service(OracleQueryServer::new(oracle_api)); info!(grpc_addr = grpc_addr.to_string(), "starting grpc server"); tokio::task::spawn( diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index 50911f45c3..161e0098fb 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -31,7 +31,10 @@ impl Component for OracleComponent { price: currency_pair.currency_pair_price().clone(), }; state - .put_currency_pair_state(currency_pair.currency_pair(), currency_pair_state) + .put_currency_pair_state_and_price( + currency_pair.currency_pair(), + currency_pair_state, + ) .context("failed to put currency pair")?; } diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs index eac5bdaa86..fbe3290614 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs @@ -1,4 +1,7 @@ +use std::collections::HashMap; + use anyhow::{ + bail, Context, Result, }; @@ -18,6 +21,7 @@ use cnidarium::{ StateRead, StateWrite, }; +use futures::StreamExt as _; use tracing::instrument; const CURRENCY_PAIR_TO_ID_PREFIX: &str = "oraclecpid"; @@ -63,7 +67,7 @@ pub(crate) trait StateReadExt: StateRead { .await .context("failed reading currency pair id from state")? else { - return Ok(0); + bail!("currency pair not found in state") }; let Id(id) = Id::try_from_slice(&bytes).context("invalid currency pair id bytes")?; Ok(id) @@ -85,6 +89,32 @@ pub(crate) trait StateReadExt: StateRead { } } + #[instrument(skip(self))] + async fn get_currency_pair_mapping(&self) -> Result> { + let prefix = format!("{CURRENCY_PAIR_TO_ID_PREFIX}/"); + let mut currency_pairs = HashMap::new(); + + let mut stream = std::pin::pin!(self.prefix_keys(&prefix)); + while let Some(Ok(key)) = stream.next().await { + let Some(bytes) = self + .get_raw(&key) + .await + .context("failed reading currency pair id from state")? + else { + bail!("currency pair not found in state; this is a bug") + }; + let Id(id) = Id::try_from_slice(&bytes).context("invalid currency pair id bytes")?; + + let currency_pair = key + .strip_prefix(&prefix) + .context("failed to strip prefix from currency pair state key")? + .parse::() + .context("failed to parse storage key suffix as currency pair")?; + currency_pairs.insert(id, currency_pair); + } + Ok(currency_pairs) + } + #[instrument(skip(self))] async fn get_num_currency_pairs(&self) -> Result { let Some(bytes) = self @@ -132,6 +162,23 @@ pub(crate) trait StateReadExt: StateRead { } } + #[instrument(skip(self))] + async fn get_all_currency_pairs(&self) -> Result> { + let prefix = format!("{CURRENCY_PAIR_STATE_PREFIX}/"); + let mut currency_pairs: Vec = Vec::new(); + + let mut stream = std::pin::pin!(self.prefix_keys(&prefix)); + while let Some(Ok(key)) = stream.next().await { + let currency_pair = key + .strip_prefix(&prefix) + .context("failed to strip prefix from currency pair state key")? + .parse::() + .context("failed to parse storage key suffix as currency pair")?; + currency_pairs.push(currency_pair); + } + Ok(currency_pairs) + } + #[instrument(skip(self))] async fn get_next_currency_pair_id(&self) -> Result { let Some(bytes) = self @@ -202,7 +249,7 @@ pub(crate) trait StateWriteExt: StateWrite { } #[instrument(skip(self))] - fn put_currency_pair_state( + fn put_currency_pair_state_and_price( &mut self, currency_pair: &CurrencyPair, currency_pair_state: CurrencyPairState, @@ -210,6 +257,7 @@ pub(crate) trait StateWriteExt: StateWrite { let bytes = serde_json::to_vec(¤cy_pair_state) .context("failed to serialize currency pair state")?; self.put_raw(currency_pair_state_storage_key(currency_pair), bytes); + self.put_price_for_currency_pair(currency_pair, currency_pair_state.price)?; Ok(()) } From 6b84a8a40f0f09e3cc321eed742d6b464bad174f Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 16:27:18 -0400 Subject: [PATCH 17/89] fixes based on running w sidecar, update genesis example to have oracle --- Cargo.lock | 1 + crates/astria-sequencer-utils/Cargo.toml | 1 + .../src/genesis_example.rs | 98 +++++++++++++------ crates/astria-sequencer/src/app/mod.rs | 37 +++++-- .../src/app/vote_extension.rs | 44 ++++++--- crates/astria-sequencer/src/grpc/slinky.rs | 4 +- crates/astria-sequencer/src/sequencer.rs | 12 ++- .../astria-sequencer/src/service/consensus.rs | 45 ++++++--- .../src/slinky/oracle/component.rs | 4 + .../src/slinky/oracle/state_ext.rs | 4 + 10 files changed, 178 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10fd74d684..d0659cf15d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,6 +899,7 @@ dependencies = [ "hex", "indenter", "itertools 0.12.1", + "pbjson-types", "predicates", "prost", "rlp", diff --git a/crates/astria-sequencer-utils/Cargo.toml b/crates/astria-sequencer-utils/Cargo.toml index 1317e08d62..6e8d45a970 100644 --- a/crates/astria-sequencer-utils/Cargo.toml +++ b/crates/astria-sequencer-utils/Cargo.toml @@ -21,6 +21,7 @@ ethers-core = "2.0.14" hex = { workspace = true } indenter = "0.3.3" itertools = { workspace = true } +pbjson-types = { workspace = true } prost = { workspace = true } rlp = "0.5.2" serde = { workspace = true } diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 166cff3a42..49f376ac70 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -21,7 +21,10 @@ use astria_core::{ Params, ProviderConfig, }, - oracle::v1::GenesisState as OracleGenesisState, + oracle::v1::{ + GenesisState as OracleGenesisState, + QuotePrice, + }, types::v1::CurrencyPair, }, }; @@ -57,45 +60,51 @@ fn charlie() -> Address { } fn genesis_state() -> GenesisState { - use astria_core::slinky::market_map::v1::{ - Market, - Ticker, + use astria_core::slinky::{ + market_map::v1::{ + Market, + Ticker, + }, + oracle::v1::CurrencyPairGenesis, }; let mut markets = std::collections::HashMap::new(); markets.insert( - "BITCOIN/USD".to_string(), + "BTC/USD".to_string(), Market { ticker: Ticker { - currency_pair: CurrencyPair::new("BITCOIN".to_string(), "USD".to_string()), + currency_pair: CurrencyPair::new("BTC".to_string(), "USD".to_string()), decimals: 8, min_provider_count: 3, enabled: true, metadata_json: "".to_string(), }, - provider_configs: vec![ - ProviderConfig { - name: "kucoin_ws".to_string(), - off_chain_ticker: "btc_usd".to_string(), - normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), - invert: false, - metadata_json: "".to_string(), - }, - ProviderConfig { - name: "binance".to_string(), - off_chain_ticker: "BTCUSD".to_string(), - normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), - invert: false, - metadata_json: "".to_string(), - }, - ProviderConfig { - name: "mexc".to_string(), - off_chain_ticker: "btc-usd".to_string(), - normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), - invert: false, - metadata_json: "".to_string(), - }, - ], + provider_configs: vec![ProviderConfig { + name: "coingecko_api".to_string(), + off_chain_ticker: "bitcoin/usd".to_string(), + normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), + invert: false, + metadata_json: "".to_string(), + }], + }, + ); + markets.insert( + "ETH/USD".to_string(), + Market { + ticker: Ticker { + currency_pair: CurrencyPair::new("ETH".to_string(), "USD".to_string()), + decimals: 8, + min_provider_count: 3, + enabled: true, + metadata_json: "".to_string(), + }, + provider_configs: vec![ProviderConfig { + name: "coingecko_api".to_string(), + off_chain_ticker: "ethereum/usd".to_string(), + normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), + invert: false, + metadata_json: "".to_string(), + }], }, ); UncheckedGenesisState { @@ -146,8 +155,35 @@ fn genesis_state() -> GenesisState { }, }, oracle: OracleGenesisState { - currency_pair_genesis: vec![], - next_id: 0, + currency_pair_genesis: vec![ + CurrencyPairGenesis { + id: 0, + nonce: 0, + currency_pair_price: QuotePrice { + price: 5834065777, + block_height: 0, + block_timestamp: pbjson_types::Timestamp { + seconds: 1720122395, + nanos: 0, + }, + }, + currency_pair: CurrencyPair::new("BTC".to_string(), "USD".to_string()), + }, + CurrencyPairGenesis { + id: 1, + nonce: 0, + currency_pair_price: QuotePrice { + price: 3138872234, + block_height: 0, + block_timestamp: pbjson_types::Timestamp { + seconds: 1720122395, + nanos: 0, + }, + }, + currency_pair: CurrencyPair::new("ETH".to_string(), "USD".to_string()), + }, + ], + next_id: 2, }, } .try_into() diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index cf3ee1e20a..e83f2e330a 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -60,6 +60,7 @@ use tracing::{ debug, info, instrument, + warn, }; use crate::{ @@ -314,27 +315,45 @@ impl App { anyhow::bail!("local last commit is empty; this should not occur") }; - // TODO: if this fails, we shouldn't return an error, but instead leave + // if this fails, we shouldn't return an error, but instead leave // the vote extensions empty in this block for liveness. // it's not a critical error if the oracle values are not updated for a block. - let extended_commit_info = ProposalHandler::prune_and_validate_extended_commit_info( + let round = last_commit.round; + let extended_commit_info = match ProposalHandler::prune_and_validate_extended_commit_info( &self.state, prepare_proposal.height.into(), last_commit, ) .await - .context("failed to prune and validate extended commit info")?; + { + Ok(info) => info, + Err(e) => { + debug!( + error = AsRef::::as_ref(&e), + "failed to generate extended commit info" + ); + tendermint::abci::types::ExtendedCommitInfo { + round, + votes: Vec::new(), + } + } + }; - let extended_commit_info_bytes = + let mut extended_commit_info_bytes = ExtendedCommitInfo::from(extended_commit_info).encode_to_vec(); let max_tx_bytes = usize::try_from(prepare_proposal.max_tx_bytes) .context("failed to convert max_tx_bytes to usize")?; - // TODO: just zero this if it's too large - ensure!( - extended_commit_info_bytes.len() <= max_tx_bytes, - "extended commit info is too large to fit in block" - ); + // zero the commit info if it's too large to fit in the block + // for liveness. + if extended_commit_info_bytes.len() > max_tx_bytes { + warn!( + extended_commit_info_bytes_len = extended_commit_info_bytes.len(), + max_tx_bytes, + "extended commit info is too large to fit in block; not including in block" + ); + extended_commit_info_bytes = Vec::new(); + } // adjust max block size to account for extended commit info let mut block_size_constraints = diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 49c130e62f..d1d4d88be3 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -34,7 +34,10 @@ use tendermint::{ }; use tendermint_proto::google::protobuf::Timestamp; use tonic::transport::Channel; -use tracing::debug; +use tracing::{ + debug, + trace, +}; use crate::{ authority::state_ext::StateReadExt as _, @@ -69,7 +72,6 @@ impl Handler { &mut self, state: &S, ) -> anyhow::Result { - tracing::info!("extending vote"); let Some(oracle_client) = self.oracle_client.as_mut() else { // we allow validators to *not* use the oracle sidecar currently // however, if >1/3 of validators are not using the oracle, the prices will not update. @@ -78,8 +80,6 @@ impl Handler { }); }; - tracing::info!("extending vote; getting prices from oracle sidecar"); - // if we fail to get prices within the timeout duration, we will return an empty vote // extension to ensure liveness. let prices = match tokio::time::timeout( @@ -109,7 +109,10 @@ impl Handler { } }; - tracing::info!(prices = ?prices, "got prices from oracle sidecar; transforming prices"); + tracing::debug!( + prices_count = prices.prices.len(), + "got prices from oracle sidecar; transforming prices" + ); let oracle_vote_extension = transform_oracle_service_prices(state, prices) .await @@ -180,18 +183,25 @@ async fn transform_oracle_service_prices( .parse() .context("failed to parse currency pair")?; - // TODO: how are the prices encoded into strings in the sidecar?? - let encoded_price = price_string.as_bytes(); - let price = - DefaultCurrencyPairStrategy::get_decoded_price(state, ¤cy_pair, encoded_price) - .await - .context("failed to get decoded price")?; + // prices are encoded as just a decimal string in the sidecar response + let price: u128 = price_string + .parse() + .context("failed to parse price string")?; - let id = DefaultCurrencyPairStrategy::id(state, ¤cy_pair) - .await - .context("failed to get id for currency pair")?; + let Ok(id) = DefaultCurrencyPairStrategy::id(state, ¤cy_pair).await else { + trace!( + currency_pair = currency_pair.to_string(), + "currency pair not found in state; skipping" + ); + continue; + }; let encoded_price = DefaultCurrencyPairStrategy::get_encoded_price(state, ¤cy_pair, price).await; + + debug!( + currency_pair = currency_pair.to_string(), + id, price, "transformed price for inclusion in vote extension" + ); strategy_prices.insert(id, encoded_price); } @@ -467,6 +477,12 @@ pub(crate) async fn apply_prices_from_vote_extensions( block_height: height, }; + tracing::debug!( + currency_pair = currency_pair.to_string(), + price = price.price, + "applied price from vote extension" + ); + state .put_price_for_currency_pair(¤cy_pair, price) .context("failed to put price")?; diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index 1249ea4b28..c09737b496 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -178,7 +178,7 @@ impl OracleService for SequencerServer { price: Some(state.price.into_raw()), nonce: state.nonce, id: state.id, - decimals: 0, // TODO: where to get this? + decimals: 0, // TODO: get this from the marketmap })) } @@ -216,7 +216,7 @@ impl OracleService for SequencerServer { price: Some(state.price.into_raw()), nonce: state.nonce, id: state.id, - decimals: 0, // TODO: where to get this? + decimals: 0, // TODO: get this from the marketmap }) } Ok(Response::new(GetPricesResponse { diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index fff26467b4..36f3efed1b 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -39,9 +39,11 @@ use tonic::transport::{ }; use tower_abci::v038::Server; use tracing::{ + debug, error, info, instrument, + warn, }; use crate::{ @@ -124,11 +126,15 @@ impl Sequencer { // ensure the oracle sidecar is reachable // TODO: allow this to retry in case the oracle sidecar is not ready yet - let prices = oracle_client + if oracle_client .prices(QueryPricesRequest::default()) .await - .context("failed to get oracle prices")?; - info!(prices = ?prices.into_inner(), "oracle sidecar is reachable"); + .is_err() + { + warn!(uri = %uri, "oracle sidecar is unreachable"); + } else { + debug!(uri = %uri, "oracle sidecar is reachable"); + }; Some(oracle_client) } else { None diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index ca8fb043f4..55c683dc58 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -269,6 +269,10 @@ mod test { use prost::Message as _; use rand::rngs::OsRng; use tendermint::{ + abci::types::{ + CommitInfo, + ExtendedCommitInfo, + }, account::Id, Hash, Time, @@ -304,7 +308,10 @@ mod test { request::PrepareProposal { txs: vec![], max_tx_bytes: 1024, - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + }), misbehavior: vec![], height: 1u32.into(), time: Time::now(), @@ -314,9 +321,21 @@ mod test { } fn new_process_proposal_request(txs: Vec) -> request::ProcessProposal { + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let bytes = extended_commit_info.encode_to_vec(); + let mut txs_with_commit_info = vec![bytes.into()]; + txs_with_commit_info.extend(txs); + request::ProcessProposal { - txs, - proposed_last_commit: None, + txs: txs_with_commit_info, + proposed_last_commit: Some(CommitInfo { + round: 0u16.into(), + votes: vec![], + }), misbehavior: vec![], hash: Hash::default(), height: 1u32.into(), @@ -344,10 +363,12 @@ mod test { .handle_prepare_proposal(prepare_proposal) .await .unwrap(); + let mut expected_txs = vec![b"".to_vec().into()]; + expected_txs.extend(res.into_transactions(txs)); assert_eq!( prepare_proposal_response, response::PrepareProposal { - txs: res.into_transactions(txs) + txs: expected_txs, } ); @@ -396,15 +417,13 @@ mod test { async fn process_proposal_fail_wrong_commitment_length() { let (mut consensus_service, _) = new_consensus_service(None).await; let process_proposal = new_process_proposal_request(vec![[0u8; 16].to_vec().into()]); - assert!( - consensus_service - .handle_process_proposal(process_proposal) - .await - .err() - .unwrap() - .to_string() - .contains("transaction commitment must be 32 bytes") - ); + let err = consensus_service + .handle_process_proposal(process_proposal) + .await + .err() + .unwrap() + .to_string(); + assert!(err.contains("transaction commitment must be 32 bytes")); } #[tokio::test] diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index 161e0098fb..0b9ee60911 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -36,6 +36,10 @@ impl Component for OracleComponent { currency_pair_state, ) .context("failed to put currency pair")?; + tracing::info!( + "put currency pair: {}", + currency_pair.currency_pair().to_string() + ); } state diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs index fbe3290614..314ad9f66d 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs @@ -258,6 +258,10 @@ pub(crate) trait StateWriteExt: StateWrite { .context("failed to serialize currency pair state")?; self.put_raw(currency_pair_state_storage_key(currency_pair), bytes); self.put_price_for_currency_pair(currency_pair, currency_pair_state.price)?; + self.put_currency_pair_id(currency_pair, currency_pair_state.id) + .context("failed to put currency pair id")?; + self.put_currency_pair(currency_pair_state.id, currency_pair.clone()) + .context("failed to put currency pair")?; Ok(()) } From 25e024b34268dcab87359e24663d118ed4ef3bf9 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 22:16:04 -0400 Subject: [PATCH 18/89] fix sequencer unit tests --- crates/astria-core/src/protocol/test_utils.rs | 9 +++ .../src/sequencerblock/v1alpha1/block.rs | 7 +- crates/astria-core/src/slinky.rs | 8 +-- ..._changes__app_finalize_block_snapshot.snap | 62 ++++++++-------- crates/astria-sequencer/src/app/tests_app.rs | 70 +++++++++++++++---- .../src/app/tests_breaking_changes.rs | 13 +++- crates/astria-sequencer/src/grpc/sequencer.rs | 2 +- .../astria-sequencer/src/service/consensus.rs | 12 ++-- 8 files changed, 125 insertions(+), 58 deletions(-) diff --git a/crates/astria-core/src/protocol/test_utils.rs b/crates/astria-core/src/protocol/test_utils.rs index ff7f325fd1..6b0a0ea4b3 100644 --- a/crates/astria-core/src/protocol/test_utils.rs +++ b/crates/astria-core/src/protocol/test_utils.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use prost::Message as _; +use tendermint::abci::types::ExtendedCommitInfo; use super::{ group_sequence_actions_in_signed_transaction_transactions_by_rollup_id, @@ -137,6 +138,13 @@ impl ConfigureSequencerBlock { rollup_transactions.sort_unstable_keys(); let rollup_transactions_tree = derive_merkle_tree_from_rollup_txs(&rollup_transactions); + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let extended_commit_info_bytes = extended_commit_info.encode_to_vec(); + let rollup_ids_root = merkle::Tree::from_leaves( rollup_transactions .keys() @@ -144,6 +152,7 @@ impl ConfigureSequencerBlock { ) .root(); let mut data = vec![ + extended_commit_info_bytes, rollup_transactions_tree.root().to_vec(), rollup_ids_root.to_vec(), ]; diff --git a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs index 48a2745eac..1df23fb782 100644 --- a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs @@ -808,13 +808,14 @@ impl SequencerBlock { } rollup_transactions.sort_unstable_keys(); - // action tree root is always the first tx in a block - let rollup_transactions_proof = tree.construct_proof(0).expect( + // action tree root is always the second tx in a block + let rollup_transactions_proof = tree.construct_proof(1).expect( "the tree has at least one leaf; if this line is reached and `construct_proof` \ returns None it means that the short circuiting checks above it have been removed", ); - let rollup_ids_proof = tree.construct_proof(1).expect( + // rollup id tree root is always the third tx in a block + let rollup_ids_proof = tree.construct_proof(2).expect( "the tree has at least two leaves; if this line is reached and `construct_proof` \ returns None it means that the short circuiting checks above it have been removed", ); diff --git a/crates/astria-core/src/slinky.rs b/crates/astria-core/src/slinky.rs index de1200d9fb..601bb3902f 100644 --- a/crates/astria-core/src/slinky.rs +++ b/crates/astria-core/src/slinky.rs @@ -629,10 +629,10 @@ pub mod oracle { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone)] pub struct CurrencyPairGenesis { - currency_pair: CurrencyPair, - currency_pair_price: QuotePrice, - id: u64, - nonce: u64, + pub currency_pair: CurrencyPair, + pub currency_pair_price: QuotePrice, + pub id: u64, + pub nonce: u64, } impl CurrencyPairGenesis { diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap index 10baa0c334..d922b4f7b0 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 168, - 40, - 26, - 85, - 142, - 62, - 98, - 97, - 65, - 205, - 249, - 210, - 237, - 174, - 132, - 98, - 101, - 27, - 175, + 24, + 135, 0, - 25, - 191, - 31, - 107, - 175, - 139, - 95, - 221, - 223, - 122, - 255, - 48 + 209, + 118, + 16, + 193, + 19, + 235, + 143, + 74, + 214, + 105, + 232, + 112, + 16, + 11, + 231, + 64, + 165, + 3, + 36, + 192, + 82, + 93, + 161, + 53, + 100, + 216, + 250, + 205, + 116 ] diff --git a/crates/astria-sequencer/src/app/tests_app.rs b/crates/astria-sequencer/src/app/tests_app.rs index 0fcbf541a0..cc68859d32 100644 --- a/crates/astria-sequencer/src/app/tests_app.rs +++ b/crates/astria-sequencer/src/app/tests_app.rs @@ -20,7 +20,10 @@ use tendermint::{ abci::{ self, request::PrepareProposal, - types::CommitInfo, + types::{ + CommitInfo, + ExtendedCommitInfo, + }, }, account, block::{ @@ -250,6 +253,15 @@ async fn app_transfer_block_fees_to_sudo() { let proposer_address: tendermint::account::Id = [99u8; 20].to_vec().try_into().unwrap(); let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], HashMap::new()); + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let bytes = extended_commit_info.encode_to_vec(); + let mut txs_with_commit_info = vec![bytes.into()]; + txs_with_commit_info + .extend(commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()])); let finalize_block = abci::request::FinalizeBlock { hash: Hash::try_from([0u8; 32].to_vec()).unwrap(), @@ -257,7 +269,7 @@ async fn app_transfer_block_fees_to_sudo() { time: Time::now(), next_validators_hash: Hash::default(), proposer_address, - txs: commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()]), + txs: txs_with_commit_info, decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -338,6 +350,15 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { ); let deposits = HashMap::from_iter(vec![(rollup_id, vec![expected_deposit.clone()])]); let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], deposits.clone()); + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let bytes = extended_commit_info.encode_to_vec(); + let mut txs_with_commit_info = vec![bytes.into()]; + txs_with_commit_info + .extend(commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()])); let finalize_block = abci::request::FinalizeBlock { hash: Hash::try_from([0u8; 32].to_vec()).unwrap(), @@ -345,7 +366,7 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()]), + txs: txs_with_commit_info, decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -428,6 +449,15 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { ); let deposits = HashMap::from_iter(vec![(rollup_id, vec![expected_deposit.clone()])]); let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], deposits.clone()); + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let bytes = extended_commit_info.encode_to_vec(); + let mut txs_with_commit_info = vec![bytes.into()]; + txs_with_commit_info + .extend(commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()])); let timestamp = Time::now(); let block_hash = Hash::try_from([99u8; 32].to_vec()).unwrap(); @@ -437,7 +467,7 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { time: timestamp, next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()]), + txs: txs_with_commit_info, decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -465,7 +495,10 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { proposer_address, txs: vec![], max_tx_bytes: 1_000_000, - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; @@ -486,7 +519,10 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), txs: finalize_block.txs.clone(), - proposed_last_commit: None, + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; @@ -571,7 +607,10 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 200_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: Height::default(), time: Time::now(), @@ -587,9 +626,9 @@ async fn app_prepare_proposal_cometbft_max_bytes_overflow_ok() { // see only first tx made it in assert_eq!( result.txs.len(), - 3, - "total transaction length should be three, including the two commitments and the one tx \ - that fit" + 4, + "total transaction length should be four, including the extended commit info, two \ + commitments and the one tx that fit" ); assert_eq!( app.mempool.len().await, @@ -644,7 +683,10 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 600_000, // make large enough to overflow sequencer bytes first txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: Height::default(), time: Time::now(), @@ -660,9 +702,9 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { // see only first tx made it in assert_eq!( result.txs.len(), - 3, - "total transaction length should be three, including the two commitments and the one tx \ - that fit" + 4, + "total transaction length should be three, including the extended commit info, two \ + commitments and the one tx that fit" ); assert_eq!( app.mempool.len().await, diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 5c638969af..9a01d99aa3 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -165,6 +165,17 @@ async fn app_finalize_block_snapshot() { let deposits = HashMap::from_iter(vec![(rollup_id, vec![expected_deposit.clone()])]); let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], deposits.clone()); + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = + tendermint::abci::types::ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let bytes = extended_commit_info.encode_to_vec(); + let mut txs_with_commit_info = vec![bytes.into()]; + txs_with_commit_info + .extend(commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()])); + let timestamp = Time::unix_epoch(); let block_hash = Hash::try_from([99u8; 32].to_vec()).unwrap(); let finalize_block = abci::request::FinalizeBlock { @@ -173,7 +184,7 @@ async fn app_finalize_block_snapshot() { time: timestamp, next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: commitments.into_transactions(vec![signed_tx.to_raw().encode_to_vec().into()]), + txs: txs_with_commit_info, decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), diff --git a/crates/astria-sequencer/src/grpc/sequencer.rs b/crates/astria-sequencer/src/grpc/sequencer.rs index f8fb17e0c4..75157daef1 100644 --- a/crates/astria-sequencer/src/grpc/sequencer.rs +++ b/crates/astria-sequencer/src/grpc/sequencer.rs @@ -233,7 +233,7 @@ mod test { } #[tokio::test] - async fn test_get_sequencer_block() { + async fn get_sequencer_block_ok() { let block = make_test_sequencer_block(1); let storage = cnidarium::TempStorage::new().await.unwrap(); let mempool = Mempool::new(); diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 55c683dc58..91ab9e1952 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -364,7 +364,8 @@ mod test { .await .unwrap(); let mut expected_txs = vec![b"".to_vec().into()]; - expected_txs.extend(res.into_transactions(txs)); + let commitments_and_txs = res.into_transactions(txs); + expected_txs.extend(commitments_and_txs.clone()); assert_eq!( prepare_proposal_response, response::PrepareProposal { @@ -374,7 +375,7 @@ mod test { let (mut consensus_service, _) = new_consensus_service(Some(signing_key.verification_key())).await; - let process_proposal = new_process_proposal_request(prepare_proposal_response.txs); + let process_proposal = new_process_proposal_request(commitments_and_txs); consensus_service .handle_process_proposal(process_proposal) .await @@ -455,10 +456,12 @@ mod test { .handle_prepare_proposal(prepare_proposal) .await .unwrap(); + let mut expected_txs = vec![b"".to_vec().into()]; + expected_txs.extend(res.into_transactions(vec![])); assert_eq!( prepare_proposal_response, response::PrepareProposal { - txs: res.into_transactions(vec![]), + txs: expected_txs, } ); } @@ -594,6 +597,7 @@ mod test { header.data_hash = Some(Hash::try_from(data_hash.to_vec()).unwrap()); let process_proposal = new_process_proposal_request(block_data.clone()); + let txs = process_proposal.txs.clone(); consensus_service .handle_request(ConsensusRequest::ProcessProposal(process_proposal)) .await @@ -611,7 +615,7 @@ mod test { votes: vec![], }, misbehavior: vec![], - txs: block_data, + txs, }; consensus_service .handle_request(ConsensusRequest::FinalizeBlock(finalize_block)) From bb1a9750f639935b6e61f0080caac78a1819531b Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 22:45:27 -0400 Subject: [PATCH 19/89] clippy --- crates/astria-core/src/sequencer.rs | 2 +- crates/astria-core/src/slinky.rs | 87 ++++++++++++++++++- .../src/genesis_example.rs | 18 ++-- .../src/genesis_parser.rs | 3 + .../astria-sequencer/app-genesis-state.json | 67 ++++++++++---- crates/astria-sequencer/src/app/mod.rs | 12 ++- .../src/app/vote_extension.rs | 31 +++++-- crates/astria-sequencer/src/config.rs | 2 +- crates/astria-sequencer/src/grpc/slinky.rs | 2 +- .../slinky/oracle/currency_pair_strategy.rs | 14 +-- 10 files changed, 191 insertions(+), 47 deletions(-) diff --git a/crates/astria-core/src/sequencer.rs b/crates/astria-core/src/sequencer.rs index 5ddc498697..58aed5fdb0 100644 --- a/crates/astria-core/src/sequencer.rs +++ b/crates/astria-core/src/sequencer.rs @@ -211,7 +211,7 @@ impl UncheckedGenesisState { } self.ensure_address_has_base_prefix( &self.market_map.params.admin, - &format!(".market_map.params.admin"), + ".market_map.params.admin", )?; Ok(()) } diff --git a/crates/astria-core/src/slinky.rs b/crates/astria-core/src/slinky.rs index 601bb3902f..1c229c6b7e 100644 --- a/crates/astria-core/src/slinky.rs +++ b/crates/astria-core/src/slinky.rs @@ -113,6 +113,7 @@ pub mod types { } impl CurrencyPairParseError { + #[must_use] pub fn invalid_currency_pair_string(s: &str) -> Self { Self(CurrencyPairParseErrorKind::InvalidCurrencyPairString( s.to_string(), @@ -147,6 +148,14 @@ pub mod market_map { } impl GenesisState { + /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. + /// + /// # Errors + /// + /// - if the `market_map` field is missing + /// - if the `market_map` field is invalid + /// - if the `params` field is missing + /// - if the `params` field is invalid pub fn try_from_raw(raw: raw::GenesisState) -> Result { let Some(market_map) = raw .market_map @@ -187,18 +196,22 @@ pub mod market_map { pub struct GenesisStateError(GenesisStateErrorKind); impl GenesisStateError { + #[must_use] pub fn missing_market_map() -> Self { Self(GenesisStateErrorKind::MissingMarketMap) } + #[must_use] pub fn invalid_market_map(err: MarketMapError) -> Self { Self(GenesisStateErrorKind::MarketMapParseError(err)) } + #[must_use] pub fn missing_params() -> Self { Self(GenesisStateErrorKind::MissingParams) } + #[must_use] pub fn invalid_params(err: ParamsError) -> Self { Self(GenesisStateErrorKind::ParamsParseError(err)) } @@ -224,6 +237,12 @@ pub mod market_map { } impl Params { + /// Converts from a raw protobuf `Params` to a native `Params`. + /// + /// # Errors + /// + /// - if any of the `market_authorities` addresses are invalid + /// - if the `admin` address is invalid pub fn try_from_raw(raw: raw::Params) -> Result { let market_authorities = raw .market_authorities @@ -256,10 +275,12 @@ pub mod market_map { pub struct ParamsError(ParamsErrorKind); impl ParamsError { + #[must_use] pub fn market_authority_parse_error(err: AddressError) -> Self { Self(ParamsErrorKind::MarketAuthorityParseError(err)) } + #[must_use] pub fn admin_parse_error(err: AddressError) -> Self { Self(ParamsErrorKind::AdminParseError(err)) } @@ -281,6 +302,13 @@ pub mod market_map { } impl Market { + /// Converts from a raw protobuf `Market` to a native `Market`. + /// + /// # Errors + /// + /// - if the `ticker` field is missing + /// - if the `ticker` field is invalid + /// - if any of the `provider_configs` are invalid pub fn try_from_raw(raw: raw::Market) -> Result { let Some(ticker) = raw .ticker @@ -321,14 +349,17 @@ pub mod market_map { pub struct MarketError(MarketErrorKind); impl MarketError { + #[must_use] pub fn missing_ticker() -> Self { Self(MarketErrorKind::MissingTicker) } + #[must_use] pub fn invalid_ticker(err: TickerError) -> Self { Self(MarketErrorKind::TickerParseError(err)) } + #[must_use] pub fn invalid_provider_config(err: ProviderConfigError) -> Self { Self(MarketErrorKind::ProviderConfigParseError(err)) } @@ -355,6 +386,12 @@ pub mod market_map { } impl Ticker { + /// Converts from a raw protobuf `Ticker` to a native `Ticker`. + /// + /// # Errors + /// + /// - if the `currency_pair` field is missing + /// - if the `currency_pair` field is invalid pub fn try_from_raw(raw: raw::Ticker) -> Result { let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { return Err(TickerError::missing_currency_pair()); @@ -385,6 +422,7 @@ pub mod market_map { pub struct TickerError(TickerErrorKind); impl TickerError { + #[must_use] pub fn missing_currency_pair() -> Self { Self(TickerErrorKind::MissingCurrencyPair) } @@ -407,6 +445,11 @@ pub mod market_map { } impl ProviderConfig { + /// Converts from a raw protobuf `ProviderConfig` to a native `ProviderConfig`. + /// + /// # Errors + /// + /// - if the `normalize_by_pair` field is missing pub fn try_from_raw(raw: raw::ProviderConfig) -> Result { let Some(normalize_by_pair) = raw.normalize_by_pair.map(CurrencyPair::from_raw) else { @@ -438,6 +481,7 @@ pub mod market_map { pub struct ProviderConfigError(ProviderConfigErrorKind); impl ProviderConfigError { + #[must_use] pub fn missing_normalize_by_pair() -> Self { Self(ProviderConfigErrorKind::MissingNormalizeByPair) } @@ -456,6 +500,12 @@ pub mod market_map { } impl MarketMap { + /// Converts from a raw protobuf `MarketMap` to a native `MarketMap`. + /// + /// # Errors + /// + /// - if any of the markets are invalid + /// - if any of the market names are invalid pub fn try_from_raw(raw: raw::MarketMap) -> Result { let mut markets = HashMap::new(); for (k, v) in raw.markets { @@ -486,6 +536,7 @@ pub mod market_map { pub struct MarketMapError(MarketMapErrorKind); impl MarketMapError { + #[must_use] pub fn invalid_market(name: String, err: MarketError) -> Self { Self(MarketMapErrorKind::InvalidMarket(name, err)) } @@ -517,12 +568,18 @@ pub mod oracle { } impl QuotePrice { + /// Converts from a raw protobuf `QuotePrice` to a native `QuotePrice`. + /// + /// # Errors + /// + /// - if the `price` field is invalid + /// - if the `block_timestamp` field is missing pub fn try_from_raw(raw: raw::QuotePrice) -> Result { let price = raw .price .parse() .map_err(QuotePriceError::price_parse_error)?; - let Some(block_timestamp) = raw.block_timestamp.clone() else { + let Some(block_timestamp) = raw.block_timestamp else { return Err(QuotePriceError::missing_block_timestamp()); }; let block_height = raw.block_height; @@ -548,10 +605,12 @@ pub mod oracle { pub struct QuotePriceError(QuotePriceErrorKind); impl QuotePriceError { + #[must_use] pub fn price_parse_error(err: std::num::ParseIntError) -> Self { Self(QuotePriceErrorKind::PriceParseError(err)) } + #[must_use] pub fn missing_block_timestamp() -> Self { Self(QuotePriceErrorKind::MissingBlockTimestamp) } @@ -574,6 +633,12 @@ pub mod oracle { } impl CurrencyPairState { + /// Converts from a raw protobuf `CurrencyPairState` to a native `CurrencyPairState`. + /// + /// # Errors + /// + /// - if the `price` field is missing + /// - if the `price` field is invalid pub fn try_from_raw( raw: raw::CurrencyPairState, ) -> Result { @@ -609,10 +674,12 @@ pub mod oracle { pub struct CurrencyPairStateError(CurrencyPairStateErrorKind); impl CurrencyPairStateError { + #[must_use] pub fn missing_price() -> Self { Self(CurrencyPairStateErrorKind::MissingPrice) } + #[must_use] pub fn quote_price_parse_error(err: QuotePriceError) -> Self { Self(CurrencyPairStateErrorKind::QuotePriceParseError(err)) } @@ -656,6 +723,15 @@ pub mod oracle { self.nonce } + /// Converts from a raw protobuf `CurrencyPairGenesis` to a native + /// `CurrencyPairGenesis`. + /// + /// # Errors + /// + /// - if the `currency_pair` field is missing + /// - if the `currency_pair` field is invalid + /// - if the `currency_pair_price` field is missing + /// - if the `currency_pair_price` field is invalid pub fn try_from_raw( raw: raw::CurrencyPairGenesis, ) -> Result { @@ -696,14 +772,17 @@ pub mod oracle { pub struct CurrencyPairGenesisError(CurrencyPairGenesisErrorKind); impl CurrencyPairGenesisError { + #[must_use] pub fn missing_currency_pair() -> Self { Self(CurrencyPairGenesisErrorKind::MissingCurrencyPair) } + #[must_use] pub fn missing_currency_pair_price() -> Self { Self(CurrencyPairGenesisErrorKind::MissingCurrencyPairPrice) } + #[must_use] pub fn quote_price_parse_error(err: QuotePriceError) -> Self { Self(CurrencyPairGenesisErrorKind::QuotePriceParseError(err)) } @@ -727,6 +806,11 @@ pub mod oracle { } impl GenesisState { + /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. + /// + /// # Errors + /// + /// - if any of the `currency_pair_genesis` are invalid pub fn try_from_raw(raw: raw::GenesisState) -> Result { let currency_pair_genesis = raw .currency_pair_genesis @@ -759,6 +843,7 @@ pub mod oracle { pub struct GenesisStateError(GenesisStateErrorKind); impl GenesisStateError { + #[must_use] pub fn currency_pair_genesis_parse_error(err: CurrencyPairGenesisError) -> Self { Self(GenesisStateErrorKind::CurrencyPairGenesisParseError(err)) } diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 49f376ac70..d1c712d2f8 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -59,6 +59,8 @@ fn charlie() -> Address { .unwrap() } +// allow clippy here because this is an example genesis state, which is long. +#[allow(clippy::too_many_lines)] fn genesis_state() -> GenesisState { use astria_core::slinky::{ market_map::v1::{ @@ -77,14 +79,14 @@ fn genesis_state() -> GenesisState { decimals: 8, min_provider_count: 3, enabled: true, - metadata_json: "".to_string(), + metadata_json: String::new(), }, provider_configs: vec![ProviderConfig { name: "coingecko_api".to_string(), off_chain_ticker: "bitcoin/usd".to_string(), normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), invert: false, - metadata_json: "".to_string(), + metadata_json: String::new(), }], }, ); @@ -96,14 +98,14 @@ fn genesis_state() -> GenesisState { decimals: 8, min_provider_count: 3, enabled: true, - metadata_json: "".to_string(), + metadata_json: String::new(), }, provider_configs: vec![ProviderConfig { name: "coingecko_api".to_string(), off_chain_ticker: "ethereum/usd".to_string(), normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), invert: false, - metadata_json: "".to_string(), + metadata_json: String::new(), }], }, ); @@ -160,10 +162,10 @@ fn genesis_state() -> GenesisState { id: 0, nonce: 0, currency_pair_price: QuotePrice { - price: 5834065777, + price: 5_834_065_777, block_height: 0, block_timestamp: pbjson_types::Timestamp { - seconds: 1720122395, + seconds: 1_720_122_395, nanos: 0, }, }, @@ -173,10 +175,10 @@ fn genesis_state() -> GenesisState { id: 1, nonce: 0, currency_pair_price: QuotePrice { - price: 3138872234, + price: 3_138_872_234, block_height: 0, block_timestamp: pbjson_types::Timestamp { - seconds: 1720122395, + seconds: 1_720_122_395, nanos: 0, }, }, diff --git a/crates/astria-sequencer-utils/src/genesis_parser.rs b/crates/astria-sequencer-utils/src/genesis_parser.rs index 5a43b6c144..4139474978 100644 --- a/crates/astria-sequencer-utils/src/genesis_parser.rs +++ b/crates/astria-sequencer-utils/src/genesis_parser.rs @@ -133,6 +133,9 @@ mod tests { "genesis_time": "2023-06-21T15:58:36.741257Z", "initial_height": "0", "consensus_params": { + "abci": { + "vote_extensions_enable_height": "1" + }, "validator": { "pub_key_types": [ "ed25519" diff --git a/crates/astria-sequencer/app-genesis-state.json b/crates/astria-sequencer/app-genesis-state.json index 562c16ee34..f99bfe1ba6 100644 --- a/crates/astria-sequencer/app-genesis-state.json +++ b/crates/astria-sequencer/app-genesis-state.json @@ -57,10 +57,10 @@ "market_map": { "market_map": { "markets": { - "BITCOIN/USD": { + "BTC/USD": { "ticker": { "currency_pair": { - "base": "BITCOIN", + "base": "BTC", "quote": "USD" }, "decimals": 8, @@ -70,28 +70,32 @@ }, "provider_configs": [ { - "name": "kucoin_ws", - "off_chain_ticker": "btc_usd", - "normalize_by_pair": { - "base": "USDT", - "quote": "USD" - }, - "invert": false, - "metadata_json": "" - }, - { - "name": "binance", - "off_chain_ticker": "BTCUSD", + "name": "coingecko_api", + "off_chain_ticker": "bitcoin/usd", "normalize_by_pair": { "base": "USDT", "quote": "USD" }, "invert": false, "metadata_json": "" + } + ] + }, + "ETH/USD": { + "ticker": { + "currency_pair": { + "base": "ETH", + "quote": "USD" }, + "decimals": 8, + "min_provider_count": 3, + "enabled": true, + "metadata_json": "" + }, + "provider_configs": [ { - "name": "mexc", - "off_chain_ticker": "btc-usd", + "name": "coingecko_api", + "off_chain_ticker": "ethereum/usd", "normalize_by_pair": { "base": "USDT", "quote": "USD" @@ -119,7 +123,34 @@ } }, "oracle": { - "currency_pair_genesis": [], - "next_id": 0 + "currency_pair_genesis": [ + { + "currency_pair": { + "base": "BTC", + "quote": "USD" + }, + "currency_pair_price": { + "price": 5834065777, + "block_timestamp": "2024-07-04T19:46:35+00:00", + "block_height": 0 + }, + "id": 0, + "nonce": 0 + }, + { + "currency_pair": { + "base": "ETH", + "quote": "USD" + }, + "currency_pair_price": { + "price": 3138872234, + "block_timestamp": "2024-07-04T19:46:35+00:00", + "block_height": 0 + }, + "id": 1, + "nonce": 0 + } + ], + "next_id": 2 } } \ No newline at end of file diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index e83f2e330a..95bff38898 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -356,9 +356,15 @@ impl App { } // adjust max block size to account for extended commit info - let mut block_size_constraints = - BlockSizeConstraints::new(max_tx_bytes - extended_commit_info_bytes.len()) - .context("failed to create block size constraints")?; + let mut block_size_constraints = BlockSizeConstraints::new( + max_tx_bytes + .checked_sub(extended_commit_info_bytes.len()) + .expect( + "extended_commit_info_bytes is shorter than max_tx_bytes, as it was checked \ + above", + ), + ) + .context("failed to create block size constraints")?; let block_data = BlockData { misbehavior: prepare_proposal.misbehavior, diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index d1d4d88be3..a081f7af91 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -37,6 +37,7 @@ use tonic::transport::Channel; use tracing::{ debug, trace, + warn, }; use crate::{ @@ -196,7 +197,7 @@ async fn transform_oracle_service_prices( continue; }; let encoded_price = - DefaultCurrencyPairStrategy::get_encoded_price(state, ¤cy_pair, price).await; + DefaultCurrencyPairStrategy::get_encoded_price(state, ¤cy_pair, price); debug!( currency_pair = currency_pair.to_string(), @@ -224,7 +225,7 @@ impl ProposalHandler { return Ok(extended_commit_info); } - for vote in extended_commit_info.votes.iter_mut() { + for vote in &mut extended_commit_info.votes { let oracle_vote_extension = RawOracleVoteExtension::decode(vote.vote_extension.clone())?.into(); if let Err(e) = verify_vote_extension(state, oracle_vote_extension, true).await { @@ -304,7 +305,7 @@ async fn validate_vote_extensions( } if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) - && vote.vote_extension.len() > 0 + && !vote.vote_extension.is_empty() { anyhow::bail!( "non-commit vote extension present for validator {}", @@ -344,8 +345,11 @@ async fn validate_vote_extensions( let vote_extension = CanonicalVoteExtension { extension: vote.vote_extension.to_vec(), - height: (height - 1) as i64, - round: extended_commit_info.round.value() as i64, + height: i64::try_from(height.checked_sub(1).expect( + "can subtract 1 from height as this function is only called for block height >1", + )) + .expect("block height must fit in an i64"), + round: i64::from(extended_commit_info.round.value()), chain_id: chain_id.to_string(), }; @@ -411,7 +415,7 @@ fn validate_extended_commit_against_last_commit( a.validator .power .partial_cmp(&b.validator.power) - .map(|v| v.reverse()) + .map(std::cmp::Ordering::reverse) } }), "extended commit votes are not sorted by voting power", @@ -430,7 +434,7 @@ fn validate_extended_commit_against_last_commit( // vote is absent; no need to check for the block id flag matching the last commit if vote.sig_info == Flag(tendermint::block::BlockIdFlag::Absent) - && vote.vote_extension.len() == 0 + && vote.vote_extension.is_empty() && vote.extension_signature.is_none() { continue; @@ -516,7 +520,6 @@ async fn aggregate_oracle_votes( let price = DefaultCurrencyPairStrategy::get_decoded_price(state, ¤cy_pair, &price_bytes) - .await .context("failed to get decoded price")?; currency_pair_to_price_list .entry(currency_pair) @@ -535,7 +538,17 @@ async fn aggregate_oracle_votes( price_list.sort_unstable(); let mid = price_list.len() / 2; if price_list.len() % 2 == 0 { - (price_list[mid - 1] + price_list[mid]) / 2 + let Some(num) = price_list[mid + .checked_sub(1) + .expect("must subtract as the length of the price list is >0")] + .checked_add(price_list[mid]) else { + warn!( + "failed to add two middle prices together; skipping currency pair: {}", + currency_pair, + ); + continue; + }; + num / 2 } else { price_list[mid] } diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index f03d00db39..f17a0fb60f 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -31,7 +31,7 @@ pub struct Config { pub metrics_http_listener_addr: String, /// Writes a human readable format to stdout instead of JSON formatted OTEL trace data. pub pretty_print: bool, - /// If the oracle is enabled. If true, the oracle_grpc_addr must be set. + /// If the oracle is enabled. If true, the `oracle_grpc_addr` must be set. /// Should be true for validator nodes and false for non-validator nodes. pub oracle_enabled: bool, /// The gRPC endpoint for the oracle sidecar. diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index c09737b496..c3af89ff4c 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -217,7 +217,7 @@ impl OracleService for SequencerServer { nonce: state.nonce, id: state.id, decimals: 0, // TODO: get this from the marketmap - }) + }); } Ok(Response::new(GetPricesResponse { prices, diff --git a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs index c09d7e7751..19fb5ce627 100644 --- a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs +++ b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs @@ -1,9 +1,12 @@ -use anyhow::Context as _; +use anyhow::{ + ensure, + Context as _, +}; use astria_core::slinky::types::v1::CurrencyPair; use crate::slinky::oracle::state_ext::StateReadExt; -/// see https://github.com/skip-mev/slinky/blob/793b2e874d6e720bd288e82e782502e41cf06f8c/abci/strategies/currencypair/default.go +/// see pub(crate) struct DefaultCurrencyPairStrategy; impl DefaultCurrencyPairStrategy { @@ -21,7 +24,7 @@ impl DefaultCurrencyPairStrategy { state.get_currency_pair(id).await } - pub(crate) async fn get_encoded_price( + pub(crate) fn get_encoded_price( _state: &S, _: &CurrencyPair, price: u128, @@ -29,11 +32,12 @@ impl DefaultCurrencyPairStrategy { price.to_be_bytes().to_vec() } - pub(crate) async fn get_decoded_price( + pub(crate) fn get_decoded_price( _state: &S, _: &CurrencyPair, encoded_price: &[u8], ) -> anyhow::Result { + ensure!(encoded_price.len() == 16, "invalid encoded price length"); let mut bytes = [0; 16]; bytes.copy_from_slice(encoded_price); Ok(u128::from_be_bytes(bytes)) @@ -53,7 +57,7 @@ impl DefaultCurrencyPairStrategy { .get_num_removed_currency_pairs() .await .context("failed to get number of removed currency pairs")?; - Ok(current + removed) + Ok(current.saturating_add(removed)) } else { Ok(current) } From f8dd3d656bf300448bc3021dfbf99e021d43183a Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 22:50:41 -0400 Subject: [PATCH 20/89] fmt --- crates/astria-sequencer/src/address/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/astria-sequencer/src/address/mod.rs b/crates/astria-sequencer/src/address/mod.rs index 6cfb2dd735..65a9b00009 100644 --- a/crates/astria-sequencer/src/address/mod.rs +++ b/crates/astria-sequencer/src/address/mod.rs @@ -56,10 +56,8 @@ mod regular { .context("failed constructing a dummy address from the provided prefix")?; BASE_PREFIX.set(base_prefix.to_string()).expect( - "THIS IS A BUG: attempted to set the base prefix more than once; it should only be \ - set - once when serving the `InitChain` consensus request, or immediately after Sequencer \ - is + "THIS IS A BUG: attempted to set the base prefix more than once; it should only be set + once when serving the `InitChain` consensus request, or immediately after Sequencer is restarted. It cannot be initialized twice or concurrently from more than one task or \ thread.", ); From b96ce64b97b52ddf77d25fdc28de156684cca41b Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 23:00:56 -0400 Subject: [PATCH 21/89] remove unused protos --- crates/astria-core/src/generated/mod.rs | 24 - .../generated/slinky.marketmap.module.v1.rs | 21 - .../slinky.marketmap.module.v1.serde.rs | 109 --- .../src/generated/slinky.marketmap.v1.rs | 788 --------------- .../generated/slinky.marketmap.v1.serde.rs | 921 ------------------ .../src/generated/slinky.oracle.module.v1.rs | 16 - .../slinky.oracle.module.v1.serde.rs | 91 -- .../src/generated/slinky.oracle.v1.rs | 452 --------- .../src/generated/slinky.oracle.v1.serde.rs | 360 ------- crates/astria-core/src/slinky.rs | 858 ---------------- crates/astria-core/src/slinky/abci.rs | 32 + crates/astria-core/src/slinky/market_map.rs | 423 ++++++++ crates/astria-core/src/slinky/mod.rs | 4 + crates/astria-core/src/slinky/oracle.rs | 302 ++++++ crates/astria-core/src/slinky/types.rs | 87 ++ proto/vendored/slinky/marketmap/v1/tx.proto | 120 --- proto/vendored/slinky/oracle/v1/tx.proto | 60 -- 17 files changed, 848 insertions(+), 3820 deletions(-) delete mode 100644 crates/astria-core/src/generated/slinky.marketmap.module.v1.rs delete mode 100644 crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs delete mode 100644 crates/astria-core/src/generated/slinky.oracle.module.v1.rs delete mode 100644 crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs delete mode 100644 crates/astria-core/src/slinky.rs create mode 100644 crates/astria-core/src/slinky/abci.rs create mode 100644 crates/astria-core/src/slinky/market_map.rs create mode 100644 crates/astria-core/src/slinky/mod.rs create mode 100644 crates/astria-core/src/slinky/oracle.rs create mode 100644 crates/astria-core/src/slinky/types.rs delete mode 100644 proto/vendored/slinky/marketmap/v1/tx.proto delete mode 100644 proto/vendored/slinky/oracle/v1/tx.proto diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index c5cbb1c0ac..c30ae4cb9f 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -258,18 +258,6 @@ pub mod slinky { } pub mod marketmap { - pub mod module { - pub mod v1 { - include!("slinky.marketmap.module.v1.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("slinky.marketmap.module.v1.serde.rs"); - } - } - } - pub mod v1 { include!("slinky.marketmap.v1.rs"); @@ -282,18 +270,6 @@ pub mod slinky { } pub mod oracle { - pub mod module { - pub mod v1 { - include!("slinky.oracle.module.v1.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("slinky.oracle.module.v1.serde.rs"); - } - } - } - pub mod v1 { include!("slinky.oracle.v1.rs"); diff --git a/crates/astria-core/src/generated/slinky.marketmap.module.v1.rs b/crates/astria-core/src/generated/slinky.marketmap.module.v1.rs deleted file mode 100644 index 1f8612a429..0000000000 --- a/crates/astria-core/src/generated/slinky.marketmap.module.v1.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// Module is the config object of the builder module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Module { - /// Authority defines the custom module authority. If not set, defaults to the - /// governance module. - #[prost(string, tag = "1")] - pub authority: ::prost::alloc::string::String, - /// HooksOrder specifies the order of marketmap hooks and should be a list - /// of module names which provide a marketmap hooks instance. If no order is - /// provided, then hooks will be applied in alphabetical order of module names. - #[prost(string, repeated, tag = "2")] - pub hooks_order: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, -} -impl ::prost::Name for Module { - const NAME: &'static str = "Module"; - const PACKAGE: &'static str = "slinky.marketmap.module.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.module.v1.{}", Self::NAME) - } -} diff --git a/crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs b/crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs deleted file mode 100644 index 877db8b5fc..0000000000 --- a/crates/astria-core/src/generated/slinky.marketmap.module.v1.serde.rs +++ /dev/null @@ -1,109 +0,0 @@ -impl serde::Serialize for Module { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.authority.is_empty() { - len += 1; - } - if !self.hooks_order.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.marketmap.module.v1.Module", len)?; - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - if !self.hooks_order.is_empty() { - struct_ser.serialize_field("hooks_order", &self.hooks_order)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for Module { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "authority", - "hooks_order", - "hooksOrder", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Authority, - HooksOrder, - } - 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 { - "authority" => Ok(GeneratedField::Authority), - "hooksOrder" | "hooks_order" => Ok(GeneratedField::HooksOrder), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = Module; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.module.v1.Module") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut authority__ = None; - let mut hooks_order__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - GeneratedField::HooksOrder => { - if hooks_order__.is_some() { - return Err(serde::de::Error::duplicate_field("hooksOrder")); - } - hooks_order__ = Some(map_.next_value()?); - } - } - } - Ok(Module { - authority: authority__.unwrap_or_default(), - hooks_order: hooks_order__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.module.v1.Module", FIELDS, GeneratedVisitor) - } -} diff --git a/crates/astria-core/src/generated/slinky.marketmap.v1.rs b/crates/astria-core/src/generated/slinky.marketmap.v1.rs index 6d334e4dd0..b420d4a558 100644 --- a/crates/astria-core/src/generated/slinky.marketmap.v1.rs +++ b/crates/astria-core/src/generated/slinky.marketmap.v1.rs @@ -788,791 +788,3 @@ pub mod query_server { const NAME: &'static str = "slinky.marketmap.v1.Query"; } } -/// MsgUpsertMarkets defines a message carrying a payload for performing market upserts (update or -/// create if does not exist) in the x/marketmap module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgUpsertMarkets { - /// Authority is the signer of this transaction. This authority must be - /// authorized by the module to execute the message. - #[prost(string, tag = "1")] - pub authority: ::prost::alloc::string::String, - /// CreateMarkets is the list of all markets to be created for the given - /// transaction. - #[prost(message, repeated, tag = "2")] - pub markets: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for MsgUpsertMarkets { - const NAME: &'static str = "MsgUpsertMarkets"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgUpsertMarketsResponse is the response from the UpsertMarkets API in the x/marketmap module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgUpsertMarketsResponse { - /// UpdatedMarkets is a map between the ticker and whether the market was updated. - #[prost(map = "string, bool", tag = "1")] - pub market_updates: ::std::collections::HashMap< - ::prost::alloc::string::String, - bool, - >, -} -impl ::prost::Name for MsgUpsertMarketsResponse { - const NAME: &'static str = "MsgUpsertMarketsResponse"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgCreateMarkets defines a message carrying a payload for creating markets in -/// the x/marketmap module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgCreateMarkets { - /// Authority is the signer of this transaction. This authority must be - /// authorized by the module to execute the message. - #[prost(string, tag = "1")] - pub authority: ::prost::alloc::string::String, - /// CreateMarkets is the list of all markets to be created for the given - /// transaction. - #[prost(message, repeated, tag = "2")] - pub create_markets: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for MsgCreateMarkets { - const NAME: &'static str = "MsgCreateMarkets"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgUpdateMarketMapResponse is the response message for MsgUpdateMarketMap. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgCreateMarketsResponse {} -impl ::prost::Name for MsgCreateMarketsResponse { - const NAME: &'static str = "MsgCreateMarketsResponse"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgUpdateMarkets defines a message carrying a payload for updating the -/// x/marketmap module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgUpdateMarkets { - /// Authority is the signer of this transaction. This authority must be - /// authorized by the module to execute the message. - #[prost(string, tag = "1")] - pub authority: ::prost::alloc::string::String, - /// UpdateMarkets is the list of all markets to be updated for the given - /// transaction. - #[prost(message, repeated, tag = "2")] - pub update_markets: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for MsgUpdateMarkets { - const NAME: &'static str = "MsgUpdateMarkets"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgUpdateMarketsResponse is the response message for MsgUpdateMarkets. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgUpdateMarketsResponse {} -impl ::prost::Name for MsgUpdateMarketsResponse { - const NAME: &'static str = "MsgUpdateMarketsResponse"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgParams defines the Msg/Params request type. It contains the -/// new parameters for the x/marketmap module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgParams { - /// Params defines the new parameters for the x/marketmap module. - #[prost(message, optional, tag = "1")] - pub params: ::core::option::Option, - /// Authority defines the authority that is updating the x/marketmap module - /// parameters. - #[prost(string, tag = "2")] - pub authority: ::prost::alloc::string::String, -} -impl ::prost::Name for MsgParams { - const NAME: &'static str = "MsgParams"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgParamsResponse defines the Msg/Params response type. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgParamsResponse {} -impl ::prost::Name for MsgParamsResponse { - const NAME: &'static str = "MsgParamsResponse"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgRemoveMarketAuthorities defines the Msg/RemoveMarketAuthoritiesResponse -/// request type. It contains the new addresses to remove from the list of -/// authorities -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgRemoveMarketAuthorities { - /// RemoveAddresses is the list of addresses to remove. - #[prost(string, repeated, tag = "1")] - pub remove_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - /// Admin defines the authority that is the x/marketmap - /// Admin account. This account is set in the module parameters. - #[prost(string, tag = "2")] - pub admin: ::prost::alloc::string::String, -} -impl ::prost::Name for MsgRemoveMarketAuthorities { - const NAME: &'static str = "MsgRemoveMarketAuthorities"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// MsgRemoveMarketAuthoritiesResponse defines the -/// Msg/RemoveMarketAuthoritiesResponse response type. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgRemoveMarketAuthoritiesResponse {} -impl ::prost::Name for MsgRemoveMarketAuthoritiesResponse { - const NAME: &'static str = "MsgRemoveMarketAuthoritiesResponse"; - const PACKAGE: &'static str = "slinky.marketmap.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.marketmap.v1.{}", Self::NAME) - } -} -/// Generated client implementations. -#[cfg(feature = "client")] -pub mod msg_client { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - /// Msg is the message service for the x/marketmap module. - #[derive(Debug, Clone)] - pub struct MsgClient { - inner: tonic::client::Grpc, - } - impl MsgClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl MsgClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + Send + 'static, - ::Error: Into + Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> MsgClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + Send + Sync, - { - MsgClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// CreateMarkets creates markets from the given message. - pub async fn create_markets( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/slinky.marketmap.v1.Msg/CreateMarkets", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "CreateMarkets")); - self.inner.unary(req, path, codec).await - } - /// UpdateMarkets updates markets from the given message. - pub async fn update_markets( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/slinky.marketmap.v1.Msg/UpdateMarkets", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "UpdateMarkets")); - self.inner.unary(req, path, codec).await - } - /// UpdateParams defines a method for updating the x/marketmap module - /// parameters. - pub async fn update_params( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/slinky.marketmap.v1.Msg/UpdateParams", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "UpdateParams")); - self.inner.unary(req, path, codec).await - } - /// RemoveMarketAuthorities defines a method for removing market authorities - /// from the x/marketmap module. the signer must be the admin. - pub async fn remove_market_authorities( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/slinky.marketmap.v1.Msg/RemoveMarketAuthorities", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new("slinky.marketmap.v1.Msg", "RemoveMarketAuthorities"), - ); - self.inner.unary(req, path, codec).await - } - /// UpsertMarkets wraps both Create / Update markets into a single message. Specifically - /// if a market does not exist it will be created, otherwise it will be updated. The response - /// will be a map between ticker -> updated. - pub async fn upsert_markets( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/slinky.marketmap.v1.Msg/UpsertMarkets", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("slinky.marketmap.v1.Msg", "UpsertMarkets")); - self.inner.unary(req, path, codec).await - } - } -} -/// Generated server implementations. -#[cfg(feature = "server")] -pub mod msg_server { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with MsgServer. - #[async_trait] - pub trait Msg: Send + Sync + 'static { - /// CreateMarkets creates markets from the given message. - async fn create_markets( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// UpdateMarkets updates markets from the given message. - async fn update_markets( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// UpdateParams defines a method for updating the x/marketmap module - /// parameters. - async fn update_params( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// RemoveMarketAuthorities defines a method for removing market authorities - /// from the x/marketmap module. the signer must be the admin. - async fn remove_market_authorities( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// UpsertMarkets wraps both Create / Update markets into a single message. Specifically - /// if a market does not exist it will be created, otherwise it will be updated. The response - /// will be a map between ticker -> updated. - async fn upsert_markets( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - /// Msg is the message service for the x/marketmap module. - #[derive(Debug)] - pub struct MsgServer { - inner: _Inner, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - struct _Inner(Arc); - impl MsgServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for MsgServer - where - T: Msg, - B: Body + Send + 'static, - B::Error: Into + Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); - match req.uri().path() { - "/slinky.marketmap.v1.Msg/CreateMarkets" => { - #[allow(non_camel_case_types)] - struct CreateMarketsSvc(pub Arc); - impl tonic::server::UnaryService - for CreateMarketsSvc { - type Response = super::MsgCreateMarketsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::create_markets(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = CreateMarketsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/slinky.marketmap.v1.Msg/UpdateMarkets" => { - #[allow(non_camel_case_types)] - struct UpdateMarketsSvc(pub Arc); - impl tonic::server::UnaryService - for UpdateMarketsSvc { - type Response = super::MsgUpdateMarketsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::update_markets(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = UpdateMarketsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/slinky.marketmap.v1.Msg/UpdateParams" => { - #[allow(non_camel_case_types)] - struct UpdateParamsSvc(pub Arc); - impl tonic::server::UnaryService - for UpdateParamsSvc { - type Response = super::MsgParamsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::update_params(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = UpdateParamsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/slinky.marketmap.v1.Msg/RemoveMarketAuthorities" => { - #[allow(non_camel_case_types)] - struct RemoveMarketAuthoritiesSvc(pub Arc); - impl< - T: Msg, - > tonic::server::UnaryService - for RemoveMarketAuthoritiesSvc { - type Response = super::MsgRemoveMarketAuthoritiesResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::remove_market_authorities(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = RemoveMarketAuthoritiesSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/slinky.marketmap.v1.Msg/UpsertMarkets" => { - #[allow(non_camel_case_types)] - struct UpsertMarketsSvc(pub Arc); - impl tonic::server::UnaryService - for UpsertMarketsSvc { - type Response = super::MsgUpsertMarketsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::upsert_markets(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = UpsertMarketsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } - } - } - } - impl Clone for MsgServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } - impl tonic::server::NamedService for MsgServer { - const NAME: &'static str = "slinky.marketmap.v1.Msg"; - } -} diff --git a/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs b/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs index eff856eef6..9b0609430f 100644 --- a/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs +++ b/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs @@ -881,927 +881,6 @@ impl<'de> serde::Deserialize<'de> for MarketResponse { deserializer.deserialize_struct("slinky.marketmap.v1.MarketResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for MsgCreateMarkets { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.authority.is_empty() { - len += 1; - } - if !self.create_markets.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgCreateMarkets", len)?; - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - if !self.create_markets.is_empty() { - struct_ser.serialize_field("create_markets", &self.create_markets)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgCreateMarkets { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "authority", - "create_markets", - "createMarkets", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Authority, - CreateMarkets, - } - 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 { - "authority" => Ok(GeneratedField::Authority), - "createMarkets" | "create_markets" => Ok(GeneratedField::CreateMarkets), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgCreateMarkets; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgCreateMarkets") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut authority__ = None; - let mut create_markets__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - GeneratedField::CreateMarkets => { - if create_markets__.is_some() { - return Err(serde::de::Error::duplicate_field("createMarkets")); - } - create_markets__ = Some(map_.next_value()?); - } - } - } - Ok(MsgCreateMarkets { - authority: authority__.unwrap_or_default(), - create_markets: create_markets__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgCreateMarkets", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgCreateMarketsResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgCreateMarketsResponse", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgCreateMarketsResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - 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, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgCreateMarketsResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgCreateMarketsResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(MsgCreateMarketsResponse { - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgCreateMarketsResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgParams { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.params.is_some() { - len += 1; - } - if !self.authority.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgParams", len)?; - if let Some(v) = self.params.as_ref() { - struct_ser.serialize_field("params", v)?; - } - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgParams { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "params", - "authority", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Params, - Authority, - } - 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 { - "params" => Ok(GeneratedField::Params), - "authority" => Ok(GeneratedField::Authority), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgParams; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgParams") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut params__ = None; - let mut authority__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Params => { - if params__.is_some() { - return Err(serde::de::Error::duplicate_field("params")); - } - params__ = map_.next_value()?; - } - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - } - } - Ok(MsgParams { - params: params__, - authority: authority__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgParams", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgParamsResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgParamsResponse", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgParamsResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - 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, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgParamsResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgParamsResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(MsgParamsResponse { - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgParamsResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgRemoveMarketAuthorities { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.remove_addresses.is_empty() { - len += 1; - } - if !self.admin.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthorities", len)?; - if !self.remove_addresses.is_empty() { - struct_ser.serialize_field("remove_addresses", &self.remove_addresses)?; - } - if !self.admin.is_empty() { - struct_ser.serialize_field("admin", &self.admin)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgRemoveMarketAuthorities { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "remove_addresses", - "removeAddresses", - "admin", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - RemoveAddresses, - Admin, - } - 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 { - "removeAddresses" | "remove_addresses" => Ok(GeneratedField::RemoveAddresses), - "admin" => Ok(GeneratedField::Admin), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgRemoveMarketAuthorities; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgRemoveMarketAuthorities") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut remove_addresses__ = None; - let mut admin__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::RemoveAddresses => { - if remove_addresses__.is_some() { - return Err(serde::de::Error::duplicate_field("removeAddresses")); - } - remove_addresses__ = Some(map_.next_value()?); - } - GeneratedField::Admin => { - if admin__.is_some() { - return Err(serde::de::Error::duplicate_field("admin")); - } - admin__ = Some(map_.next_value()?); - } - } - } - Ok(MsgRemoveMarketAuthorities { - remove_addresses: remove_addresses__.unwrap_or_default(), - admin: admin__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthorities", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgRemoveMarketAuthoritiesResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthoritiesResponse", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgRemoveMarketAuthoritiesResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - 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, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgRemoveMarketAuthoritiesResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgRemoveMarketAuthoritiesResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(MsgRemoveMarketAuthoritiesResponse { - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgRemoveMarketAuthoritiesResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgUpdateMarkets { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.authority.is_empty() { - len += 1; - } - if !self.update_markets.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpdateMarkets", len)?; - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - if !self.update_markets.is_empty() { - struct_ser.serialize_field("update_markets", &self.update_markets)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgUpdateMarkets { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "authority", - "update_markets", - "updateMarkets", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Authority, - UpdateMarkets, - } - 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 { - "authority" => Ok(GeneratedField::Authority), - "updateMarkets" | "update_markets" => Ok(GeneratedField::UpdateMarkets), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgUpdateMarkets; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgUpdateMarkets") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut authority__ = None; - let mut update_markets__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - GeneratedField::UpdateMarkets => { - if update_markets__.is_some() { - return Err(serde::de::Error::duplicate_field("updateMarkets")); - } - update_markets__ = Some(map_.next_value()?); - } - } - } - Ok(MsgUpdateMarkets { - authority: authority__.unwrap_or_default(), - update_markets: update_markets__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpdateMarkets", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgUpdateMarketsResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpdateMarketsResponse", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgUpdateMarketsResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - 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, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgUpdateMarketsResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgUpdateMarketsResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(MsgUpdateMarketsResponse { - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpdateMarketsResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgUpsertMarkets { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.authority.is_empty() { - len += 1; - } - if !self.markets.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpsertMarkets", len)?; - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - if !self.markets.is_empty() { - struct_ser.serialize_field("markets", &self.markets)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgUpsertMarkets { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "authority", - "markets", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Authority, - Markets, - } - 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 { - "authority" => Ok(GeneratedField::Authority), - "markets" => Ok(GeneratedField::Markets), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgUpsertMarkets; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgUpsertMarkets") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut authority__ = None; - let mut markets__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - GeneratedField::Markets => { - if markets__.is_some() { - return Err(serde::de::Error::duplicate_field("markets")); - } - markets__ = Some(map_.next_value()?); - } - } - } - Ok(MsgUpsertMarkets { - authority: authority__.unwrap_or_default(), - markets: markets__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpsertMarkets", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgUpsertMarketsResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.market_updates.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MsgUpsertMarketsResponse", len)?; - if !self.market_updates.is_empty() { - struct_ser.serialize_field("market_updates", &self.market_updates)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgUpsertMarketsResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "market_updates", - "marketUpdates", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - MarketUpdates, - } - 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 { - "marketUpdates" | "market_updates" => Ok(GeneratedField::MarketUpdates), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgUpsertMarketsResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.marketmap.v1.MsgUpsertMarketsResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut market_updates__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::MarketUpdates => { - if market_updates__.is_some() { - return Err(serde::de::Error::duplicate_field("marketUpdates")); - } - market_updates__ = Some( - map_.next_value::>()? - ); - } - } - } - Ok(MsgUpsertMarketsResponse { - market_updates: market_updates__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.marketmap.v1.MsgUpsertMarketsResponse", FIELDS, GeneratedVisitor) - } -} impl serde::Serialize for Params { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/astria-core/src/generated/slinky.oracle.module.v1.rs b/crates/astria-core/src/generated/slinky.oracle.module.v1.rs deleted file mode 100644 index f185539e81..0000000000 --- a/crates/astria-core/src/generated/slinky.oracle.module.v1.rs +++ /dev/null @@ -1,16 +0,0 @@ -/// Module is the config object of the builder module. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Module { - /// Authority defines the custom module authority. If not set, defaults to the - /// governance module. - #[prost(string, tag = "1")] - pub authority: ::prost::alloc::string::String, -} -impl ::prost::Name for Module { - const NAME: &'static str = "Module"; - const PACKAGE: &'static str = "slinky.oracle.module.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.oracle.module.v1.{}", Self::NAME) - } -} diff --git a/crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs b/crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs deleted file mode 100644 index 6783394950..0000000000 --- a/crates/astria-core/src/generated/slinky.oracle.module.v1.serde.rs +++ /dev/null @@ -1,91 +0,0 @@ -impl serde::Serialize for Module { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.authority.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.oracle.module.v1.Module", len)?; - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for Module { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "authority", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Authority, - } - 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 { - "authority" => Ok(GeneratedField::Authority), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = Module; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.oracle.module.v1.Module") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut authority__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - } - } - Ok(Module { - authority: authority__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.oracle.module.v1.Module", FIELDS, GeneratedVisitor) - } -} diff --git a/crates/astria-core/src/generated/slinky.oracle.v1.rs b/crates/astria-core/src/generated/slinky.oracle.v1.rs index 76999bb2a9..9bce503a5d 100644 --- a/crates/astria-core/src/generated/slinky.oracle.v1.rs +++ b/crates/astria-core/src/generated/slinky.oracle.v1.rs @@ -765,455 +765,3 @@ pub mod query_server { const NAME: &'static str = "slinky.oracle.v1.Query"; } } -/// Given an authority + a set of CurrencyPairs, the x/oracle module will -/// check to see that the authority has permissions to update the set of -/// CurrencyPairs tracked in the oracle, and add the given CurrencyPairs to be -/// tracked in each VoteExtension -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgAddCurrencyPairs { - /// authority is the address of the account that is authorized to update the - /// x/oracle's CurrencyPairs - #[prost(string, tag = "1")] - pub authority: ::prost::alloc::string::String, - /// set of CurrencyPairs to be added to the module (+ prices if they are to be - /// set) - #[prost(message, repeated, tag = "2")] - pub currency_pairs: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for MsgAddCurrencyPairs { - const NAME: &'static str = "MsgAddCurrencyPairs"; - const PACKAGE: &'static str = "slinky.oracle.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgAddCurrencyPairsResponse {} -impl ::prost::Name for MsgAddCurrencyPairsResponse { - const NAME: &'static str = "MsgAddCurrencyPairsResponse"; - const PACKAGE: &'static str = "slinky.oracle.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) - } -} -/// Given an authority + a set of CurrencyPairIDs, the x/oracle module's message -/// service will remove all of the CurrencyPairs identified by each -/// CurrencyPairID in the request from state. Notice, if a given currency-pair -/// does not exist in state, the module ignores that currency-pair and continues -/// removing the rest. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgRemoveCurrencyPairs { - /// authority is the address of the account that is authorized to update the - /// x/oracle's CurrencyPairs - #[prost(string, tag = "1")] - pub authority: ::prost::alloc::string::String, - /// currency_pair_ids are the stringified representation of a currency-pairs - /// (base/quote) to be removed from the module's state - #[prost(string, repeated, tag = "2")] - pub currency_pair_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, -} -impl ::prost::Name for MsgRemoveCurrencyPairs { - const NAME: &'static str = "MsgRemoveCurrencyPairs"; - const PACKAGE: &'static str = "slinky.oracle.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgRemoveCurrencyPairsResponse {} -impl ::prost::Name for MsgRemoveCurrencyPairsResponse { - const NAME: &'static str = "MsgRemoveCurrencyPairsResponse"; - const PACKAGE: &'static str = "slinky.oracle.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("slinky.oracle.v1.{}", Self::NAME) - } -} -/// Generated client implementations. -#[cfg(feature = "client")] -pub mod msg_client { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - /// Msg is the message service for the x/oracle module. - #[derive(Debug, Clone)] - pub struct MsgClient { - inner: tonic::client::Grpc, - } - impl MsgClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl MsgClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + Send + 'static, - ::Error: Into + Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> MsgClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + Send + Sync, - { - MsgClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// AddCurrencyPairs will be used only by governance to update the set of - /// available CurrencyPairs. Given a set of CurrencyPair objects, update - /// the available currency pairs in the module . - pub async fn add_currency_pairs( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/slinky.oracle.v1.Msg/AddCurrencyPairs", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("slinky.oracle.v1.Msg", "AddCurrencyPairs")); - self.inner.unary(req, path, codec).await - } - /// RemoveCurrencyPairs will be used explicitly by governance to remove the - /// given set of currency-pairs from the module's state. Thus these - /// CurrencyPairs will no longer have price-data available from this module. - pub async fn remove_currency_pairs( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/slinky.oracle.v1.Msg/RemoveCurrencyPairs", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("slinky.oracle.v1.Msg", "RemoveCurrencyPairs")); - self.inner.unary(req, path, codec).await - } - } -} -/// Generated server implementations. -#[cfg(feature = "server")] -pub mod msg_server { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with MsgServer. - #[async_trait] - pub trait Msg: Send + Sync + 'static { - /// AddCurrencyPairs will be used only by governance to update the set of - /// available CurrencyPairs. Given a set of CurrencyPair objects, update - /// the available currency pairs in the module . - async fn add_currency_pairs( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// RemoveCurrencyPairs will be used explicitly by governance to remove the - /// given set of currency-pairs from the module's state. Thus these - /// CurrencyPairs will no longer have price-data available from this module. - async fn remove_currency_pairs( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - /// Msg is the message service for the x/oracle module. - #[derive(Debug)] - pub struct MsgServer { - inner: _Inner, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - struct _Inner(Arc); - impl MsgServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for MsgServer - where - T: Msg, - B: Body + Send + 'static, - B::Error: Into + Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); - match req.uri().path() { - "/slinky.oracle.v1.Msg/AddCurrencyPairs" => { - #[allow(non_camel_case_types)] - struct AddCurrencyPairsSvc(pub Arc); - impl tonic::server::UnaryService - for AddCurrencyPairsSvc { - type Response = super::MsgAddCurrencyPairsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::add_currency_pairs(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = AddCurrencyPairsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/slinky.oracle.v1.Msg/RemoveCurrencyPairs" => { - #[allow(non_camel_case_types)] - struct RemoveCurrencyPairsSvc(pub Arc); - impl< - T: Msg, - > tonic::server::UnaryService - for RemoveCurrencyPairsSvc { - type Response = super::MsgRemoveCurrencyPairsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::remove_currency_pairs(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = RemoveCurrencyPairsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } - } - } - } - impl Clone for MsgServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } - impl tonic::server::NamedService for MsgServer { - const NAME: &'static str = "slinky.oracle.v1.Msg"; - } -} diff --git a/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs b/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs index ee28a3bdf6..852ce75a99 100644 --- a/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs +++ b/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs @@ -1147,366 +1147,6 @@ impl<'de> serde::Deserialize<'de> for GetPricesResponse { deserializer.deserialize_struct("slinky.oracle.v1.GetPricesResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for MsgAddCurrencyPairs { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.authority.is_empty() { - len += 1; - } - if !self.currency_pairs.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgAddCurrencyPairs", len)?; - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - if !self.currency_pairs.is_empty() { - struct_ser.serialize_field("currency_pairs", &self.currency_pairs)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgAddCurrencyPairs { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "authority", - "currency_pairs", - "currencyPairs", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Authority, - CurrencyPairs, - } - 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 { - "authority" => Ok(GeneratedField::Authority), - "currencyPairs" | "currency_pairs" => Ok(GeneratedField::CurrencyPairs), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgAddCurrencyPairs; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.oracle.v1.MsgAddCurrencyPairs") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut authority__ = None; - let mut currency_pairs__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - GeneratedField::CurrencyPairs => { - if currency_pairs__.is_some() { - return Err(serde::de::Error::duplicate_field("currencyPairs")); - } - currency_pairs__ = Some(map_.next_value()?); - } - } - } - Ok(MsgAddCurrencyPairs { - authority: authority__.unwrap_or_default(), - currency_pairs: currency_pairs__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.oracle.v1.MsgAddCurrencyPairs", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgAddCurrencyPairsResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgAddCurrencyPairsResponse", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgAddCurrencyPairsResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - 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, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgAddCurrencyPairsResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.oracle.v1.MsgAddCurrencyPairsResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(MsgAddCurrencyPairsResponse { - }) - } - } - deserializer.deserialize_struct("slinky.oracle.v1.MsgAddCurrencyPairsResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgRemoveCurrencyPairs { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.authority.is_empty() { - len += 1; - } - if !self.currency_pair_ids.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairs", len)?; - if !self.authority.is_empty() { - struct_ser.serialize_field("authority", &self.authority)?; - } - if !self.currency_pair_ids.is_empty() { - struct_ser.serialize_field("currency_pair_ids", &self.currency_pair_ids)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgRemoveCurrencyPairs { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "authority", - "currency_pair_ids", - "currencyPairIds", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Authority, - CurrencyPairIds, - } - 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 { - "authority" => Ok(GeneratedField::Authority), - "currencyPairIds" | "currency_pair_ids" => Ok(GeneratedField::CurrencyPairIds), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgRemoveCurrencyPairs; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.oracle.v1.MsgRemoveCurrencyPairs") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut authority__ = None; - let mut currency_pair_ids__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Authority => { - if authority__.is_some() { - return Err(serde::de::Error::duplicate_field("authority")); - } - authority__ = Some(map_.next_value()?); - } - GeneratedField::CurrencyPairIds => { - if currency_pair_ids__.is_some() { - return Err(serde::de::Error::duplicate_field("currencyPairIds")); - } - currency_pair_ids__ = Some(map_.next_value()?); - } - } - } - Ok(MsgRemoveCurrencyPairs { - authority: authority__.unwrap_or_default(), - currency_pair_ids: currency_pair_ids__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairs", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for MsgRemoveCurrencyPairsResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairsResponse", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MsgRemoveCurrencyPairsResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - 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, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MsgRemoveCurrencyPairsResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct slinky.oracle.v1.MsgRemoveCurrencyPairsResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(MsgRemoveCurrencyPairsResponse { - }) - } - } - deserializer.deserialize_struct("slinky.oracle.v1.MsgRemoveCurrencyPairsResponse", FIELDS, GeneratedVisitor) - } -} impl serde::Serialize for QuotePrice { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/astria-core/src/slinky.rs b/crates/astria-core/src/slinky.rs deleted file mode 100644 index 1c229c6b7e..0000000000 --- a/crates/astria-core/src/slinky.rs +++ /dev/null @@ -1,858 +0,0 @@ -pub mod abci { - pub mod v1 { - use std::collections::HashMap; - - use crate::generated::slinky::abci::v1 as raw; - - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct OracleVoteExtension { - pub prices: HashMap>, - } - - impl OracleVoteExtension { - #[must_use] - pub fn from_raw(raw: raw::OracleVoteExtension) -> Self { - Self { - prices: raw.prices, - } - } - - #[must_use] - pub fn into_raw(self) -> raw::OracleVoteExtension { - raw::OracleVoteExtension { - prices: self.prices, - } - } - } - - impl From for OracleVoteExtension { - fn from(raw: raw::OracleVoteExtension) -> Self { - Self::from_raw(raw) - } - } - } -} - -pub mod types { - pub mod v1 { - use crate::generated::slinky::types::v1 as raw; - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct CurrencyPair { - base: String, - quote: String, - } - - impl CurrencyPair { - #[must_use] - pub fn new(base: String, quote: String) -> Self { - Self { - base, - quote, - } - } - - #[must_use] - pub fn base(&self) -> &str { - &self.base - } - - #[must_use] - pub fn quote(&self) -> &str { - &self.quote - } - - #[must_use] - pub fn from_raw(raw: raw::CurrencyPair) -> Self { - Self { - base: raw.base, - quote: raw.quote, - } - } - - #[must_use] - pub fn into_raw(self) -> raw::CurrencyPair { - raw::CurrencyPair { - base: self.base, - quote: self.quote, - } - } - } - - impl std::fmt::Display for CurrencyPair { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}/{}", self.base, self.quote) - } - } - - impl std::str::FromStr for CurrencyPair { - type Err = CurrencyPairParseError; - - fn from_str(s: &str) -> Result { - let parts: Vec<&str> = s.split('/').collect(); - if parts.len() != 2 { - return Err(CurrencyPairParseError::invalid_currency_pair_string(s)); - } - - Ok(Self { - base: parts[0].to_string(), - quote: parts[1].to_string(), - }) - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct CurrencyPairParseError(CurrencyPairParseErrorKind); - - #[derive(Debug, thiserror::Error)] - pub enum CurrencyPairParseErrorKind { - #[error("invalid currency pair string: {0}")] - InvalidCurrencyPairString(String), - } - - impl CurrencyPairParseError { - #[must_use] - pub fn invalid_currency_pair_string(s: &str) -> Self { - Self(CurrencyPairParseErrorKind::InvalidCurrencyPairString( - s.to_string(), - )) - } - } - } -} - -pub mod market_map { - pub mod v1 { - use std::{ - collections::HashMap, - str::FromStr, - }; - - use crate::{ - generated::slinky::marketmap::v1 as raw, - primitive::v1::{ - Address, - AddressError, - }, - slinky::types::v1::CurrencyPair, - }; - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct GenesisState { - pub market_map: MarketMap, - pub last_updated: u64, - pub params: Params, - } - - impl GenesisState { - /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. - /// - /// # Errors - /// - /// - if the `market_map` field is missing - /// - if the `market_map` field is invalid - /// - if the `params` field is missing - /// - if the `params` field is invalid - pub fn try_from_raw(raw: raw::GenesisState) -> Result { - let Some(market_map) = raw - .market_map - .map(MarketMap::try_from_raw) - .transpose() - .map_err(GenesisStateError::invalid_market_map)? - else { - return Err(GenesisStateError::missing_market_map()); - }; - let last_updated = raw.last_updated; - let Some(params) = raw - .params - .map(Params::try_from_raw) - .transpose() - .map_err(GenesisStateError::invalid_params)? - else { - return Err(GenesisStateError::missing_params()); - }; - Ok(Self { - market_map, - last_updated, - params, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::GenesisState { - raw::GenesisState { - market_map: Some(self.market_map.into_raw()), - last_updated: self.last_updated, - params: Some(self.params.into_raw()), - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct GenesisStateError(GenesisStateErrorKind); - - impl GenesisStateError { - #[must_use] - pub fn missing_market_map() -> Self { - Self(GenesisStateErrorKind::MissingMarketMap) - } - - #[must_use] - pub fn invalid_market_map(err: MarketMapError) -> Self { - Self(GenesisStateErrorKind::MarketMapParseError(err)) - } - - #[must_use] - pub fn missing_params() -> Self { - Self(GenesisStateErrorKind::MissingParams) - } - - #[must_use] - pub fn invalid_params(err: ParamsError) -> Self { - Self(GenesisStateErrorKind::ParamsParseError(err)) - } - } - - #[derive(Debug, thiserror::Error)] - enum GenesisStateErrorKind { - #[error("missing market map")] - MissingMarketMap, - #[error(transparent)] - MarketMapParseError(#[from] MarketMapError), - #[error("missing params")] - MissingParams, - #[error(transparent)] - ParamsParseError(#[from] ParamsError), - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Params { - pub market_authorities: Vec
, - pub admin: Address, - } - - impl Params { - /// Converts from a raw protobuf `Params` to a native `Params`. - /// - /// # Errors - /// - /// - if any of the `market_authorities` addresses are invalid - /// - if the `admin` address is invalid - pub fn try_from_raw(raw: raw::Params) -> Result { - let market_authorities = raw - .market_authorities - .into_iter() - .map(|s| Address::from_str(&s)) - .collect::, _>>() - .map_err(ParamsError::market_authority_parse_error)?; - let admin = raw.admin.parse().map_err(ParamsError::admin_parse_error)?; - Ok(Self { - market_authorities, - admin, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::Params { - raw::Params { - market_authorities: self - .market_authorities - .into_iter() - .map(|a| a.to_string()) - .collect(), - admin: self.admin.to_string(), - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct ParamsError(ParamsErrorKind); - - impl ParamsError { - #[must_use] - pub fn market_authority_parse_error(err: AddressError) -> Self { - Self(ParamsErrorKind::MarketAuthorityParseError(err)) - } - - #[must_use] - pub fn admin_parse_error(err: AddressError) -> Self { - Self(ParamsErrorKind::AdminParseError(err)) - } - } - - #[derive(Debug, thiserror::Error)] - pub enum ParamsErrorKind { - #[error("failed to parse market authority address")] - MarketAuthorityParseError(#[source] AddressError), - #[error("failed to parse admin address")] - AdminParseError(#[source] AddressError), - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Market { - pub ticker: Ticker, - pub provider_configs: Vec, - } - - impl Market { - /// Converts from a raw protobuf `Market` to a native `Market`. - /// - /// # Errors - /// - /// - if the `ticker` field is missing - /// - if the `ticker` field is invalid - /// - if any of the `provider_configs` are invalid - pub fn try_from_raw(raw: raw::Market) -> Result { - let Some(ticker) = raw - .ticker - .map(Ticker::try_from_raw) - .transpose() - .map_err(MarketError::invalid_ticker)? - else { - return Err(MarketError::missing_ticker()); - }; - - let provider_configs = raw - .provider_configs - .into_iter() - .map(ProviderConfig::try_from_raw) - .collect::, _>>() - .map_err(MarketError::invalid_provider_config)?; - Ok(Self { - ticker, - provider_configs, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::Market { - raw::Market { - ticker: Some(self.ticker.into_raw()), - provider_configs: self - .provider_configs - .into_iter() - .map(ProviderConfig::into_raw) - .collect(), - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct MarketError(MarketErrorKind); - - impl MarketError { - #[must_use] - pub fn missing_ticker() -> Self { - Self(MarketErrorKind::MissingTicker) - } - - #[must_use] - pub fn invalid_ticker(err: TickerError) -> Self { - Self(MarketErrorKind::TickerParseError(err)) - } - - #[must_use] - pub fn invalid_provider_config(err: ProviderConfigError) -> Self { - Self(MarketErrorKind::ProviderConfigParseError(err)) - } - } - - #[derive(Debug, thiserror::Error)] - enum MarketErrorKind { - #[error("missing ticker")] - MissingTicker, - #[error(transparent)] - TickerParseError(#[from] TickerError), - #[error(transparent)] - ProviderConfigParseError(#[from] ProviderConfigError), - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Ticker { - pub currency_pair: CurrencyPair, - pub decimals: u64, - pub min_provider_count: u64, - pub enabled: bool, - pub metadata_json: String, - } - - impl Ticker { - /// Converts from a raw protobuf `Ticker` to a native `Ticker`. - /// - /// # Errors - /// - /// - if the `currency_pair` field is missing - /// - if the `currency_pair` field is invalid - pub fn try_from_raw(raw: raw::Ticker) -> Result { - let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { - return Err(TickerError::missing_currency_pair()); - }; - Ok(Self { - currency_pair, - decimals: raw.decimals, - min_provider_count: raw.min_provider_count, - enabled: raw.enabled, - metadata_json: raw.metadata_json, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::Ticker { - raw::Ticker { - currency_pair: Some(self.currency_pair.into_raw()), - decimals: self.decimals, - min_provider_count: self.min_provider_count, - enabled: self.enabled, - metadata_json: self.metadata_json, - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct TickerError(TickerErrorKind); - - impl TickerError { - #[must_use] - pub fn missing_currency_pair() -> Self { - Self(TickerErrorKind::MissingCurrencyPair) - } - } - - #[derive(Debug, thiserror::Error)] - enum TickerErrorKind { - #[error("missing currency pair")] - MissingCurrencyPair, - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct ProviderConfig { - pub name: String, - pub off_chain_ticker: String, - pub normalize_by_pair: CurrencyPair, - pub invert: bool, - pub metadata_json: String, - } - - impl ProviderConfig { - /// Converts from a raw protobuf `ProviderConfig` to a native `ProviderConfig`. - /// - /// # Errors - /// - /// - if the `normalize_by_pair` field is missing - pub fn try_from_raw(raw: raw::ProviderConfig) -> Result { - let Some(normalize_by_pair) = raw.normalize_by_pair.map(CurrencyPair::from_raw) - else { - return Err(ProviderConfigError::missing_normalize_by_pair()); - }; - Ok(Self { - name: raw.name, - off_chain_ticker: raw.off_chain_ticker, - normalize_by_pair, - invert: raw.invert, - metadata_json: raw.metadata_json, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::ProviderConfig { - raw::ProviderConfig { - name: self.name, - off_chain_ticker: self.off_chain_ticker, - normalize_by_pair: Some(self.normalize_by_pair.into_raw()), - invert: self.invert, - metadata_json: self.metadata_json, - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct ProviderConfigError(ProviderConfigErrorKind); - - impl ProviderConfigError { - #[must_use] - pub fn missing_normalize_by_pair() -> Self { - Self(ProviderConfigErrorKind::MissingNormalizeByPair) - } - } - - #[derive(Debug, thiserror::Error)] - enum ProviderConfigErrorKind { - #[error("missing normalize by pair")] - MissingNormalizeByPair, - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct MarketMap { - pub markets: HashMap, - } - - impl MarketMap { - /// Converts from a raw protobuf `MarketMap` to a native `MarketMap`. - /// - /// # Errors - /// - /// - if any of the markets are invalid - /// - if any of the market names are invalid - pub fn try_from_raw(raw: raw::MarketMap) -> Result { - let mut markets = HashMap::new(); - for (k, v) in raw.markets { - let market = Market::try_from_raw(v) - .map_err(|e| MarketMapError::invalid_market(k.clone(), e))?; - markets.insert(k, market); - } - Ok(Self { - markets, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::MarketMap { - let markets = self - .markets - .into_iter() - .map(|(k, v)| (k, v.into_raw())) - .collect(); - raw::MarketMap { - markets, - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct MarketMapError(MarketMapErrorKind); - - impl MarketMapError { - #[must_use] - pub fn invalid_market(name: String, err: MarketError) -> Self { - Self(MarketMapErrorKind::InvalidMarket(name, err)) - } - } - - #[derive(Debug, thiserror::Error)] - enum MarketMapErrorKind { - #[error("invalid market {0}")] - InvalidMarket(String, MarketError), - } - } -} - -pub mod oracle { - pub mod v1 { - use pbjson_types::Timestamp; - - use crate::{ - generated::slinky::oracle::v1 as raw, - slinky::types::v1::CurrencyPair, - }; - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone)] - pub struct QuotePrice { - pub price: u128, - pub block_timestamp: Timestamp, - pub block_height: u64, - } - - impl QuotePrice { - /// Converts from a raw protobuf `QuotePrice` to a native `QuotePrice`. - /// - /// # Errors - /// - /// - if the `price` field is invalid - /// - if the `block_timestamp` field is missing - pub fn try_from_raw(raw: raw::QuotePrice) -> Result { - let price = raw - .price - .parse() - .map_err(QuotePriceError::price_parse_error)?; - let Some(block_timestamp) = raw.block_timestamp else { - return Err(QuotePriceError::missing_block_timestamp()); - }; - let block_height = raw.block_height; - Ok(Self { - price, - block_timestamp, - block_height, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::QuotePrice { - raw::QuotePrice { - price: self.price.to_string(), - block_timestamp: Some(self.block_timestamp), - block_height: self.block_height, - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct QuotePriceError(QuotePriceErrorKind); - - impl QuotePriceError { - #[must_use] - pub fn price_parse_error(err: std::num::ParseIntError) -> Self { - Self(QuotePriceErrorKind::PriceParseError(err)) - } - - #[must_use] - pub fn missing_block_timestamp() -> Self { - Self(QuotePriceErrorKind::MissingBlockTimestamp) - } - } - - #[derive(Debug, thiserror::Error)] - enum QuotePriceErrorKind { - #[error(transparent)] - PriceParseError(#[from] std::num::ParseIntError), - #[error("missing block timestamp")] - MissingBlockTimestamp, - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone)] - pub struct CurrencyPairState { - pub price: QuotePrice, - pub nonce: u64, - pub id: u64, - } - - impl CurrencyPairState { - /// Converts from a raw protobuf `CurrencyPairState` to a native `CurrencyPairState`. - /// - /// # Errors - /// - /// - if the `price` field is missing - /// - if the `price` field is invalid - pub fn try_from_raw( - raw: raw::CurrencyPairState, - ) -> Result { - let Some(price) = raw - .price - .map(QuotePrice::try_from_raw) - .transpose() - .map_err(CurrencyPairStateError::quote_price_parse_error)? - else { - return Err(CurrencyPairStateError::missing_price()); - }; - let nonce = raw.nonce; - let id = raw.id; - Ok(Self { - price, - nonce, - id, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::CurrencyPairState { - raw::CurrencyPairState { - price: Some(self.price.into_raw()), - nonce: self.nonce, - id: self.id, - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct CurrencyPairStateError(CurrencyPairStateErrorKind); - - impl CurrencyPairStateError { - #[must_use] - pub fn missing_price() -> Self { - Self(CurrencyPairStateErrorKind::MissingPrice) - } - - #[must_use] - pub fn quote_price_parse_error(err: QuotePriceError) -> Self { - Self(CurrencyPairStateErrorKind::QuotePriceParseError(err)) - } - } - - #[derive(Debug, thiserror::Error)] - enum CurrencyPairStateErrorKind { - #[error("missing price")] - MissingPrice, - #[error(transparent)] - QuotePriceParseError(QuotePriceError), - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone)] - pub struct CurrencyPairGenesis { - pub currency_pair: CurrencyPair, - pub currency_pair_price: QuotePrice, - pub id: u64, - pub nonce: u64, - } - - impl CurrencyPairGenesis { - #[must_use] - pub fn currency_pair(&self) -> &CurrencyPair { - &self.currency_pair - } - - #[must_use] - pub fn currency_pair_price(&self) -> &QuotePrice { - &self.currency_pair_price - } - - #[must_use] - pub fn id(&self) -> u64 { - self.id - } - - #[must_use] - pub fn nonce(&self) -> u64 { - self.nonce - } - - /// Converts from a raw protobuf `CurrencyPairGenesis` to a native - /// `CurrencyPairGenesis`. - /// - /// # Errors - /// - /// - if the `currency_pair` field is missing - /// - if the `currency_pair` field is invalid - /// - if the `currency_pair_price` field is missing - /// - if the `currency_pair_price` field is invalid - pub fn try_from_raw( - raw: raw::CurrencyPairGenesis, - ) -> Result { - let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { - return Err(CurrencyPairGenesisError::missing_currency_pair()); - }; - let Some(currency_pair_price) = raw - .currency_pair_price - .map(QuotePrice::try_from_raw) - .transpose() - .map_err(CurrencyPairGenesisError::quote_price_parse_error)? - else { - return Err(CurrencyPairGenesisError::missing_currency_pair_price()); - }; - let id = raw.id; - let nonce = raw.nonce; - Ok(Self { - currency_pair, - currency_pair_price, - id, - nonce, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::CurrencyPairGenesis { - raw::CurrencyPairGenesis { - currency_pair: Some(self.currency_pair.into_raw()), - currency_pair_price: Some(self.currency_pair_price.into_raw()), - id: self.id, - nonce: self.nonce, - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct CurrencyPairGenesisError(CurrencyPairGenesisErrorKind); - - impl CurrencyPairGenesisError { - #[must_use] - pub fn missing_currency_pair() -> Self { - Self(CurrencyPairGenesisErrorKind::MissingCurrencyPair) - } - - #[must_use] - pub fn missing_currency_pair_price() -> Self { - Self(CurrencyPairGenesisErrorKind::MissingCurrencyPairPrice) - } - - #[must_use] - pub fn quote_price_parse_error(err: QuotePriceError) -> Self { - Self(CurrencyPairGenesisErrorKind::QuotePriceParseError(err)) - } - } - - #[derive(Debug, thiserror::Error)] - enum CurrencyPairGenesisErrorKind { - #[error("missing currency pair")] - MissingCurrencyPair, - #[error("missing currency pair price")] - MissingCurrencyPairPrice, - #[error(transparent)] - QuotePriceParseError(QuotePriceError), - } - - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Debug, Clone)] - pub struct GenesisState { - pub currency_pair_genesis: Vec, - pub next_id: u64, - } - - impl GenesisState { - /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. - /// - /// # Errors - /// - /// - if any of the `currency_pair_genesis` are invalid - pub fn try_from_raw(raw: raw::GenesisState) -> Result { - let currency_pair_genesis = raw - .currency_pair_genesis - .into_iter() - .map(CurrencyPairGenesis::try_from_raw) - .collect::, _>>() - .map_err(GenesisStateError::currency_pair_genesis_parse_error)?; - let next_id = raw.next_id; - Ok(Self { - currency_pair_genesis, - next_id, - }) - } - - #[must_use] - pub fn into_raw(self) -> raw::GenesisState { - raw::GenesisState { - currency_pair_genesis: self - .currency_pair_genesis - .into_iter() - .map(CurrencyPairGenesis::into_raw) - .collect(), - next_id: self.next_id, - } - } - } - - #[derive(Debug, thiserror::Error)] - #[error(transparent)] - pub struct GenesisStateError(GenesisStateErrorKind); - - impl GenesisStateError { - #[must_use] - pub fn currency_pair_genesis_parse_error(err: CurrencyPairGenesisError) -> Self { - Self(GenesisStateErrorKind::CurrencyPairGenesisParseError(err)) - } - } - - #[derive(Debug, thiserror::Error)] - enum GenesisStateErrorKind { - #[error(transparent)] - CurrencyPairGenesisParseError(CurrencyPairGenesisError), - } - } -} diff --git a/crates/astria-core/src/slinky/abci.rs b/crates/astria-core/src/slinky/abci.rs new file mode 100644 index 0000000000..df897df052 --- /dev/null +++ b/crates/astria-core/src/slinky/abci.rs @@ -0,0 +1,32 @@ +pub mod v1 { + use std::collections::HashMap; + + use crate::generated::slinky::abci::v1 as raw; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct OracleVoteExtension { + pub prices: HashMap>, + } + + impl OracleVoteExtension { + #[must_use] + pub fn from_raw(raw: raw::OracleVoteExtension) -> Self { + Self { + prices: raw.prices, + } + } + + #[must_use] + pub fn into_raw(self) -> raw::OracleVoteExtension { + raw::OracleVoteExtension { + prices: self.prices, + } + } + } + + impl From for OracleVoteExtension { + fn from(raw: raw::OracleVoteExtension) -> Self { + Self::from_raw(raw) + } + } +} diff --git a/crates/astria-core/src/slinky/market_map.rs b/crates/astria-core/src/slinky/market_map.rs new file mode 100644 index 0000000000..d938ff9afe --- /dev/null +++ b/crates/astria-core/src/slinky/market_map.rs @@ -0,0 +1,423 @@ +pub mod v1 { + use std::{ + collections::HashMap, + str::FromStr, + }; + + use crate::{ + generated::slinky::marketmap::v1 as raw, + primitive::v1::{ + Address, + AddressError, + }, + slinky::types::v1::CurrencyPair, + }; + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct GenesisState { + pub market_map: MarketMap, + pub last_updated: u64, + pub params: Params, + } + + impl GenesisState { + /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. + /// + /// # Errors + /// + /// - if the `market_map` field is missing + /// - if the `market_map` field is invalid + /// - if the `params` field is missing + /// - if the `params` field is invalid + pub fn try_from_raw(raw: raw::GenesisState) -> Result { + let Some(market_map) = raw + .market_map + .map(MarketMap::try_from_raw) + .transpose() + .map_err(GenesisStateError::invalid_market_map)? + else { + return Err(GenesisStateError::missing_market_map()); + }; + let last_updated = raw.last_updated; + let Some(params) = raw + .params + .map(Params::try_from_raw) + .transpose() + .map_err(GenesisStateError::invalid_params)? + else { + return Err(GenesisStateError::missing_params()); + }; + Ok(Self { + market_map, + last_updated, + params, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::GenesisState { + raw::GenesisState { + market_map: Some(self.market_map.into_raw()), + last_updated: self.last_updated, + params: Some(self.params.into_raw()), + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct GenesisStateError(GenesisStateErrorKind); + + impl GenesisStateError { + #[must_use] + pub fn missing_market_map() -> Self { + Self(GenesisStateErrorKind::MissingMarketMap) + } + + #[must_use] + pub fn invalid_market_map(err: MarketMapError) -> Self { + Self(GenesisStateErrorKind::MarketMapParseError(err)) + } + + #[must_use] + pub fn missing_params() -> Self { + Self(GenesisStateErrorKind::MissingParams) + } + + #[must_use] + pub fn invalid_params(err: ParamsError) -> Self { + Self(GenesisStateErrorKind::ParamsParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum GenesisStateErrorKind { + #[error("missing market map")] + MissingMarketMap, + #[error(transparent)] + MarketMapParseError(#[from] MarketMapError), + #[error("missing params")] + MissingParams, + #[error(transparent)] + ParamsParseError(#[from] ParamsError), + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Params { + pub market_authorities: Vec
, + pub admin: Address, + } + + impl Params { + /// Converts from a raw protobuf `Params` to a native `Params`. + /// + /// # Errors + /// + /// - if any of the `market_authorities` addresses are invalid + /// - if the `admin` address is invalid + pub fn try_from_raw(raw: raw::Params) -> Result { + let market_authorities = raw + .market_authorities + .into_iter() + .map(|s| Address::from_str(&s)) + .collect::, _>>() + .map_err(ParamsError::market_authority_parse_error)?; + let admin = raw.admin.parse().map_err(ParamsError::admin_parse_error)?; + Ok(Self { + market_authorities, + admin, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::Params { + raw::Params { + market_authorities: self + .market_authorities + .into_iter() + .map(|a| a.to_string()) + .collect(), + admin: self.admin.to_string(), + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct ParamsError(ParamsErrorKind); + + impl ParamsError { + #[must_use] + pub fn market_authority_parse_error(err: AddressError) -> Self { + Self(ParamsErrorKind::MarketAuthorityParseError(err)) + } + + #[must_use] + pub fn admin_parse_error(err: AddressError) -> Self { + Self(ParamsErrorKind::AdminParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + pub enum ParamsErrorKind { + #[error("failed to parse market authority address")] + MarketAuthorityParseError(#[source] AddressError), + #[error("failed to parse admin address")] + AdminParseError(#[source] AddressError), + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Market { + pub ticker: Ticker, + pub provider_configs: Vec, + } + + impl Market { + /// Converts from a raw protobuf `Market` to a native `Market`. + /// + /// # Errors + /// + /// - if the `ticker` field is missing + /// - if the `ticker` field is invalid + /// - if any of the `provider_configs` are invalid + pub fn try_from_raw(raw: raw::Market) -> Result { + let Some(ticker) = raw + .ticker + .map(Ticker::try_from_raw) + .transpose() + .map_err(MarketError::invalid_ticker)? + else { + return Err(MarketError::missing_ticker()); + }; + + let provider_configs = raw + .provider_configs + .into_iter() + .map(ProviderConfig::try_from_raw) + .collect::, _>>() + .map_err(MarketError::invalid_provider_config)?; + Ok(Self { + ticker, + provider_configs, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::Market { + raw::Market { + ticker: Some(self.ticker.into_raw()), + provider_configs: self + .provider_configs + .into_iter() + .map(ProviderConfig::into_raw) + .collect(), + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct MarketError(MarketErrorKind); + + impl MarketError { + #[must_use] + pub fn missing_ticker() -> Self { + Self(MarketErrorKind::MissingTicker) + } + + #[must_use] + pub fn invalid_ticker(err: TickerError) -> Self { + Self(MarketErrorKind::TickerParseError(err)) + } + + #[must_use] + pub fn invalid_provider_config(err: ProviderConfigError) -> Self { + Self(MarketErrorKind::ProviderConfigParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum MarketErrorKind { + #[error("missing ticker")] + MissingTicker, + #[error(transparent)] + TickerParseError(#[from] TickerError), + #[error(transparent)] + ProviderConfigParseError(#[from] ProviderConfigError), + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Ticker { + pub currency_pair: CurrencyPair, + pub decimals: u64, + pub min_provider_count: u64, + pub enabled: bool, + pub metadata_json: String, + } + + impl Ticker { + /// Converts from a raw protobuf `Ticker` to a native `Ticker`. + /// + /// # Errors + /// + /// - if the `currency_pair` field is missing + /// - if the `currency_pair` field is invalid + pub fn try_from_raw(raw: raw::Ticker) -> Result { + let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { + return Err(TickerError::missing_currency_pair()); + }; + Ok(Self { + currency_pair, + decimals: raw.decimals, + min_provider_count: raw.min_provider_count, + enabled: raw.enabled, + metadata_json: raw.metadata_json, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::Ticker { + raw::Ticker { + currency_pair: Some(self.currency_pair.into_raw()), + decimals: self.decimals, + min_provider_count: self.min_provider_count, + enabled: self.enabled, + metadata_json: self.metadata_json, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct TickerError(TickerErrorKind); + + impl TickerError { + #[must_use] + pub fn missing_currency_pair() -> Self { + Self(TickerErrorKind::MissingCurrencyPair) + } + } + + #[derive(Debug, thiserror::Error)] + enum TickerErrorKind { + #[error("missing currency pair")] + MissingCurrencyPair, + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct ProviderConfig { + pub name: String, + pub off_chain_ticker: String, + pub normalize_by_pair: CurrencyPair, + pub invert: bool, + pub metadata_json: String, + } + + impl ProviderConfig { + /// Converts from a raw protobuf `ProviderConfig` to a native `ProviderConfig`. + /// + /// # Errors + /// + /// - if the `normalize_by_pair` field is missing + pub fn try_from_raw(raw: raw::ProviderConfig) -> Result { + let Some(normalize_by_pair) = raw.normalize_by_pair.map(CurrencyPair::from_raw) else { + return Err(ProviderConfigError::missing_normalize_by_pair()); + }; + Ok(Self { + name: raw.name, + off_chain_ticker: raw.off_chain_ticker, + normalize_by_pair, + invert: raw.invert, + metadata_json: raw.metadata_json, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::ProviderConfig { + raw::ProviderConfig { + name: self.name, + off_chain_ticker: self.off_chain_ticker, + normalize_by_pair: Some(self.normalize_by_pair.into_raw()), + invert: self.invert, + metadata_json: self.metadata_json, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct ProviderConfigError(ProviderConfigErrorKind); + + impl ProviderConfigError { + #[must_use] + pub fn missing_normalize_by_pair() -> Self { + Self(ProviderConfigErrorKind::MissingNormalizeByPair) + } + } + + #[derive(Debug, thiserror::Error)] + enum ProviderConfigErrorKind { + #[error("missing normalize by pair")] + MissingNormalizeByPair, + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct MarketMap { + pub markets: HashMap, + } + + impl MarketMap { + /// Converts from a raw protobuf `MarketMap` to a native `MarketMap`. + /// + /// # Errors + /// + /// - if any of the markets are invalid + /// - if any of the market names are invalid + pub fn try_from_raw(raw: raw::MarketMap) -> Result { + let mut markets = HashMap::new(); + for (k, v) in raw.markets { + let market = Market::try_from_raw(v) + .map_err(|e| MarketMapError::invalid_market(k.clone(), e))?; + markets.insert(k, market); + } + Ok(Self { + markets, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::MarketMap { + let markets = self + .markets + .into_iter() + .map(|(k, v)| (k, v.into_raw())) + .collect(); + raw::MarketMap { + markets, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct MarketMapError(MarketMapErrorKind); + + impl MarketMapError { + #[must_use] + pub fn invalid_market(name: String, err: MarketError) -> Self { + Self(MarketMapErrorKind::InvalidMarket(name, err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum MarketMapErrorKind { + #[error("invalid market {0}")] + InvalidMarket(String, MarketError), + } +} diff --git a/crates/astria-core/src/slinky/mod.rs b/crates/astria-core/src/slinky/mod.rs new file mode 100644 index 0000000000..619f008c3b --- /dev/null +++ b/crates/astria-core/src/slinky/mod.rs @@ -0,0 +1,4 @@ +pub mod abci; +pub mod market_map; +pub mod oracle; +pub mod types; diff --git a/crates/astria-core/src/slinky/oracle.rs b/crates/astria-core/src/slinky/oracle.rs new file mode 100644 index 0000000000..cc0a999203 --- /dev/null +++ b/crates/astria-core/src/slinky/oracle.rs @@ -0,0 +1,302 @@ +pub mod v1 { + use pbjson_types::Timestamp; + + use crate::{ + generated::slinky::oracle::v1 as raw, + slinky::types::v1::CurrencyPair, + }; + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone)] + pub struct QuotePrice { + pub price: u128, + pub block_timestamp: Timestamp, + pub block_height: u64, + } + + impl QuotePrice { + /// Converts from a raw protobuf `QuotePrice` to a native `QuotePrice`. + /// + /// # Errors + /// + /// - if the `price` field is invalid + /// - if the `block_timestamp` field is missing + pub fn try_from_raw(raw: raw::QuotePrice) -> Result { + let price = raw + .price + .parse() + .map_err(QuotePriceError::price_parse_error)?; + let Some(block_timestamp) = raw.block_timestamp else { + return Err(QuotePriceError::missing_block_timestamp()); + }; + let block_height = raw.block_height; + Ok(Self { + price, + block_timestamp, + block_height, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::QuotePrice { + raw::QuotePrice { + price: self.price.to_string(), + block_timestamp: Some(self.block_timestamp), + block_height: self.block_height, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct QuotePriceError(QuotePriceErrorKind); + + impl QuotePriceError { + #[must_use] + pub fn price_parse_error(err: std::num::ParseIntError) -> Self { + Self(QuotePriceErrorKind::PriceParseError(err)) + } + + #[must_use] + pub fn missing_block_timestamp() -> Self { + Self(QuotePriceErrorKind::MissingBlockTimestamp) + } + } + + #[derive(Debug, thiserror::Error)] + enum QuotePriceErrorKind { + #[error(transparent)] + PriceParseError(#[from] std::num::ParseIntError), + #[error("missing block timestamp")] + MissingBlockTimestamp, + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone)] + pub struct CurrencyPairState { + pub price: QuotePrice, + pub nonce: u64, + pub id: u64, + } + + impl CurrencyPairState { + /// Converts from a raw protobuf `CurrencyPairState` to a native `CurrencyPairState`. + /// + /// # Errors + /// + /// - if the `price` field is missing + /// - if the `price` field is invalid + pub fn try_from_raw(raw: raw::CurrencyPairState) -> Result { + let Some(price) = raw + .price + .map(QuotePrice::try_from_raw) + .transpose() + .map_err(CurrencyPairStateError::quote_price_parse_error)? + else { + return Err(CurrencyPairStateError::missing_price()); + }; + let nonce = raw.nonce; + let id = raw.id; + Ok(Self { + price, + nonce, + id, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::CurrencyPairState { + raw::CurrencyPairState { + price: Some(self.price.into_raw()), + nonce: self.nonce, + id: self.id, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct CurrencyPairStateError(CurrencyPairStateErrorKind); + + impl CurrencyPairStateError { + #[must_use] + pub fn missing_price() -> Self { + Self(CurrencyPairStateErrorKind::MissingPrice) + } + + #[must_use] + pub fn quote_price_parse_error(err: QuotePriceError) -> Self { + Self(CurrencyPairStateErrorKind::QuotePriceParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum CurrencyPairStateErrorKind { + #[error("missing price")] + MissingPrice, + #[error(transparent)] + QuotePriceParseError(QuotePriceError), + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone)] + pub struct CurrencyPairGenesis { + pub currency_pair: CurrencyPair, + pub currency_pair_price: QuotePrice, + pub id: u64, + pub nonce: u64, + } + + impl CurrencyPairGenesis { + #[must_use] + pub fn currency_pair(&self) -> &CurrencyPair { + &self.currency_pair + } + + #[must_use] + pub fn currency_pair_price(&self) -> &QuotePrice { + &self.currency_pair_price + } + + #[must_use] + pub fn id(&self) -> u64 { + self.id + } + + #[must_use] + pub fn nonce(&self) -> u64 { + self.nonce + } + + /// Converts from a raw protobuf `CurrencyPairGenesis` to a native + /// `CurrencyPairGenesis`. + /// + /// # Errors + /// + /// - if the `currency_pair` field is missing + /// - if the `currency_pair` field is invalid + /// - if the `currency_pair_price` field is missing + /// - if the `currency_pair_price` field is invalid + pub fn try_from_raw( + raw: raw::CurrencyPairGenesis, + ) -> Result { + let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { + return Err(CurrencyPairGenesisError::missing_currency_pair()); + }; + let Some(currency_pair_price) = raw + .currency_pair_price + .map(QuotePrice::try_from_raw) + .transpose() + .map_err(CurrencyPairGenesisError::quote_price_parse_error)? + else { + return Err(CurrencyPairGenesisError::missing_currency_pair_price()); + }; + let id = raw.id; + let nonce = raw.nonce; + Ok(Self { + currency_pair, + currency_pair_price, + id, + nonce, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::CurrencyPairGenesis { + raw::CurrencyPairGenesis { + currency_pair: Some(self.currency_pair.into_raw()), + currency_pair_price: Some(self.currency_pair_price.into_raw()), + id: self.id, + nonce: self.nonce, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct CurrencyPairGenesisError(CurrencyPairGenesisErrorKind); + + impl CurrencyPairGenesisError { + #[must_use] + pub fn missing_currency_pair() -> Self { + Self(CurrencyPairGenesisErrorKind::MissingCurrencyPair) + } + + #[must_use] + pub fn missing_currency_pair_price() -> Self { + Self(CurrencyPairGenesisErrorKind::MissingCurrencyPairPrice) + } + + #[must_use] + pub fn quote_price_parse_error(err: QuotePriceError) -> Self { + Self(CurrencyPairGenesisErrorKind::QuotePriceParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum CurrencyPairGenesisErrorKind { + #[error("missing currency pair")] + MissingCurrencyPair, + #[error("missing currency pair price")] + MissingCurrencyPairPrice, + #[error(transparent)] + QuotePriceParseError(QuotePriceError), + } + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone)] + pub struct GenesisState { + pub currency_pair_genesis: Vec, + pub next_id: u64, + } + + impl GenesisState { + /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. + /// + /// # Errors + /// + /// - if any of the `currency_pair_genesis` are invalid + pub fn try_from_raw(raw: raw::GenesisState) -> Result { + let currency_pair_genesis = raw + .currency_pair_genesis + .into_iter() + .map(CurrencyPairGenesis::try_from_raw) + .collect::, _>>() + .map_err(GenesisStateError::currency_pair_genesis_parse_error)?; + let next_id = raw.next_id; + Ok(Self { + currency_pair_genesis, + next_id, + }) + } + + #[must_use] + pub fn into_raw(self) -> raw::GenesisState { + raw::GenesisState { + currency_pair_genesis: self + .currency_pair_genesis + .into_iter() + .map(CurrencyPairGenesis::into_raw) + .collect(), + next_id: self.next_id, + } + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct GenesisStateError(GenesisStateErrorKind); + + impl GenesisStateError { + #[must_use] + pub fn currency_pair_genesis_parse_error(err: CurrencyPairGenesisError) -> Self { + Self(GenesisStateErrorKind::CurrencyPairGenesisParseError(err)) + } + } + + #[derive(Debug, thiserror::Error)] + enum GenesisStateErrorKind { + #[error(transparent)] + CurrencyPairGenesisParseError(CurrencyPairGenesisError), + } +} diff --git a/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/slinky/types.rs new file mode 100644 index 0000000000..a3f04f71e0 --- /dev/null +++ b/crates/astria-core/src/slinky/types.rs @@ -0,0 +1,87 @@ +pub mod v1 { + use crate::generated::slinky::types::v1 as raw; + + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct CurrencyPair { + base: String, + quote: String, + } + + impl CurrencyPair { + #[must_use] + pub fn new(base: String, quote: String) -> Self { + Self { + base, + quote, + } + } + + #[must_use] + pub fn base(&self) -> &str { + &self.base + } + + #[must_use] + pub fn quote(&self) -> &str { + &self.quote + } + + #[must_use] + pub fn from_raw(raw: raw::CurrencyPair) -> Self { + Self { + base: raw.base, + quote: raw.quote, + } + } + + #[must_use] + pub fn into_raw(self) -> raw::CurrencyPair { + raw::CurrencyPair { + base: self.base, + quote: self.quote, + } + } + } + + impl std::fmt::Display for CurrencyPair { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}/{}", self.base, self.quote) + } + } + + impl std::str::FromStr for CurrencyPair { + type Err = CurrencyPairParseError; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split('/').collect(); + if parts.len() != 2 { + return Err(CurrencyPairParseError::invalid_currency_pair_string(s)); + } + + Ok(Self { + base: parts[0].to_string(), + quote: parts[1].to_string(), + }) + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct CurrencyPairParseError(CurrencyPairParseErrorKind); + + #[derive(Debug, thiserror::Error)] + pub enum CurrencyPairParseErrorKind { + #[error("invalid currency pair string: {0}")] + InvalidCurrencyPairString(String), + } + + impl CurrencyPairParseError { + #[must_use] + pub fn invalid_currency_pair_string(s: &str) -> Self { + Self(CurrencyPairParseErrorKind::InvalidCurrencyPairString( + s.to_string(), + )) + } + } +} diff --git a/proto/vendored/slinky/marketmap/v1/tx.proto b/proto/vendored/slinky/marketmap/v1/tx.proto deleted file mode 100644 index 3877a3e295..0000000000 --- a/proto/vendored/slinky/marketmap/v1/tx.proto +++ /dev/null @@ -1,120 +0,0 @@ -syntax = "proto3"; -package slinky.marketmap.v1; - -import "cosmos_sdk/cosmos/msg/v1/msg.proto"; -import "cosmos_sdk/cosmos_proto/cosmos.proto"; -import "slinky/marketmap/v1/market.proto"; -import "slinky/marketmap/v1/params.proto"; - -option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; - -// Msg is the message service for the x/marketmap module. -service Msg { - option (cosmos.msg.v1.service) = true; - - // CreateMarkets creates markets from the given message. - rpc CreateMarkets(MsgCreateMarkets) returns (MsgCreateMarketsResponse); - - // UpdateMarkets updates markets from the given message. - rpc UpdateMarkets(MsgUpdateMarkets) returns (MsgUpdateMarketsResponse); - - // UpdateParams defines a method for updating the x/marketmap module - // parameters. - rpc UpdateParams(MsgParams) returns (MsgParamsResponse); - - // RemoveMarketAuthorities defines a method for removing market authorities - // from the x/marketmap module. the signer must be the admin. - rpc RemoveMarketAuthorities(MsgRemoveMarketAuthorities) returns (MsgRemoveMarketAuthoritiesResponse); - - // UpsertMarkets wraps both Create / Update markets into a single message. Specifically - // if a market does not exist it will be created, otherwise it will be updated. The response - // will be a map between ticker -> updated. - rpc UpsertMarkets(MsgUpsertMarkets) returns (MsgUpsertMarketsResponse); -} - -// MsgUpsertMarkets defines a message carrying a payload for performing market upserts (update or -// create if does not exist) in the x/marketmap module. -message MsgUpsertMarkets { - option (cosmos.msg.v1.signer) = "authority"; - - // Authority is the signer of this transaction. This authority must be - // authorized by the module to execute the message. - string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - - // CreateMarkets is the list of all markets to be created for the given - // transaction. - repeated Market markets = 2; -} - -// MsgUpsertMarketsResponse is the response from the UpsertMarkets API in the x/marketmap module. -message MsgUpsertMarketsResponse { - // UpdatedMarkets is a map between the ticker and whether the market was updated. - map market_updates = 1; -} - -// MsgCreateMarkets defines a message carrying a payload for creating markets in -// the x/marketmap module. -message MsgCreateMarkets { - option (cosmos.msg.v1.signer) = "authority"; - - // Authority is the signer of this transaction. This authority must be - // authorized by the module to execute the message. - string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - - // CreateMarkets is the list of all markets to be created for the given - // transaction. - repeated Market create_markets = 2; -} - -// MsgUpdateMarketMapResponse is the response message for MsgUpdateMarketMap. -message MsgCreateMarketsResponse {} - -// MsgUpdateMarkets defines a message carrying a payload for updating the -// x/marketmap module. -message MsgUpdateMarkets { - option (cosmos.msg.v1.signer) = "authority"; - - // Authority is the signer of this transaction. This authority must be - // authorized by the module to execute the message. - string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - - // UpdateMarkets is the list of all markets to be updated for the given - // transaction. - repeated Market update_markets = 2; -} - -// MsgUpdateMarketsResponse is the response message for MsgUpdateMarkets. -message MsgUpdateMarketsResponse {} - -// MsgParams defines the Msg/Params request type. It contains the -// new parameters for the x/marketmap module. -message MsgParams { - option (cosmos.msg.v1.signer) = "authority"; - - // Params defines the new parameters for the x/marketmap module. - Params params = 1; - // Authority defines the authority that is updating the x/marketmap module - // parameters. - string authority = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; -} - -// MsgParamsResponse defines the Msg/Params response type. -message MsgParamsResponse {} - -// MsgRemoveMarketAuthorities defines the Msg/RemoveMarketAuthoritiesResponse -// request type. It contains the new addresses to remove from the list of -// authorities -message MsgRemoveMarketAuthorities { - option (cosmos.msg.v1.signer) = "admin"; - - // RemoveAddresses is the list of addresses to remove. - repeated string remove_addresses = 1; - - // Admin defines the authority that is the x/marketmap - // Admin account. This account is set in the module parameters. - string admin = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; -} - -// MsgRemoveMarketAuthoritiesResponse defines the -// Msg/RemoveMarketAuthoritiesResponse response type. -message MsgRemoveMarketAuthoritiesResponse {} diff --git a/proto/vendored/slinky/oracle/v1/tx.proto b/proto/vendored/slinky/oracle/v1/tx.proto deleted file mode 100644 index 8a4c937d87..0000000000 --- a/proto/vendored/slinky/oracle/v1/tx.proto +++ /dev/null @@ -1,60 +0,0 @@ -syntax = "proto3"; -package slinky.oracle.v1; - -import "cosmos_sdk/cosmos/msg/v1/msg.proto"; -import "cosmos_sdk/cosmos_proto/cosmos.proto"; -import "slinky/oracle/v1/genesis.proto"; -import "slinky/types/v1/currency_pair.proto"; - -option go_package = "github.com/skip-mev/slinky/x/oracle/types"; - -// Msg is the message service for the x/oracle module. -service Msg { - option (cosmos.msg.v1.service) = true; - - // AddCurrencyPairs will be used only by governance to update the set of - // available CurrencyPairs. Given a set of CurrencyPair objects, update - // the available currency pairs in the module . - rpc AddCurrencyPairs(MsgAddCurrencyPairs) returns (MsgAddCurrencyPairsResponse); - - // RemoveCurrencyPairs will be used explicitly by governance to remove the - // given set of currency-pairs from the module's state. Thus these - // CurrencyPairs will no longer have price-data available from this module. - rpc RemoveCurrencyPairs(MsgRemoveCurrencyPairs) returns (MsgRemoveCurrencyPairsResponse); -} - -// Given an authority + a set of CurrencyPairs, the x/oracle module will -// check to see that the authority has permissions to update the set of -// CurrencyPairs tracked in the oracle, and add the given CurrencyPairs to be -// tracked in each VoteExtension -message MsgAddCurrencyPairs { - option (cosmos.msg.v1.signer) = "authority"; - - // authority is the address of the account that is authorized to update the - // x/oracle's CurrencyPairs - string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // set of CurrencyPairs to be added to the module (+ prices if they are to be - // set) - repeated slinky.types.v1.CurrencyPair currency_pairs = 2; -} - -message MsgAddCurrencyPairsResponse {} - -// Given an authority + a set of CurrencyPairIDs, the x/oracle module's message -// service will remove all of the CurrencyPairs identified by each -// CurrencyPairID in the request from state. Notice, if a given currency-pair -// does not exist in state, the module ignores that currency-pair and continues -// removing the rest. -message MsgRemoveCurrencyPairs { - option (cosmos.msg.v1.signer) = "authority"; - - // authority is the address of the account that is authorized to update the - // x/oracle's CurrencyPairs - string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - - // currency_pair_ids are the stringified representation of a currency-pairs - // (base/quote) to be removed from the module's state - repeated string currency_pair_ids = 2; -} - -message MsgRemoveCurrencyPairsResponse {} From 5f9ea4f8f506d0dd69bb8ed598738f6fbcfae2df Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 23:11:59 -0400 Subject: [PATCH 22/89] attempt to fix sequencer genesis in chart --- charts/sequencer/Chart.yaml | 2 +- .../files/cometbft/config/genesis.json | 21 ++++++++++++++++++- charts/sequencer/values.yaml | 3 ++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/charts/sequencer/Chart.yaml b/charts/sequencer/Chart.yaml index f127768d48..64f5b4cb6d 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.18.0 +version: 0.18.1 # 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..3b85a61289 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -41,13 +41,32 @@ {{- if $index }},{{- end }} {{ include "sequencer.address" $value }} {{- end }} - ] + ], + "market_map": { + "market_map": { + "markets": {} + }, + "last_updated": 0, + "params": { + "market_authorities": [], + "admin": { + "bech32m": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} + } + } + }, + "oracle": { + "currency_pair_genesis": [], + "next_id": 0, + } {{- if not .Values.global.dev }} {{- else }} {{- end}} }, "chain_id": "{{ .Values.genesis.chainId }}", "consensus_params": { + "abci": { + "vote_extensions_enable_height": "1", + }, "block": { "max_bytes": " {{ .Values.genesis.consensusParams.blockMaxBytes }}", "max_gas": "{{ .Values.genesis.consensusParams.blockMaxGas }}" diff --git a/charts/sequencer/values.yaml b/charts/sequencer/values.yaml index ef5b620cb1..15de582199 100644 --- a/charts/sequencer/values.yaml +++ b/charts/sequencer/values.yaml @@ -27,6 +27,7 @@ genesis: addressPrefixes: base: "astria" authoritySudoAddress: "" + marketAdminAddress: "" nativeAssetBaseDenomination: nria allowedFeeAssets: [] # - nria @@ -211,7 +212,7 @@ ports: cometbftRpc: 26657 cometbftMetrics: 26660 sequencerABCI: 26658 - sequencerGrpc: 8080 + sequencerGrpc: 8080 # TODO: change to 9090 if running with oracle sidecar relayerRpc: 2450 sequencerMetrics: 9000 From 078b11644740d963261b4747efa4ba851fc1c72d Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 4 Jul 2024 23:15:33 -0400 Subject: [PATCH 23/89] lint chart --- charts/sequencer/values.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/charts/sequencer/values.yaml b/charts/sequencer/values.yaml index 15de582199..4ca9c8e9a7 100644 --- a/charts/sequencer/values.yaml +++ b/charts/sequencer/values.yaml @@ -212,7 +212,8 @@ ports: cometbftRpc: 26657 cometbftMetrics: 26660 sequencerABCI: 26658 - sequencerGrpc: 8080 # TODO: change to 9090 if running with oracle sidecar + # TODO: change to 9090 if running with oracle sidecar + sequencerGrpc: 8080 relayerRpc: 2450 sequencerMetrics: 9000 From 0ea34d9d295c093d1471a8d36d1b7243f5c19086 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 5 Jul 2024 13:36:37 -0400 Subject: [PATCH 24/89] maybe fix genesis in chart --- charts/sequencer/files/cometbft/config/genesis.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 3b85a61289..31f5258c6d 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -56,7 +56,7 @@ }, "oracle": { "currency_pair_genesis": [], - "next_id": 0, + "next_id": 0 } {{- if not .Values.global.dev }} {{- else }} @@ -65,7 +65,7 @@ "chain_id": "{{ .Values.genesis.chainId }}", "consensus_params": { "abci": { - "vote_extensions_enable_height": "1", + "vote_extensions_enable_height": "1" }, "block": { "max_bytes": " {{ .Values.genesis.consensusParams.blockMaxBytes }}", From 63b6b2c54a8ecd579f66368d9f5d8a468a8c2076 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 5 Jul 2024 13:37:00 -0400 Subject: [PATCH 25/89] lint chart --- charts/sequencer/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/sequencer/values.yaml b/charts/sequencer/values.yaml index 4ca9c8e9a7..f115872eb9 100644 --- a/charts/sequencer/values.yaml +++ b/charts/sequencer/values.yaml @@ -213,7 +213,7 @@ ports: cometbftMetrics: 26660 sequencerABCI: 26658 # TODO: change to 9090 if running with oracle sidecar - sequencerGrpc: 8080 + sequencerGrpc: 8080 relayerRpc: 2450 sequencerMetrics: 9000 From 72b9fc45cea7ac5648687d63d0b8172d9fb159a4 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 5 Jul 2024 14:00:57 -0400 Subject: [PATCH 26/89] cleanup --- .gitignore | 1 + crates/astria-sequencer/Cargo.toml | 2 +- .../astria-sequencer/app-genesis-state.json | 156 ------------------ .../src/app/vote_extension.rs | 23 ++- .../src/slinky/oracle/component.rs | 1 - .../cosmos_sdk/cosmos/msg/v1/msg.proto | 27 --- 6 files changed, 23 insertions(+), 187 deletions(-) delete mode 100644 crates/astria-sequencer/app-genesis-state.json delete mode 100644 proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto diff --git a/.gitignore b/.gitignore index 3842531bc5..14cd97d988 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ target/ charts/*/charts crates/astria-bridge-withdrawer/src/withdrawer/ethereum/generated/ +crates/astria-sequencer/app-genesis-state.json diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index 9fbb786771..27e3c8a129 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -29,7 +29,7 @@ telemetry = { package = "astria-telemetry", path = "../astria-telemetry", featur anyhow = "1" borsh = { version = "1", features = ["derive"] } -# `is_sorted_by` is available in rust 1.81.0, but we haven't updated or msrv yet +# `is_sorted_by` is available in rust 1.81.0, but we haven't updated our msrv yet is_sorted = "0.1.1" matchit = "0.7.2" priority-queue = "2.0.2" diff --git a/crates/astria-sequencer/app-genesis-state.json b/crates/astria-sequencer/app-genesis-state.json deleted file mode 100644 index f99bfe1ba6..0000000000 --- a/crates/astria-sequencer/app-genesis-state.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "address_prefixes": { - "base": "astria" - }, - "accounts": [ - { - "address": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "balance": 1000000000000000000 - }, - { - "address": { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - }, - "balance": 1000000000000000000 - }, - { - "address": { - "bech32m": "astria1vpcfutferpjtwv457r63uwr6hdm8gwr3pxt5ny" - }, - "balance": 1000000000000000000 - } - ], - "authority_sudo_address": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "ibc_sudo_address": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "ibc_relayer_addresses": [ - { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - } - ], - "native_asset_base_denomination": "nria", - "ibc_params": { - "ibcEnabled": true, - "inboundIcs20TransfersEnabled": true, - "outboundIcs20TransfersEnabled": true - }, - "allowed_fee_assets": [ - "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 - }, - "market_map": { - "market_map": { - "markets": { - "BTC/USD": { - "ticker": { - "currency_pair": { - "base": "BTC", - "quote": "USD" - }, - "decimals": 8, - "min_provider_count": 3, - "enabled": true, - "metadata_json": "" - }, - "provider_configs": [ - { - "name": "coingecko_api", - "off_chain_ticker": "bitcoin/usd", - "normalize_by_pair": { - "base": "USDT", - "quote": "USD" - }, - "invert": false, - "metadata_json": "" - } - ] - }, - "ETH/USD": { - "ticker": { - "currency_pair": { - "base": "ETH", - "quote": "USD" - }, - "decimals": 8, - "min_provider_count": 3, - "enabled": true, - "metadata_json": "" - }, - "provider_configs": [ - { - "name": "coingecko_api", - "off_chain_ticker": "ethereum/usd", - "normalize_by_pair": { - "base": "USDT", - "quote": "USD" - }, - "invert": false, - "metadata_json": "" - } - ] - } - } - }, - "last_updated": 0, - "params": { - "market_authorities": [ - { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - } - ], - "admin": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - } - } - }, - "oracle": { - "currency_pair_genesis": [ - { - "currency_pair": { - "base": "BTC", - "quote": "USD" - }, - "currency_pair_price": { - "price": 5834065777, - "block_timestamp": "2024-07-04T19:46:35+00:00", - "block_height": 0 - }, - "id": 0, - "nonce": 0 - }, - { - "currency_pair": { - "base": "ETH", - "quote": "USD" - }, - "currency_pair_price": { - "price": 3138872234, - "block_timestamp": "2024-07-04T19:46:35+00:00", - "block_height": 0 - }, - "id": 1, - "nonce": 0 - } - ], - "next_id": 2 - } -} \ No newline at end of file diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index a081f7af91..a642d207be 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -469,8 +469,6 @@ pub(crate) async fn apply_prices_from_vote_extensions( .await .context("failed to aggregate oracle votes")?; - // TODO: if a currency pair exists in the state, but isn't in the prices, - // is it considered "removed"? for (currency_pair, price) in prices { let price = QuotePrice { price, @@ -558,3 +556,24 @@ async fn aggregate_oracle_votes( Ok(prices) } + +#[cfg(test)] +mod test { + use super::*; + use tendermint::abci; + + #[tokio::test] + async fn verify_vote_extension_proposal_phase_ok() { + let handler = Handler::new(None, 1000); + let storage = cnidarium::TempStorage::new().await.unwrap(); + let snapshot = storage.latest_snapshot(); + + let vote = abci::request::VerifyVoteExtension { + hash: [0u8; 32].to_vec().try_into().unwrap(), + vote_extension: vec![].into(), + validator_address: [1u8; 20].to_vec().try_into().unwrap(), + height: 1u32.into(), + }; + assert_eq!(handler.verify_vote_extension(&snapshot, vote, true).await, abci::response::VerifyVoteExtension::Accept); + } +} diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index 0b9ee60911..4f01cb8ad2 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -14,7 +14,6 @@ use tracing::instrument; use super::state_ext::StateWriteExt; use crate::component::Component; -// TODO do we want to put all slinky stuff in one component? #[derive(Default)] pub(crate) struct OracleComponent; diff --git a/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto b/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto deleted file mode 100644 index 7e88f27d3a..0000000000 --- a/proto/vendored/cosmos_sdk/cosmos/msg/v1/msg.proto +++ /dev/null @@ -1,27 +0,0 @@ -syntax = "proto3"; -package cosmos.msg.v1; - -import "google/protobuf/descriptor.proto"; - -// TODO(fdymylja): once we fully migrate to protov2 the go_package needs to be updated. -// We need this right now because gogoproto codegen needs to import the extension. -option go_package = "github.com/cosmos/cosmos-sdk/types/msgservice"; -extend google.protobuf.ServiceOptions { - // service indicates that the service is a Msg service and that requests - // must be transported via blockchain transactions rather than gRPC. - // Tooling can use this annotation to distinguish between Msg services and - // other types of services via reflection. - bool service = 11110000; -} -extend google.protobuf.MessageOptions { - // signer must be used in cosmos messages in order - // to signal to external clients which fields in a - // given cosmos message must be filled with signer - // information (address). - // The field must be the protobuf name of the message - // field extended with this MessageOption. - // The field must either be of string kind, or of message - // kind in case the signer information is contained within - // a message inside the cosmos message. - repeated string signer = 11110000; -} From 5b6a99cde86562b6f446ea2db71cfacc82e3de1a Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 5 Jul 2024 14:10:48 -0400 Subject: [PATCH 27/89] charts --- charts/sequencer/files/cometbft/config/genesis.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 31f5258c6d..32e2d57aa2 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -50,7 +50,7 @@ "params": { "market_authorities": [], "admin": { - "bech32m": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" } } }, From 24e916faf5e60837751d8a8cd9a336953f2c787a Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 5 Jul 2024 15:01:37 -0400 Subject: [PATCH 28/89] fmt --- .../src/app/vote_extension.rs | 30 ++++++++++++++++--- .../src/slinky/marketmap/component.rs | 3 ++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index a642d207be..1cc456e649 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -556,12 +556,13 @@ async fn aggregate_oracle_votes( Ok(prices) } - + #[cfg(test)] mod test { - use super::*; use tendermint::abci; + use super::*; + #[tokio::test] async fn verify_vote_extension_proposal_phase_ok() { let handler = Handler::new(None, 1000); @@ -574,6 +575,27 @@ mod test { validator_address: [1u8; 20].to_vec().try_into().unwrap(), height: 1u32.into(), }; - assert_eq!(handler.verify_vote_extension(&snapshot, vote, true).await, abci::response::VerifyVoteExtension::Accept); - } + assert_eq!( + handler.verify_vote_extension(&snapshot, vote, true).await, + abci::response::VerifyVoteExtension::Accept + ); + } + + #[tokio::test] + async fn verify_vote_extension_not_proposal_phase_ok() { + let handler = Handler::new(None, 1000); + let storage = cnidarium::TempStorage::new().await.unwrap(); + let snapshot = storage.latest_snapshot(); + + let vote = abci::request::VerifyVoteExtension { + hash: [0u8; 32].to_vec().try_into().unwrap(), + vote_extension: vec![].into(), + validator_address: [1u8; 20].to_vec().try_into().unwrap(), + height: 1u32.into(), + }; + assert_eq!( + handler.verify_vote_extension(&snapshot, vote, false).await, + abci::response::VerifyVoteExtension::Accept + ); + } } diff --git a/crates/astria-sequencer/src/slinky/marketmap/component.rs b/crates/astria-sequencer/src/slinky/marketmap/component.rs index 64838797af..7c1c8c8174 100644 --- a/crates/astria-sequencer/src/slinky/marketmap/component.rs +++ b/crates/astria-sequencer/src/slinky/marketmap/component.rs @@ -22,6 +22,9 @@ impl Component for MarketMapComponent { #[instrument(name = "MarketMapComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { + // TODO: put market map authorites and admin in state; + // only required for related actions however + state .put_market_map(app_state.market_map().market_map.clone()) .context("failed to put market map")?; From 0b6fd9bff95dd56335b638c8c3412b5ad3029cd7 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Sat, 6 Jul 2024 12:46:16 -0400 Subject: [PATCH 29/89] add oracle config values to chart --- charts/sequencer/templates/configmaps.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index 2f49d4416c..f0374a9514 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,6 +73,9 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" + ASTRIA_SEQUENCER_ORACLE_ENABLED: false + ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8080" + ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT: 1000 {{- if not .Values.global.dev }} {{- else }} {{- end }} From 4192612f61a16a3e63dbe660a80cc38b3def0bd4 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Sat, 6 Jul 2024 12:56:21 -0400 Subject: [PATCH 30/89] maybe fix chart --- charts/sequencer/templates/configmaps.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index f0374a9514..93551fcfaf 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,9 +73,9 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" - ASTRIA_SEQUENCER_ORACLE_ENABLED: false + ASTRIA_SEQUENCER_ORACLE_ENABLED: "false" ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8080" - ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT: 1000 + ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT: "1000" {{- if not .Values.global.dev }} {{- else }} {{- end }} From 936bf0044eae994145f0cd6048eec4355b9bbe4b Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 9 Jul 2024 10:07:40 -0400 Subject: [PATCH 31/89] update protos --- .../generated/slinky.marketmap.v1.serde.rs | 30 +++++++++---------- .../src/generated/slinky.oracle.v1.serde.rs | 20 ++++++------- .../src/generated/slinky.service.v1.serde.rs | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs b/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs index 9b0609430f..13111ee1ce 100644 --- a/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs +++ b/crates/astria-core/src/generated/slinky.marketmap.v1.serde.rs @@ -17,11 +17,11 @@ impl serde::Serialize for GenesisState { } let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.GenesisState", len)?; if let Some(v) = self.market_map.as_ref() { - struct_ser.serialize_field("market_map", v)?; + struct_ser.serialize_field("marketMap", v)?; } if self.last_updated != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("last_updated", ToString::to_string(&self.last_updated).as_str())?; + struct_ser.serialize_field("lastUpdated", ToString::to_string(&self.last_updated).as_str())?; } if let Some(v) = self.params.as_ref() { struct_ser.serialize_field("params", v)?; @@ -213,7 +213,7 @@ impl serde::Serialize for LastUpdatedResponse { let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.LastUpdatedResponse", len)?; if self.last_updated != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("last_updated", ToString::to_string(&self.last_updated).as_str())?; + struct_ser.serialize_field("lastUpdated", ToString::to_string(&self.last_updated).as_str())?; } struct_ser.end() } @@ -313,7 +313,7 @@ impl serde::Serialize for Market { struct_ser.serialize_field("ticker", v)?; } if !self.provider_configs.is_empty() { - struct_ser.serialize_field("provider_configs", &self.provider_configs)?; + struct_ser.serialize_field("providerConfigs", &self.provider_configs)?; } struct_ser.end() } @@ -586,14 +586,14 @@ impl serde::Serialize for MarketMapResponse { } let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MarketMapResponse", len)?; if let Some(v) = self.market_map.as_ref() { - struct_ser.serialize_field("market_map", v)?; + struct_ser.serialize_field("marketMap", v)?; } if self.last_updated != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("last_updated", ToString::to_string(&self.last_updated).as_str())?; + struct_ser.serialize_field("lastUpdated", ToString::to_string(&self.last_updated).as_str())?; } if !self.chain_id.is_empty() { - struct_ser.serialize_field("chain_id", &self.chain_id)?; + struct_ser.serialize_field("chainId", &self.chain_id)?; } struct_ser.end() } @@ -711,7 +711,7 @@ impl serde::Serialize for MarketRequest { } let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.MarketRequest", len)?; if let Some(v) = self.currency_pair.as_ref() { - struct_ser.serialize_field("currency_pair", v)?; + struct_ser.serialize_field("currencyPair", v)?; } struct_ser.end() } @@ -897,7 +897,7 @@ impl serde::Serialize for Params { } let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.Params", len)?; if !self.market_authorities.is_empty() { - struct_ser.serialize_field("market_authorities", &self.market_authorities)?; + struct_ser.serialize_field("marketAuthorities", &self.market_authorities)?; } if !self.admin.is_empty() { struct_ser.serialize_field("admin", &self.admin)?; @@ -1180,16 +1180,16 @@ impl serde::Serialize for ProviderConfig { struct_ser.serialize_field("name", &self.name)?; } if !self.off_chain_ticker.is_empty() { - struct_ser.serialize_field("off_chain_ticker", &self.off_chain_ticker)?; + struct_ser.serialize_field("offChainTicker", &self.off_chain_ticker)?; } if let Some(v) = self.normalize_by_pair.as_ref() { - struct_ser.serialize_field("normalize_by_pair", v)?; + struct_ser.serialize_field("normalizeByPair", v)?; } if self.invert { struct_ser.serialize_field("invert", &self.invert)?; } if !self.metadata_json.is_empty() { - struct_ser.serialize_field("metadata_JSON", &self.metadata_json)?; + struct_ser.serialize_field("metadataJSON", &self.metadata_json)?; } struct_ser.end() } @@ -1339,7 +1339,7 @@ impl serde::Serialize for Ticker { } let mut struct_ser = serializer.serialize_struct("slinky.marketmap.v1.Ticker", len)?; if let Some(v) = self.currency_pair.as_ref() { - struct_ser.serialize_field("currency_pair", v)?; + struct_ser.serialize_field("currencyPair", v)?; } if self.decimals != 0 { #[allow(clippy::needless_borrow)] @@ -1347,13 +1347,13 @@ impl serde::Serialize for Ticker { } if self.min_provider_count != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("min_provider_count", ToString::to_string(&self.min_provider_count).as_str())?; + struct_ser.serialize_field("minProviderCount", ToString::to_string(&self.min_provider_count).as_str())?; } if self.enabled { struct_ser.serialize_field("enabled", &self.enabled)?; } if !self.metadata_json.is_empty() { - struct_ser.serialize_field("metadata_JSON", &self.metadata_json)?; + struct_ser.serialize_field("metadataJSON", &self.metadata_json)?; } struct_ser.end() } diff --git a/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs b/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs index 852ce75a99..d5279f07ce 100644 --- a/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs +++ b/crates/astria-core/src/generated/slinky.oracle.v1.serde.rs @@ -20,10 +20,10 @@ impl serde::Serialize for CurrencyPairGenesis { } let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.CurrencyPairGenesis", len)?; if let Some(v) = self.currency_pair.as_ref() { - struct_ser.serialize_field("currency_pair", v)?; + struct_ser.serialize_field("currencyPair", v)?; } if let Some(v) = self.currency_pair_price.as_ref() { - struct_ser.serialize_field("currency_pair_price", v)?; + struct_ser.serialize_field("currencyPairPrice", v)?; } if self.nonce != 0 { #[allow(clippy::needless_borrow)] @@ -295,11 +295,11 @@ impl serde::Serialize for GenesisState { } let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GenesisState", len)?; if !self.currency_pair_genesis.is_empty() { - struct_ser.serialize_field("currency_pair_genesis", &self.currency_pair_genesis)?; + struct_ser.serialize_field("currencyPairGenesis", &self.currency_pair_genesis)?; } if self.next_id != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("next_id", ToString::to_string(&self.next_id).as_str())?; + struct_ser.serialize_field("nextId", ToString::to_string(&self.next_id).as_str())?; } struct_ser.end() } @@ -476,7 +476,7 @@ impl serde::Serialize for GetAllCurrencyPairsResponse { } let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetAllCurrencyPairsResponse", len)?; if !self.currency_pairs.is_empty() { - struct_ser.serialize_field("currency_pairs", &self.currency_pairs)?; + struct_ser.serialize_field("currencyPairs", &self.currency_pairs)?; } struct_ser.end() } @@ -639,7 +639,7 @@ impl serde::Serialize for GetCurrencyPairMappingResponse { } let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetCurrencyPairMappingResponse", len)?; if !self.currency_pair_mapping.is_empty() { - struct_ser.serialize_field("currency_pair_mapping", &self.currency_pair_mapping)?; + struct_ser.serialize_field("currencyPairMapping", &self.currency_pair_mapping)?; } struct_ser.end() } @@ -734,7 +734,7 @@ impl serde::Serialize for GetPriceRequest { } let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetPriceRequest", len)?; if let Some(v) = self.currency_pair.as_ref() { - struct_ser.serialize_field("currency_pair", v)?; + struct_ser.serialize_field("currencyPair", v)?; } struct_ser.end() } @@ -977,7 +977,7 @@ impl serde::Serialize for GetPricesRequest { } let mut struct_ser = serializer.serialize_struct("slinky.oracle.v1.GetPricesRequest", len)?; if !self.currency_pair_ids.is_empty() { - struct_ser.serialize_field("currency_pair_ids", &self.currency_pair_ids)?; + struct_ser.serialize_field("currencyPairIds", &self.currency_pair_ids)?; } struct_ser.end() } @@ -1169,11 +1169,11 @@ impl serde::Serialize for QuotePrice { struct_ser.serialize_field("price", &self.price)?; } if let Some(v) = self.block_timestamp.as_ref() { - struct_ser.serialize_field("block_timestamp", v)?; + struct_ser.serialize_field("blockTimestamp", v)?; } if self.block_height != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("block_height", ToString::to_string(&self.block_height).as_str())?; + struct_ser.serialize_field("blockHeight", ToString::to_string(&self.block_height).as_str())?; } struct_ser.end() } diff --git a/crates/astria-core/src/generated/slinky.service.v1.serde.rs b/crates/astria-core/src/generated/slinky.service.v1.serde.rs index fc98e5a6c9..9c11bee42c 100644 --- a/crates/astria-core/src/generated/slinky.service.v1.serde.rs +++ b/crates/astria-core/src/generated/slinky.service.v1.serde.rs @@ -82,7 +82,7 @@ impl serde::Serialize for QueryMarketMapResponse { } let mut struct_ser = serializer.serialize_struct("slinky.service.v1.QueryMarketMapResponse", len)?; if let Some(v) = self.market_map.as_ref() { - struct_ser.serialize_field("market_map", v)?; + struct_ser.serialize_field("marketMap", v)?; } struct_ser.end() } From dd2b936d53b96d93fbfd6883bcc9ab3511b9a99f Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 12 Jul 2024 18:14:26 -0400 Subject: [PATCH 32/89] address comments --- charts/sequencer/templates/configmaps.yaml | 2 +- crates/astria-sequencer/justfile | 4 +- crates/astria-sequencer/local.env.example | 2 +- crates/astria-sequencer/src/app/mod.rs | 40 +++++------ crates/astria-sequencer/src/app/test_utils.rs | 2 +- crates/astria-sequencer/src/app/tests_app.rs | 2 +- .../src/app/vote_extension.rs | 60 +++++------------ crates/astria-sequencer/src/config.rs | 2 +- crates/astria-sequencer/src/grpc/slinky.rs | 66 ++++++++++++++----- crates/astria-sequencer/src/sequencer.rs | 6 +- .../astria-sequencer/src/service/consensus.rs | 2 +- 11 files changed, 96 insertions(+), 92 deletions(-) diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index 93551fcfaf..fa39a70ca0 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -75,7 +75,7 @@ data: OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" ASTRIA_SEQUENCER_ORACLE_ENABLED: "false" ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8080" - ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT: "1000" + ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" {{- if not .Values.global.dev }} {{- else }} {{- end }} diff --git a/crates/astria-sequencer/justfile b/crates/astria-sequencer/justfile index 8391be8864..5251162ed9 100644 --- a/crates/astria-sequencer/justfile +++ b/crates/astria-sequencer/justfile @@ -14,10 +14,10 @@ run: run-cometbft: cometbft init cargo run -p astria-sequencer-utils -- \ - generate-genesis-state -o app-genesis-state.json + generate-genesis-state -o ../../target/app-genesis-state.json cargo run -p astria-sequencer-utils -- \ copy-genesis-state \ - --genesis-app-state-file=app-genesis-state.json \ + --genesis-app-state-file=../../target/app-genesis-state.json \ --destination-genesis-file=$HOME/.cometbft/config/genesis.json \ --chain-id=astria sed -i'.bak' 's/timeout_commit = "1s"/timeout_commit = "2s"/g' ~/.cometbft/config/config.toml diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index b9a7047e9f..b345c58347 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -42,7 +42,7 @@ ASTRIA_SEQUENCER_ORACLE_ENABLED=false ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="http://127.0.0.1:8080" # The timeout for the responses from the oracle sidecar in milliseconds. -ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT=1000 +ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS=1000 # If set to any non-empty value removes ANSI escape characters from the pretty # printed output. Note that this does nothing unless `ASTRIA_SEQUENCER_PRETTY_PRINT` diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index a7cd671430..6614480448 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -356,27 +356,22 @@ impl App { let max_tx_bytes = usize::try_from(prepare_proposal.max_tx_bytes) .context("failed to convert max_tx_bytes to usize")?; - // zero the commit info if it's too large to fit in the block - // for liveness. - if extended_commit_info_bytes.len() > max_tx_bytes { - warn!( - extended_commit_info_bytes_len = extended_commit_info_bytes.len(), - max_tx_bytes, - "extended commit info is too large to fit in block; not including in block" - ); - extended_commit_info_bytes = Vec::new(); - } - // adjust max block size to account for extended commit info - let mut block_size_constraints = BlockSizeConstraints::new( - max_tx_bytes - .checked_sub(extended_commit_info_bytes.len()) - .expect( - "extended_commit_info_bytes is shorter than max_tx_bytes, as it was checked \ - above", - ), - ) - .context("failed to create block size constraints")?; + let adjusted_max_tx_bytes = max_tx_bytes + .checked_sub(extended_commit_info_bytes.len()) + .unwrap_or_else(|| { + // zero the commit info if it's too large to fit in the block + // for liveness. + warn!( + extended_commit_info_bytes_len = extended_commit_info_bytes.len(), + max_tx_bytes, + "extended commit info is too large to fit in block; not including in block" + ); + extended_commit_info_bytes.clear(); + max_tx_bytes + }); + let mut block_size_constraints = BlockSizeConstraints::new(adjusted_max_tx_bytes) + .context("failed to create block size constraints")?; let block_data = BlockData { misbehavior: prepare_proposal.misbehavior, @@ -410,8 +405,9 @@ impl App { let res = generate_rollup_datas_commitment(&signed_txs_included, deposits); // inject the extended commit info into the start of the block's txs - let mut txs = vec![extended_commit_info_bytes.into()]; - txs.extend(res.into_transactions(included_tx_bytes)); + let txs = std::iter::once(extended_commit_info_bytes.into()) + .chain(res.into_transactions(included_tx_bytes)) + .collect(); Ok(abci::response::PrepareProposal { txs, }) diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index ec1b98e4c1..885e540801 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -155,7 +155,7 @@ pub(crate) async fn initialize_app_with_storage( let mut app = App::new( snapshot, mempool, - vote_extension::Handler::new(None, 100), + vote_extension::Handler::new(None), metrics, ) .await diff --git a/crates/astria-sequencer/src/app/tests_app.rs b/crates/astria-sequencer/src/app/tests_app.rs index da90229545..3ae92e9a3a 100644 --- a/crates/astria-sequencer/src/app/tests_app.rs +++ b/crates/astria-sequencer/src/app/tests_app.rs @@ -692,7 +692,7 @@ async fn app_prepare_proposal_sequencer_max_bytes_overflow_ok() { assert_eq!( result.txs.len(), 4, - "total transaction length should be three, including the extended commit info, two \ + "total transaction length should be four, including the extended commit info, two \ commitments and the one tx that fit" ); assert_eq!( diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 1cc456e649..14e10756f3 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -1,14 +1,12 @@ use std::collections::HashMap; use anyhow::{ + bail, ensure, Context as _, }; use astria_core::{ - crypto::{ - Signature, - VerificationKey, - }, + crypto::Signature, generated::slinky::{ abci::v1::OracleVoteExtension as RawOracleVoteExtension, service::v1::{ @@ -55,17 +53,12 @@ const MAXIMUM_PRICE_BYTE_LEN: usize = 33; pub(crate) struct Handler { // gRPC client for the slinky oracle sidecar. oracle_client: Option>, - oracle_client_timeout: tokio::time::Duration, } impl Handler { - pub(crate) fn new( - oracle_client: Option>, - oracle_client_timeout: u64, - ) -> Self { + pub(crate) fn new(oracle_client: Option>) -> Self { Self { oracle_client, - oracle_client_timeout: tokio::time::Duration::from_millis(oracle_client_timeout), } } @@ -74,35 +67,21 @@ impl Handler { state: &S, ) -> anyhow::Result { let Some(oracle_client) = self.oracle_client.as_mut() else { - // we allow validators to *not* use the oracle sidecar currently + // we allow validators to *not* use the oracle sidecar currently, + // so this will get converted to an empty vote extension when bubbled up. + // // however, if >1/3 of validators are not using the oracle, the prices will not update. - return Ok(abci::response::ExtendVote { - vote_extension: vec![].into(), - }); + bail!("oracle client not set") }; // if we fail to get prices within the timeout duration, we will return an empty vote // extension to ensure liveness. - let prices = match tokio::time::timeout( - self.oracle_client_timeout, - oracle_client.prices(QueryPricesRequest {}), - ) - .await - { - Ok(Ok(prices)) => prices.into_inner(), - Ok(Err(e)) => { - tracing::error!( - error = %e, - "failed to get prices from oracle sidecar" - ); - return Ok(abci::response::ExtendVote { - vote_extension: vec![].into(), - }); - } + let prices = match oracle_client.prices(QueryPricesRequest {}).await { + Ok(prices) => prices.into_inner(), Err(e) => { tracing::error!( error = %e, - "failed to get prices from oracle sidecar within timeout duration" + "failed to get prices from oracle sidecar" ); return Ok(abci::response::ExtendVote { vote_extension: vec![].into(), @@ -329,19 +308,10 @@ async fn validate_vote_extensions( submitted_voting_power = submitted_voting_power.saturating_add(vote.validator.power.value()); - let pubkey = validator_set - .get( - &vote - .validator - .address - .to_vec() - .try_into() - .expect("can always convert 20 bytes to account::Id"), - ) + let verification_key = validator_set + .get(vote.validator.address) .context("validator not found")? - .pub_key; - let verification_key = VerificationKey::try_from(pubkey.to_bytes().as_slice()) - .context("failed to create verification key")?; + .verification_key; let vote_extension = CanonicalVoteExtension { extension: vote.vote_extension.to_vec(), @@ -565,7 +535,7 @@ mod test { #[tokio::test] async fn verify_vote_extension_proposal_phase_ok() { - let handler = Handler::new(None, 1000); + let handler = Handler::new(None); let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); @@ -583,7 +553,7 @@ mod test { #[tokio::test] async fn verify_vote_extension_not_proposal_phase_ok() { - let handler = Handler::new(None, 1000); + let handler = Handler::new(None); let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index f17a0fb60f..f6395d3690 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -37,7 +37,7 @@ pub struct Config { /// The gRPC endpoint for the oracle sidecar. pub oracle_grpc_addr: String, /// The timeout for the responses from the oracle sidecar in milliseconds. - pub oracle_client_timeout: u64, + pub oracle_client_timeout_milliseconds: u64, } impl config::Config for Config { diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index c3af89ff4c..635c9ec568 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -67,20 +67,22 @@ impl MarketMapQueryService for SequencerServer { ) -> Result, Status> { let snapshot = self.storage.latest_snapshot(); let market_map = snapshot.get_market_map().await.map_err(|e| { - Status::internal(format!("failed to get block market map from storage: {e}")) + Status::internal(format!( + "failed to get block market map from storage: {e:#}" + )) })?; let last_updated = snapshot .get_market_map_last_updated_height() .await .map_err(|e| { Status::internal(format!( - "failed to get block market map last updated height from storage: {e}" + "failed to get block market map last updated height from storage: {e:#}" )) })?; let chain_id = snapshot .get_chain_id() .await - .map_err(|e| Status::internal(format!("failed to get chain id from storage: {e}")))?; + .map_err(|e| Status::internal(format!("failed to get chain id from storage: {e:#}")))?; Ok(Response::new(MarketMapResponse { market_map: market_map.map(astria_core::slinky::market_map::v1::MarketMap::into_raw), @@ -94,8 +96,7 @@ impl MarketMapQueryService for SequencerServer { self: Arc, _request: Request, ) -> Result, Status> { - // TODO - Ok(Response::new(MarketResponse::default())) + Err(Status::unimplemented("market endpoint is not implemented")) } #[instrument(skip_all)] @@ -109,7 +110,7 @@ impl MarketMapQueryService for SequencerServer { .await .map_err(|e| { Status::internal(format!( - "failed to get block market map last updated height from storage: {e}" + "failed to get block market map last updated height from storage: {e:#}" )) })?; @@ -125,7 +126,7 @@ impl MarketMapQueryService for SequencerServer { ) -> Result, Status> { let snapshot = self.storage.latest_snapshot(); let params = snapshot.get_params().await.map_err(|e| { - Status::internal(format!("failed to get block params from storage: {e}")) + Status::internal(format!("failed to get block params from storage: {e:#}")) })?; Ok(Response::new(ParamsResponse { @@ -144,7 +145,7 @@ impl OracleService for SequencerServer { let snapshot = self.storage.latest_snapshot(); let currency_pairs = snapshot.get_all_currency_pairs().await.map_err(|e| { Status::internal(format!( - "failed to get all currency pairs from storage: {e}" + "failed to get all currency pairs from storage: {e:#}" )) })?; Ok(Response::new(GetAllCurrencyPairsResponse { @@ -169,16 +170,31 @@ impl OracleService for SequencerServer { let Some(state) = snapshot .get_currency_pair_state(¤cy_pair) .await - .map_err(|e| Status::internal(format!("failed to get state from storage: {e}")))? + .map_err(|e| Status::internal(format!("failed to get state from storage: {e:#}")))? else { return Err(Status::not_found("currency pair state not found")); }; + let Some(market_map) = snapshot.get_market_map().await.map_err(|e| { + Status::internal(format!( + "failed to get block market map from storage: {e:#}" + )) + })? + else { + return Err(Status::internal("market map not found")); + }; + + let Some(market) = market_map.markets.get(¤cy_pair.to_string()) else { + return Err(Status::not_found(format!( + "market not found for {currency_pair}" + ))); + }; + Ok(Response::new(GetPriceResponse { price: Some(state.price.into_raw()), nonce: state.nonce, id: state.id, - decimals: 0, // TODO: get this from the marketmap + decimals: market.ticker.decimals, })) } @@ -197,26 +213,46 @@ impl OracleService for SequencerServer { Ok(currency_pairs) => currency_pairs, Err(e) => { return Err(Status::invalid_argument(format!( - "invalid currency pair id: {e}" + "invalid currency pair id: {e:#}" ))); } }; let snapshot = self.storage.latest_snapshot(); + let Some(market_map) = snapshot.get_market_map().await.map_err(|e| { + Status::internal(format!( + "failed to get block market map from storage: {e:#}" + )) + })? + else { + return Err(Status::internal("market map not found")); + }; + let mut prices = Vec::new(); for currency_pair in currency_pairs { let Some(state) = snapshot .get_currency_pair_state(¤cy_pair) .await - .map_err(|e| Status::internal(format!("failed to get state from storage: {e}")))? + .map_err(|e| { + Status::internal(format!("failed to get state from storage: {e:#}")) + })? else { - return Err(Status::not_found("currency pair state not found")); + return Err(Status::not_found(format!( + "currency pair state for {currency_pair} not found" + ))); }; + + let Some(market) = market_map.markets.get(¤cy_pair.to_string()) else { + return Err(Status::not_found(format!( + "market not found for {currency_pair}" + ))); + }; + prices.push(GetPriceResponse { price: Some(state.price.into_raw()), nonce: state.nonce, id: state.id, - decimals: 0, // TODO: get this from the marketmap + decimals: market.ticker.decimals, }); } Ok(Response::new(GetPricesResponse { @@ -232,7 +268,7 @@ impl OracleService for SequencerServer { let snapshot = self.storage.latest_snapshot(); let currency_pair_mapping = snapshot.get_currency_pair_mapping().await.map_err(|e| { Status::internal(format!( - "failed to get currency pair mapping from storage: {e}" + "failed to get currency pair mapping from storage: {e:#}" )) })?; let currency_pair_mapping = currency_pair_mapping diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index 04f38dac5f..7dde070b71 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -124,7 +124,9 @@ impl Sequencer { .oracle_grpc_addr .parse() .context("failed parsing oracle grpc address as Uri")?; - let endpoint = Endpoint::from(uri.clone()); + let endpoint = Endpoint::from(uri.clone()).timeout(std::time::Duration::from_millis( + config.oracle_client_timeout_milliseconds, + )); let mut oracle_client = OracleClient::new(endpoint.connect_lazy()); // ensure the oracle sidecar is reachable @@ -147,7 +149,7 @@ impl Sequencer { let app = App::new( snapshot, mempool.clone(), - crate::app::vote_extension::Handler::new(oracle_client, config.oracle_client_timeout), + crate::app::vote_extension::Handler::new(oracle_client), metrics, ) .await diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index c8ecef673e..210e94e3c2 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -570,7 +570,7 @@ mod test { let mut app = App::new( snapshot, mempool.clone(), - crate::app::vote_extension::Handler::new(None, 100), + crate::app::vote_extension::Handler::new(None), metrics, ) .await From 30929710567217744841f31ba5a8c52f4322c20d Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 19 Aug 2024 14:01:24 -0400 Subject: [PATCH 33/89] bump chart version --- charts/sequencer/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/sequencer/Chart.yaml b/charts/sequencer/Chart.yaml index bb8d4fc527..2eca57f926 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.19.2 # 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. From eafff755f1724a2d475681747aa2a3e62bbfa220 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 21 Aug 2024 15:20:28 -0400 Subject: [PATCH 34/89] address comments --- .../src/sequencerblock/v1alpha1/block.rs | 4 +- crates/astria-core/src/slinky/market_map.rs | 10 +-- crates/astria-core/src/slinky/oracle.rs | 14 +-- crates/astria-core/src/slinky/types.rs | 12 +-- crates/astria-sequencer/src/app/mod.rs | 2 +- .../src/app/vote_extension.rs | 86 +++++++------------ 6 files changed, 47 insertions(+), 81 deletions(-) diff --git a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs index 059f3a644f..9aa6b52d73 100644 --- a/crates/astria-core/src/sequencerblock/v1alpha1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1alpha1/block.rs @@ -811,13 +811,13 @@ impl SequencerBlock { // action tree root is always the second tx in a block let rollup_transactions_proof = tree.construct_proof(1).expect( - "the tree has at least one leaf; if this line is reached and `construct_proof` \ + "the tree has at least two leaves; if this line is reached and `construct_proof` \ returns None it means that the short circuiting checks above it have been removed", ); // rollup id tree root is always the third tx in a block let rollup_ids_proof = tree.construct_proof(2).expect( - "the tree has at least two leaves; if this line is reached and `construct_proof` \ + "the tree has at least three leaves; if this line is reached and `construct_proof` \ returns None it means that the short circuiting checks above it have been removed", ); diff --git a/crates/astria-core/src/slinky/market_map.rs b/crates/astria-core/src/slinky/market_map.rs index d938ff9afe..0bdf5af365 100644 --- a/crates/astria-core/src/slinky/market_map.rs +++ b/crates/astria-core/src/slinky/market_map.rs @@ -95,11 +95,11 @@ pub mod v1 { enum GenesisStateErrorKind { #[error("missing market map")] MissingMarketMap, - #[error(transparent)] + #[error("failed to parse market map")] MarketMapParseError(#[from] MarketMapError), #[error("missing params")] MissingParams, - #[error(transparent)] + #[error("failed to parse params")] ParamsParseError(#[from] ParamsError), } @@ -243,9 +243,9 @@ pub mod v1 { enum MarketErrorKind { #[error("missing ticker")] MissingTicker, - #[error(transparent)] + #[error("failed to parse ticker")] TickerParseError(#[from] TickerError), - #[error(transparent)] + #[error("failed to parse provider config")] ProviderConfigParseError(#[from] ProviderConfigError), } @@ -418,6 +418,6 @@ pub mod v1 { #[derive(Debug, thiserror::Error)] enum MarketMapErrorKind { #[error("invalid market {0}")] - InvalidMarket(String, MarketError), + InvalidMarket(String, #[source] MarketError), } } diff --git a/crates/astria-core/src/slinky/oracle.rs b/crates/astria-core/src/slinky/oracle.rs index cc0a999203..1b6a8fed6f 100644 --- a/crates/astria-core/src/slinky/oracle.rs +++ b/crates/astria-core/src/slinky/oracle.rs @@ -65,7 +65,7 @@ pub mod v1 { #[derive(Debug, thiserror::Error)] enum QuotePriceErrorKind { - #[error(transparent)] + #[error("failed to parse price")] PriceParseError(#[from] std::num::ParseIntError), #[error("missing block timestamp")] MissingBlockTimestamp, @@ -134,8 +134,8 @@ pub mod v1 { enum CurrencyPairStateErrorKind { #[error("missing price")] MissingPrice, - #[error(transparent)] - QuotePriceParseError(QuotePriceError), + #[error("failed to parse quote price")] + QuotePriceParseError(#[source] QuotePriceError), } #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -239,8 +239,8 @@ pub mod v1 { MissingCurrencyPair, #[error("missing currency pair price")] MissingCurrencyPairPrice, - #[error(transparent)] - QuotePriceParseError(QuotePriceError), + #[error("failed to parse quote price")] + QuotePriceParseError(#[source] QuotePriceError), } #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -296,7 +296,7 @@ pub mod v1 { #[derive(Debug, thiserror::Error)] enum GenesisStateErrorKind { - #[error(transparent)] - CurrencyPairGenesisParseError(CurrencyPairGenesisError), + #[error("failed to parse genesis currency pair")] + CurrencyPairGenesisParseError(#[source] CurrencyPairGenesisError), } } diff --git a/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/slinky/types.rs index a3f04f71e0..6bf58bf4da 100644 --- a/crates/astria-core/src/slinky/types.rs +++ b/crates/astria-core/src/slinky/types.rs @@ -54,15 +54,9 @@ pub mod v1 { type Err = CurrencyPairParseError; fn from_str(s: &str) -> Result { - let parts: Vec<&str> = s.split('/').collect(); - if parts.len() != 2 { - return Err(CurrencyPairParseError::invalid_currency_pair_string(s)); - } - - Ok(Self { - base: parts[0].to_string(), - quote: parts[1].to_string(), - }) + s.split_once('/') + .map(|(base, quote)| Self::new(base.to_string(), quote.to_string())) + .ok_or_else(|| CurrencyPairParseError::invalid_currency_pair_string(s)) } } diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index d732b946cc..5c93bcc161 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -855,7 +855,7 @@ impl App { vote_extension: abci::request::VerifyVoteExtension, ) -> abci::response::VerifyVoteExtension { self.vote_extension_handler - .verify_vote_extension(&self.state, vote_extension, false) + .verify_vote_extension(&self.state, vote_extension) .await } diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 8af8e3ad3b..62dc96fdfb 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -80,13 +80,7 @@ impl Handler { let prices = match oracle_client.prices(QueryPricesRequest {}).await { Ok(prices) => prices.into_inner(), Err(e) => { - tracing::error!( - error = %e, - "failed to get prices from oracle sidecar" - ); - return Ok(abci::response::ExtendVote { - vote_extension: vec![].into(), - }); + bail!("failed to get prices from oracle sidecar: {e}",); } }; @@ -108,16 +102,12 @@ impl Handler { &self, state: &S, vote: abci::request::VerifyVoteExtension, - is_proposal_phase: bool, ) -> abci::response::VerifyVoteExtension { - let oracle_vote_extension = match RawOracleVoteExtension::decode(vote.vote_extension) { - Ok(oracle_vote_extension) => oracle_vote_extension.into(), - Err(e) => { - tracing::error!(error = %e, "failed to decode oracle vote extension"); - return abci::response::VerifyVoteExtension::Reject; - } - }; - match verify_vote_extension(state, oracle_vote_extension, is_proposal_phase).await { + if vote.vote_extension.is_empty() { + return abci::response::VerifyVoteExtension::Accept; + } + + match verify_vote_extension(state, vote.vote_extension, false).await { Ok(()) => abci::response::VerifyVoteExtension::Accept, Err(e) => { tracing::error!(error = %e, "failed to verify vote extension"); @@ -128,11 +118,13 @@ impl Handler { } // see https://github.com/skip-mev/slinky/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L24 -pub(crate) async fn verify_vote_extension( +async fn verify_vote_extension( state: &S, - oracle_vote_extension: OracleVoteExtension, + oracle_vote_extension_bytes: bytes::Bytes, is_proposal_phase: bool, ) -> anyhow::Result<()> { + let oracle_vote_extension = RawOracleVoteExtension::decode(oracle_vote_extension_bytes) + .context("failed to decode oracle vote extension")?; let max_num_currency_pairs = DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, is_proposal_phase) .await @@ -206,9 +198,7 @@ impl ProposalHandler { } for vote in &mut extended_commit_info.votes { - let oracle_vote_extension = - RawOracleVoteExtension::decode(vote.vote_extension.clone())?.into(); - if let Err(e) = verify_vote_extension(state, oracle_vote_extension, true).await { + if let Err(e) = verify_vote_extension(state, vote.vote_extension.clone(), true).await { let address = state .try_base_prefixed(vote.validator.address.as_slice()) .await @@ -220,7 +210,7 @@ impl ProposalHandler { ); vote.sig_info = Flag(tendermint::block::BlockIdFlag::Absent); vote.extension_signature = None; - vote.vote_extension = vec![].into(); + vote.vote_extension.clear(); } } validate_vote_extensions(state, height, &extended_commit_info) @@ -324,7 +314,6 @@ async fn validate_vote_extensions( chain_id: chain_id.to_string(), }; - // TODO: double check that it's length-delimited let message = vote_extension.encode_length_delimited_to_vec(); let signature = Signature::try_from( vote.extension_signature @@ -392,27 +381,30 @@ fn validate_extended_commit_against_last_commit( "extended commit votes are not sorted by voting power", ); - for (i, vote) in extended_commit_info.votes.iter().enumerate() { - let last_commit_vote = &last_commit.votes[i]; + for (last_commit_vote, extended_commit_info_vote) in last_commit + .votes + .iter() + .zip(extended_commit_info.votes.iter()) + { ensure!( - last_commit_vote.validator.address == vote.validator.address, + last_commit_vote.validator.address == extended_commit_info_vote.validator.address, "last commit vote address does not match extended commit vote address" ); ensure!( - last_commit_vote.validator.power == vote.validator.power, + last_commit_vote.validator.power == extended_commit_info_vote.validator.power, "last commit vote power does not match extended commit vote power" ); // vote is absent; no need to check for the block id flag matching the last commit - if vote.sig_info == Flag(tendermint::block::BlockIdFlag::Absent) - && vote.vote_extension.is_empty() - && vote.extension_signature.is_none() + if extended_commit_info_vote.sig_info == Flag(tendermint::block::BlockIdFlag::Absent) + && extended_commit_info_vote.vote_extension.is_empty() + && extended_commit_info_vote.extension_signature.is_none() { continue; } ensure!( - vote.sig_info == last_commit_vote.sig_info, + extended_commit_info_vote.sig_info == last_commit_vote.sig_info, "last commit vote sig info does not match extended commit vote sig info" ); } @@ -530,43 +522,23 @@ async fn aggregate_oracle_votes( #[cfg(test)] mod test { - use tendermint::abci; - use super::*; #[tokio::test] async fn verify_vote_extension_proposal_phase_ok() { - let handler = Handler::new(None); let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); - - let vote = abci::request::VerifyVoteExtension { - hash: [0u8; 32].to_vec().try_into().unwrap(), - vote_extension: vec![].into(), - validator_address: [1u8; 20].to_vec().try_into().unwrap(), - height: 1u32.into(), - }; - assert_eq!( - handler.verify_vote_extension(&snapshot, vote, true).await, - abci::response::VerifyVoteExtension::Accept - ); + verify_vote_extension(&snapshot, vec![].as_slice(), true) + .await + .unwrap(); } #[tokio::test] async fn verify_vote_extension_not_proposal_phase_ok() { - let handler = Handler::new(None); let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); - - let vote = abci::request::VerifyVoteExtension { - hash: [0u8; 32].to_vec().try_into().unwrap(), - vote_extension: vec![].into(), - validator_address: [1u8; 20].to_vec().try_into().unwrap(), - height: 1u32.into(), - }; - assert_eq!( - handler.verify_vote_extension(&snapshot, vote, false).await, - abci::response::VerifyVoteExtension::Accept - ); + verify_vote_extension(&snapshot, vec![].as_slice(), true) + .await + .unwrap(); } } From 18af189d8e14ffdd38c9cab5624a20ee7f31ec1d Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 21 Aug 2024 20:23:45 -0400 Subject: [PATCH 35/89] address comments --- .../src/app/vote_extension.rs | 36 ++++---- .../src/slinky/marketmap/state_ext.rs | 21 +++-- .../src/slinky/oracle/component.rs | 5 +- .../slinky/oracle/currency_pair_strategy.rs | 7 +- .../src/slinky/oracle/state_ext.rs | 85 ++++++++----------- 5 files changed, 64 insertions(+), 90 deletions(-) diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 62dc96fdfb..0038cb2299 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -35,7 +35,6 @@ use tonic::transport::Channel; use tracing::{ debug, trace, - warn, }; use crate::{ @@ -168,8 +167,7 @@ async fn transform_oracle_service_prices( ); continue; }; - let encoded_price = - DefaultCurrencyPairStrategy::get_encoded_price(state, ¤cy_pair, price); + let encoded_price = DefaultCurrencyPairStrategy::get_encoded_price(state, price); debug!( currency_pair = currency_pair.to_string(), @@ -337,8 +335,8 @@ async fn validate_vote_extensions( .context("failed to multiply total voting power by 2")? .checked_div(3) .context("failed to divide total voting power by 3")? - .checked_sub(1) - .context("failed to subtract 1 from total voting power")?; + .checked_add(1) + .context("failed to add 1 from total voting power")?; ensure!( submitted_voting_power >= required_voting_power, "submitted voting power is less than required voting power", @@ -450,6 +448,7 @@ pub(crate) async fn apply_prices_from_vote_extensions( state .put_price_for_currency_pair(¤cy_pair, price) + .await .context("failed to put price")?; } @@ -479,9 +478,8 @@ async fn aggregate_oracle_votes( continue; }; - let price = - DefaultCurrencyPairStrategy::get_decoded_price(state, ¤cy_pair, &price_bytes) - .context("failed to get decoded price")?; + let price = DefaultCurrencyPairStrategy::get_decoded_price(state, &price_bytes) + .context("failed to get decoded price")?; currency_pair_to_price_list .entry(currency_pair) .and_modify(|prices: &mut Vec| prices.push(price)) @@ -499,19 +497,15 @@ async fn aggregate_oracle_votes( price_list.sort_unstable(); let mid = price_list.len() / 2; if price_list.len() % 2 == 0 { - let Some(num) = price_list[mid + let num_to_skip = mid .checked_sub(1) - .expect("must subtract as the length of the price list is >0")] - .checked_add(price_list[mid]) else { - warn!( - "failed to add two middle prices together; skipping currency pair: {}", - currency_pair, - ); - continue; - }; - num / 2 + .expect("must subtract as the length of the price list is >0"); + price_list.iter().skip(num_to_skip).take(2).sum::() / 2 } else { - price_list[mid] + price_list + .get(mid) + .copied() + .expect("must have element as mid < len") } }; prices.insert(currency_pair, median_price); @@ -528,7 +522,7 @@ mod test { async fn verify_vote_extension_proposal_phase_ok() { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); - verify_vote_extension(&snapshot, vec![].as_slice(), true) + verify_vote_extension(&snapshot, vec![].into(), true) .await .unwrap(); } @@ -537,7 +531,7 @@ mod test { async fn verify_vote_extension_not_proposal_phase_ok() { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); - verify_vote_extension(&snapshot, vec![].as_slice(), true) + verify_vote_extension(&snapshot, vec![].into(), true) .await .unwrap(); } diff --git a/crates/astria-sequencer/src/slinky/marketmap/state_ext.rs b/crates/astria-sequencer/src/slinky/marketmap/state_ext.rs index ef368cd081..1c49546580 100644 --- a/crates/astria-sequencer/src/slinky/marketmap/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/marketmap/state_ext.rs @@ -19,7 +19,7 @@ use tracing::instrument; const MARKET_MAP_KEY: &str = "slinkymarketmap"; const PARAMS_KEY: &str = "slinkyparams"; -const MARKET_MAP_LAST_UPDATED_KEY: &[u8] = b"slinkymarketmaplastupdated"; +const MARKET_MAP_LAST_UPDATED_KEY: &str = "slinkymarketmaplastupdated"; /// Newtype wrapper to read and write a u64 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] @@ -27,7 +27,7 @@ struct Height(u64); #[async_trait] pub(crate) trait StateReadExt: StateRead { - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_market_map(&self) -> Result> { let bytes = self .get_raw(MARKET_MAP_KEY) @@ -43,10 +43,10 @@ pub(crate) trait StateReadExt: StateRead { } } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_market_map_last_updated_height(&self) -> Result { let Some(bytes) = self - .nonverifiable_get_raw(MARKET_MAP_LAST_UPDATED_KEY) + .get_raw(MARKET_MAP_LAST_UPDATED_KEY) .await .context("failed reading market map last updated height from state")? else { @@ -56,7 +56,7 @@ pub(crate) trait StateReadExt: StateRead { Ok(height) } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_params(&self) -> Result> { let bytes = self .get_raw(PARAMS_KEY) @@ -77,24 +77,23 @@ impl StateReadExt for T {} #[async_trait] pub(crate) trait StateWriteExt: StateWrite { - #[instrument(skip(self))] + #[instrument(skip_all)] fn put_market_map(&mut self, market_map: MarketMap) -> Result<()> { let bytes = serde_json::to_vec(&market_map).context("failed to serialize market map")?; self.put_raw(MARKET_MAP_KEY.to_string(), bytes); Ok(()) } - #[instrument(skip(self))] + #[instrument(skip_all)] fn put_market_map_last_updated_height(&mut self, height: u64) -> Result<()> { - // TODO: since this is an optimization, should it be in the nonconsensus store? - self.nonverifiable_put_raw( - MARKET_MAP_LAST_UPDATED_KEY.to_vec(), + self.put_raw( + MARKET_MAP_LAST_UPDATED_KEY.to_string(), borsh::to_vec(&Height(height)).context("failed to serialize height")?, ); Ok(()) } - #[instrument(skip(self))] + #[instrument(skip_all)] fn put_params(&mut self, params: Params) -> Result<()> { let bytes = serde_json::to_vec(¶ms).context("failed to serialize params")?; self.put_raw(PARAMS_KEY.to_string(), bytes); diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index 4f01cb8ad2..757692be81 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -30,10 +30,7 @@ impl Component for OracleComponent { price: currency_pair.currency_pair_price().clone(), }; state - .put_currency_pair_state_and_price( - currency_pair.currency_pair(), - currency_pair_state, - ) + .put_currency_pair_state(currency_pair.currency_pair(), currency_pair_state) .context("failed to put currency pair")?; tracing::info!( "put currency pair: {}", diff --git a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs index 19fb5ce627..6d0458d4e8 100644 --- a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs +++ b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs @@ -24,17 +24,12 @@ impl DefaultCurrencyPairStrategy { state.get_currency_pair(id).await } - pub(crate) fn get_encoded_price( - _state: &S, - _: &CurrencyPair, - price: u128, - ) -> Vec { + pub(crate) fn get_encoded_price(_state: &S, price: u128) -> Vec { price.to_be_bytes().to_vec() } pub(crate) fn get_decoded_price( _state: &S, - _: &CurrencyPair, encoded_price: &[u8], ) -> anyhow::Result { ensure!(encoded_price.len() == 16, "invalid encoded price length"); diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs index 314ad9f66d..ac5aebf45c 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs @@ -27,9 +27,7 @@ use tracing::instrument; const CURRENCY_PAIR_TO_ID_PREFIX: &str = "oraclecpid"; const ID_TO_CURRENCY_PAIR_PREFIX: &str = "oracleidcp"; const CURRENCY_PAIR_STATE_PREFIX: &str = "oraclecpstate"; -const CURRENCY_PAIR_PRICE_PREFIX: &str = "oraclecpprice"; -// TODO: should these values be in nonverifiable storage? const NUM_CURRENCY_PAIRS_KEY: &str = "oraclenumcps"; const NUM_REMOVED_CURRENCY_PAIRS_KEY: &str = "oraclenumremovedcps"; const NEXT_CURRENCY_PAIR_ID_KEY: &str = "oraclenextcpid"; @@ -46,10 +44,6 @@ fn currency_pair_state_storage_key(currency_pair: &CurrencyPair) -> String { format!("{CURRENCY_PAIR_STATE_PREFIX}/{currency_pair}",) } -fn currency_pair_price_storage_key(currency_pair: &CurrencyPair) -> String { - format!("{CURRENCY_PAIR_PRICE_PREFIX}/{currency_pair}",) -} - /// Newtype wrapper to read and write a u64 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Id(u64); @@ -60,7 +54,7 @@ struct Count(u64); #[async_trait] pub(crate) trait StateReadExt: StateRead { - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_currency_pair_id(&self, currency_pair: &CurrencyPair) -> Result { let Some(bytes) = self .get_raw(¤cy_pair_to_id_storage_key(currency_pair)) @@ -73,7 +67,7 @@ pub(crate) trait StateReadExt: StateRead { Ok(id) } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_currency_pair(&self, id: u64) -> Result> { let bytes = self .get_raw(&id_to_currency_pair_storage_key(id)) @@ -89,7 +83,7 @@ pub(crate) trait StateReadExt: StateRead { } } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_currency_pair_mapping(&self) -> Result> { let prefix = format!("{CURRENCY_PAIR_TO_ID_PREFIX}/"); let mut currency_pairs = HashMap::new(); @@ -115,7 +109,7 @@ pub(crate) trait StateReadExt: StateRead { Ok(currency_pairs) } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_num_currency_pairs(&self) -> Result { let Some(bytes) = self .get_raw(NUM_CURRENCY_PAIRS_KEY) @@ -129,7 +123,7 @@ pub(crate) trait StateReadExt: StateRead { Ok(num_currency_pairs) } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_num_removed_currency_pairs(&self) -> Result { let Some(bytes) = self .get_raw(NUM_REMOVED_CURRENCY_PAIRS_KEY) @@ -143,7 +137,7 @@ pub(crate) trait StateReadExt: StateRead { Ok(num_removed_currency_pairs) } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_currency_pair_state( &self, currency_pair: &CurrencyPair, @@ -162,7 +156,7 @@ pub(crate) trait StateReadExt: StateRead { } } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_all_currency_pairs(&self) -> Result> { let prefix = format!("{CURRENCY_PAIR_STATE_PREFIX}/"); let mut currency_pairs: Vec = Vec::new(); @@ -179,7 +173,7 @@ pub(crate) trait StateReadExt: StateRead { Ok(currency_pairs) } - #[instrument(skip(self))] + #[instrument(skip_all)] async fn get_next_currency_pair_id(&self) -> Result { let Some(bytes) = self .get_raw(NEXT_CURRENCY_PAIR_ID_KEY) @@ -192,47 +186,28 @@ pub(crate) trait StateReadExt: StateRead { Id::try_from_slice(&bytes).context("invalid next currency pair id bytes")?; Ok(next_currency_pair_id) } - - #[instrument(skip(self))] - async fn get_price_for_currency_pair( - &self, - currency_pair: &CurrencyPair, - ) -> Result> { - let bytes = self - .get_raw(¤cy_pair_price_storage_key(currency_pair)) - .await - .context("failed to get price for currency pair from state")?; - match bytes { - Some(bytes) => { - let price = - serde_json::from_slice(&bytes).context("failed to deserialize price")?; - Ok(Some(price)) - } - None => Ok(None), - } - } } impl StateReadExt for T {} #[async_trait] pub(crate) trait StateWriteExt: StateWrite { - #[instrument(skip(self))] + #[instrument(skip_all)] fn put_currency_pair_id(&mut self, currency_pair: &CurrencyPair, id: u64) -> Result<()> { let bytes = borsh::to_vec(&Id(id)).context("failed to serialize currency pair id")?; self.put_raw(currency_pair_to_id_storage_key(currency_pair), bytes); Ok(()) } - #[instrument(skip(self))] - fn put_currency_pair(&mut self, id: u64, currency_pair: CurrencyPair) -> Result<()> { + #[instrument(skip_all)] + fn put_currency_pair(&mut self, id: u64, currency_pair: &CurrencyPair) -> Result<()> { let bytes = serde_json::to_vec(¤cy_pair).context("failed to serialize currency pair")?; self.put_raw(id_to_currency_pair_storage_key(id), bytes); Ok(()) } - #[instrument(skip(self))] + #[instrument(skip_all)] fn put_num_currency_pairs(&mut self, num_currency_pairs: u64) -> Result<()> { let bytes = borsh::to_vec(&Count(num_currency_pairs)) .context("failed to serialize number of currency pairs")?; @@ -240,7 +215,7 @@ pub(crate) trait StateWriteExt: StateWrite { Ok(()) } - #[instrument(skip(self))] + #[instrument(skip_all)] fn put_num_removed_currency_pairs(&mut self, num_removed_currency_pairs: u64) -> Result<()> { let bytes = borsh::to_vec(&Count(num_removed_currency_pairs)) .context("failed to serialize number of removed currency pairs")?; @@ -248,8 +223,8 @@ pub(crate) trait StateWriteExt: StateWrite { Ok(()) } - #[instrument(skip(self))] - fn put_currency_pair_state_and_price( + #[instrument(skip_all)] + fn put_currency_pair_state( &mut self, currency_pair: &CurrencyPair, currency_pair_state: CurrencyPairState, @@ -257,15 +232,14 @@ pub(crate) trait StateWriteExt: StateWrite { let bytes = serde_json::to_vec(¤cy_pair_state) .context("failed to serialize currency pair state")?; self.put_raw(currency_pair_state_storage_key(currency_pair), bytes); - self.put_price_for_currency_pair(currency_pair, currency_pair_state.price)?; self.put_currency_pair_id(currency_pair, currency_pair_state.id) .context("failed to put currency pair id")?; - self.put_currency_pair(currency_pair_state.id, currency_pair.clone()) + self.put_currency_pair(currency_pair_state.id, currency_pair) .context("failed to put currency pair")?; Ok(()) } - #[instrument(skip(self))] + #[instrument(skip_all)] fn put_next_currency_pair_id(&mut self, next_currency_pair_id: u64) -> Result<()> { let bytes = borsh::to_vec(&Id(next_currency_pair_id)) .context("failed to serialize next currency pair id")?; @@ -273,15 +247,30 @@ pub(crate) trait StateWriteExt: StateWrite { Ok(()) } - #[instrument(skip(self))] - fn put_price_for_currency_pair( + #[instrument(skip_all)] + async fn put_price_for_currency_pair( &mut self, currency_pair: &CurrencyPair, price: QuotePrice, ) -> Result<()> { - // TODO: also `put_currency_pair` if it didn't exist? - let bytes = serde_json::to_vec(&price).context("failed to serialize price")?; - self.put_raw(currency_pair_price_storage_key(currency_pair), bytes); + let state = if let Some(mut state) = self + .get_currency_pair_state(currency_pair) + .await + .context("failed to get currency pair state")? + { + state.price = price; + state.nonce.checked_add(1).context("nonce overflow")?; + state + } else { + let id = self.get_next_currency_pair_id().await?; + CurrencyPairState { + price, + nonce: 0, + id, + } + }; + self.put_currency_pair_state(currency_pair, state) + .context("failed to put currency pair state")?; Ok(()) } } From c87d28d97a345c28c436ee36c633e8d4880e9620 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 22 Aug 2024 10:59:32 -0400 Subject: [PATCH 36/89] address chart comments --- charts/sequencer/files/cometbft/config/genesis.json | 2 +- charts/sequencer/templates/configmaps.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 32e2d57aa2..d411b7bb33 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -50,7 +50,7 @@ "params": { "market_authorities": [], "admin": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + "bech32m": {{ include "sequencer.address" .Values.genesis.authoritySudoAddress }} } } }, diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index fa39a70ca0..d0db3b365c 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,10 +73,10 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" - ASTRIA_SEQUENCER_ORACLE_ENABLED: "false" ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8080" ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" {{- if not .Values.global.dev }} {{- else }} + ASTRIA_SEQUENCER_ORACLE_ENABLED: "false" {{- end }} --- From cd87fd4590c6e882fa1226e8d3f698c2a6114daf Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 22 Aug 2024 11:00:02 -0400 Subject: [PATCH 37/89] remove clippy too many lines --- .../src/genesis_example.rs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 464f239a57..8a7bd86a31 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, fs::File, io::Write, path::PathBuf, @@ -17,11 +18,14 @@ use astria_core::{ slinky::{ market_map::v1::{ GenesisState as MarketMapGenesisState, + Market, MarketMap, Params, ProviderConfig, + Ticker, }, oracle::v1::{ + CurrencyPairGenesis, GenesisState as OracleGenesisState, QuotePrice, }, @@ -59,18 +63,8 @@ fn charlie() -> Address { .unwrap() } -// allow clippy here because this is an example genesis state, which is long. -#[allow(clippy::too_many_lines)] -fn genesis_state() -> GenesisState { - use astria_core::slinky::{ - market_map::v1::{ - Market, - Ticker, - }, - oracle::v1::CurrencyPairGenesis, - }; - - let mut markets = std::collections::HashMap::new(); +fn genesis_state_markets() -> HashMap { + let mut markets = HashMap::new(); markets.insert( "BTC/USD".to_string(), Market { @@ -109,6 +103,10 @@ fn genesis_state() -> GenesisState { }], }, ); + markets +} + +fn genesis_state() -> GenesisState { UncheckedGenesisState { accounts: vec![ Account { @@ -148,7 +146,7 @@ fn genesis_state() -> GenesisState { }, market_map: MarketMapGenesisState { market_map: MarketMap { - markets, + markets: genesis_state_markets(), }, last_updated: 0, params: Params { From b64fc4101d51a0d100d4de9c44dfaedc89b260a3 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 23 Aug 2024 14:13:45 -0400 Subject: [PATCH 38/89] update genesis to have SlinkyGenesis field --- .../astria.protocol.genesis.v1alpha1.rs | 27 ++- .../astria.protocol.genesis.v1alpha1.serde.rs | 154 +++++++++++--- ...a1__tests__genesis_state_is_unchanged.snap | 16 +- .../src/protocol/genesis/v1alpha1.rs | 188 ++++++++++++++---- crates/astria-core/src/slinky/market_map.rs | 18 +- crates/astria-core/src/slinky/oracle.rs | 37 +++- .../src/genesis_example.rs | 98 +++++---- crates/astria-sequencer/src/app/test_utils.rs | 43 ++-- .../src/slinky/marketmap/component.rs | 4 +- .../src/slinky/oracle/component.rs | 12 +- .../protocol/genesis/v1alpha1/types.proto | 8 +- .../slinky/oracle/v1/genesis.proto | 3 +- .../slinky/service/v1/oracle.proto | 1 - .../cosmos_sdk/cosmos_proto/cosmos.proto | 106 ---------- tools/protobuf-compiler/src/main.rs | 1 - 15 files changed, 448 insertions(+), 268 deletions(-) delete mode 100644 proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs index 1a220e895f..c5ce8cf844 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs @@ -28,13 +28,7 @@ pub struct GenesisAppState { #[prost(message, optional, tag = "10")] pub fees: ::core::option::Option, #[prost(message, optional, tag = "11")] - pub market_map_genesis: ::core::option::Option< - super::super::super::astria_vendored::slinky::marketmap::v1::GenesisState, - >, - #[prost(message, optional, tag = "12")] - pub oracle_genesis: ::core::option::Option< - super::super::super::astria_vendored::slinky::oracle::v1::GenesisState, - >, + pub slinky_genesis: ::core::option::Option, } impl ::prost::Name for GenesisAppState { const NAME: &'static str = "GenesisAppState"; @@ -132,3 +126,22 @@ impl ::prost::Name for Fees { ::prost::alloc::format!("astria.protocol.genesis.v1alpha1.{}", Self::NAME) } } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SlinkyGenesis { + #[prost(message, optional, tag = "1")] + pub market_map_genesis: ::core::option::Option< + super::super::super::astria_vendored::slinky::marketmap::v1::GenesisState, + >, + #[prost(message, optional, tag = "2")] + pub oracle_genesis: ::core::option::Option< + super::super::super::astria_vendored::slinky::oracle::v1::GenesisState, + >, +} +impl ::prost::Name for SlinkyGenesis { + const NAME: &'static str = "SlinkyGenesis"; + 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 index d8b6e14ced..087b7138d9 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs @@ -435,10 +435,7 @@ impl serde::Serialize for GenesisAppState { if self.fees.is_some() { len += 1; } - if self.market_map_genesis.is_some() { - len += 1; - } - if self.oracle_genesis.is_some() { + if self.slinky_genesis.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.GenesisAppState", len)?; @@ -472,11 +469,8 @@ impl serde::Serialize for GenesisAppState { if let Some(v) = self.fees.as_ref() { struct_ser.serialize_field("fees", v)?; } - if let Some(v) = self.market_map_genesis.as_ref() { - struct_ser.serialize_field("marketMapGenesis", v)?; - } - if let Some(v) = self.oracle_genesis.as_ref() { - struct_ser.serialize_field("oracleGenesis", v)?; + if let Some(v) = self.slinky_genesis.as_ref() { + struct_ser.serialize_field("slinkyGenesis", v)?; } struct_ser.end() } @@ -506,10 +500,8 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "allowed_fee_assets", "allowedFeeAssets", "fees", - "market_map_genesis", - "marketMapGenesis", - "oracle_genesis", - "oracleGenesis", + "slinky_genesis", + "slinkyGenesis", ]; #[allow(clippy::enum_variant_names)] @@ -524,8 +516,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { IbcParameters, AllowedFeeAssets, Fees, - MarketMapGenesis, - OracleGenesis, + SlinkyGenesis, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -557,8 +548,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "ibcParameters" | "ibc_parameters" => Ok(GeneratedField::IbcParameters), "allowedFeeAssets" | "allowed_fee_assets" => Ok(GeneratedField::AllowedFeeAssets), "fees" => Ok(GeneratedField::Fees), - "marketMapGenesis" | "market_map_genesis" => Ok(GeneratedField::MarketMapGenesis), - "oracleGenesis" | "oracle_genesis" => Ok(GeneratedField::OracleGenesis), + "slinkyGenesis" | "slinky_genesis" => Ok(GeneratedField::SlinkyGenesis), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -588,8 +578,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { let mut ibc_parameters__ = None; let mut allowed_fee_assets__ = None; let mut fees__ = None; - let mut market_map_genesis__ = None; - let mut oracle_genesis__ = None; + let mut slinky_genesis__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::ChainId => { @@ -652,17 +641,11 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { } fees__ = map_.next_value()?; } - GeneratedField::MarketMapGenesis => { - if market_map_genesis__.is_some() { - return Err(serde::de::Error::duplicate_field("marketMapGenesis")); + GeneratedField::SlinkyGenesis => { + if slinky_genesis__.is_some() { + return Err(serde::de::Error::duplicate_field("slinkyGenesis")); } - market_map_genesis__ = map_.next_value()?; - } - GeneratedField::OracleGenesis => { - if oracle_genesis__.is_some() { - return Err(serde::de::Error::duplicate_field("oracleGenesis")); - } - oracle_genesis__ = map_.next_value()?; + slinky_genesis__ = map_.next_value()?; } } } @@ -677,8 +660,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { ibc_parameters: ibc_parameters__, allowed_fee_assets: allowed_fee_assets__.unwrap_or_default(), fees: fees__, - market_map_genesis: market_map_genesis__, - oracle_genesis: oracle_genesis__, + slinky_genesis: slinky_genesis__, }) } } @@ -813,3 +795,113 @@ impl<'de> serde::Deserialize<'de> for IbcParameters { deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.IbcParameters", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for SlinkyGenesis { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.market_map_genesis.is_some() { + len += 1; + } + if self.oracle_genesis.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.SlinkyGenesis", len)?; + if let Some(v) = self.market_map_genesis.as_ref() { + struct_ser.serialize_field("marketMapGenesis", v)?; + } + if let Some(v) = self.oracle_genesis.as_ref() { + struct_ser.serialize_field("oracleGenesis", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for SlinkyGenesis { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market_map_genesis", + "marketMapGenesis", + "oracle_genesis", + "oracleGenesis", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MarketMapGenesis, + OracleGenesis, + } + 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 { + "marketMapGenesis" | "market_map_genesis" => Ok(GeneratedField::MarketMapGenesis), + "oracleGenesis" | "oracle_genesis" => Ok(GeneratedField::OracleGenesis), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = SlinkyGenesis; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.protocol.genesis.v1alpha1.SlinkyGenesis") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market_map_genesis__ = None; + let mut oracle_genesis__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MarketMapGenesis => { + if market_map_genesis__.is_some() { + return Err(serde::de::Error::duplicate_field("marketMapGenesis")); + } + market_map_genesis__ = map_.next_value()?; + } + GeneratedField::OracleGenesis => { + if oracle_genesis__.is_some() { + return Err(serde::de::Error::duplicate_field("oracleGenesis")); + } + oracle_genesis__ = map_.next_value()?; + } + } + } + Ok(SlinkyGenesis { + market_map_genesis: market_map_genesis__, + oracle_genesis: oracle_genesis__, + }) + } + } + deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.SlinkyGenesis", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 index 19dc73dc48..d87c3b0a7e 100644 --- a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 @@ -79,11 +79,13 @@ expression: genesis_state() "lo": "24" } }, - "marketMapGenesis": { - "marketMap": {}, - "params": { - "admin": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - } - }, - "oracleGenesis": {} + "slinkyGenesis": { + "marketMapGenesis": { + "marketMap": {}, + "params": { + "admin": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + } + }, + "oracleGenesis": {} + } } diff --git a/crates/astria-core/src/protocol/genesis/v1alpha1.rs b/crates/astria-core/src/protocol/genesis/v1alpha1.rs index bea158207b..26957227cc 100644 --- a/crates/astria-core/src/protocol/genesis/v1alpha1.rs +++ b/crates/astria-core/src/protocol/genesis/v1alpha1.rs @@ -27,6 +27,117 @@ use crate::{ Protobuf, }; +#[derive(Clone, Debug)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::SlinkyGenesis", into = "raw::SlinkyGenesis") +)] +pub struct SlinkyGenesis { + market_map: MarketMapGenesisState, + oracle: OracleGenesisState, +} + +impl SlinkyGenesis { + #[must_use] + pub fn market_map(&self) -> &MarketMapGenesisState { + &self.market_map + } + + #[must_use] + pub fn oracle(&self) -> &OracleGenesisState { + &self.oracle + } +} + +impl Protobuf for SlinkyGenesis { + type Error = SlinkyGenesisError; + type Raw = raw::SlinkyGenesis; + + fn try_from_raw_ref(raw: &Self::Raw) -> Result { + let Self::Raw { + market_map_genesis, + oracle_genesis, + } = raw; + let market_map = market_map_genesis + .as_ref() + .ok_or_else(|| Self::Error::field_not_set("market_map_genesis")) + .and_then(|market_map| { + MarketMapGenesisState::try_from_raw_ref(market_map).map_err(Self::Error::market_map) + })?; + let oracle = oracle_genesis + .as_ref() + .ok_or_else(|| Self::Error::field_not_set("oracle_genesis")) + .and_then(|oracle| { + OracleGenesisState::try_from_raw_ref(oracle).map_err(Self::Error::oracle) + })?; + Ok(Self { + market_map, + oracle, + }) + } + + fn to_raw(&self) -> Self::Raw { + let Self { + market_map, + oracle, + } = self; + Self::Raw { + market_map_genesis: Some(market_map.to_raw()), + oracle_genesis: Some(oracle.to_raw()), + } + } +} + +impl TryFrom for SlinkyGenesis { + type Error = ::Error; + + fn try_from(value: raw::SlinkyGenesis) -> Result { + Self::try_from_raw(value) + } +} + +impl From for raw::SlinkyGenesis { + fn from(value: SlinkyGenesis) -> Self { + value.into_raw() + } +} + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct SlinkyGenesisError(SlinkyGenesisErrorKind); + +impl SlinkyGenesisError { + fn field_not_set(name: &'static str) -> Self { + Self(SlinkyGenesisErrorKind::FieldNotSet { + name, + }) + } + + fn market_map(source: MarketMapGenesisStateError) -> Self { + Self(SlinkyGenesisErrorKind::MarketMap { + source, + }) + } + + fn oracle(source: OracleGenesisStateError) -> Self { + Self(SlinkyGenesisErrorKind::Oracle { + source, + }) + } +} + +#[derive(Debug, thiserror::Error)] +#[error("failed ensuring invariants of {}", SlinkyGenesis::full_name())] +enum SlinkyGenesisErrorKind { + #[error("field was not set: `{name}`")] + FieldNotSet { name: &'static str }, + #[error("`market_map` field was invalid")] + MarketMap { source: MarketMapGenesisStateError }, + #[error("`oracle` field was invalid")] + Oracle { source: OracleGenesisStateError }, +} + /// The genesis state of Astria's Sequencer. /// /// Verified to only contain valid fields (right now, addresses that have the same base prefix @@ -48,8 +159,7 @@ pub struct GenesisAppState { ibc_parameters: IBCParameters, allowed_fee_assets: Vec, fees: Fees, - market_map: MarketMapGenesisState, - oracle: OracleGenesisState, + slinky_genesis: SlinkyGenesis, } impl GenesisAppState { @@ -104,13 +214,8 @@ impl GenesisAppState { } #[must_use] - pub fn market_map(&self) -> &MarketMapGenesisState { - &self.market_map - } - - #[must_use] - pub fn oracle(&self) -> &OracleGenesisState { - &self.oracle + pub fn slinky_genesis(&self) -> &SlinkyGenesis { + &self.slinky_genesis } fn ensure_address_has_base_prefix( @@ -144,14 +249,21 @@ impl GenesisAppState { self.ensure_address_has_base_prefix(address, &format!(".ibc_relayer_addresses[{i}]"))?; } - for (i, address) in self.market_map.params.market_authorities.iter().enumerate() { + for (i, address) in self + .slinky_genesis + .market_map + .params + .market_authorities + .iter() + .enumerate() + { self.ensure_address_has_base_prefix( address, &format!(".market_map.params.market_authorities[{i}]"), )?; } self.ensure_address_has_base_prefix( - &self.market_map.params.admin, + &self.slinky_genesis.market_map.params.admin, ".market_map.params.admin", )?; @@ -175,8 +287,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - market_map_genesis, - oracle_genesis, + slinky_genesis, } = raw; let address_prefixes = address_prefixes .as_ref() @@ -229,7 +340,12 @@ impl Protobuf for GenesisAppState { .ok_or_else(|| Self::Error::field_not_set("fees")) .and_then(|fees| Fees::try_from_raw_ref(fees).map_err(Self::Error::fees))?; - let market_map = market_map_genesis + let slinky_genesis = slinky_genesis + .as_ref() + .ok_or_else(|| Self::Error::field_not_set("slinky_genesis"))?; + + let market_map = slinky_genesis + .market_map_genesis .as_ref() .ok_or_else(|| Self::Error::field_not_set("market_map_genesis")) .and_then(|market_map| { @@ -237,7 +353,8 @@ impl Protobuf for GenesisAppState { .map_err(Self::Error::market_map) })?; - let oracle = oracle_genesis + let oracle = slinky_genesis + .oracle_genesis .as_ref() .ok_or_else(|| Self::Error::field_not_set("oracle_genesis")) .and_then(|oracle| { @@ -255,8 +372,10 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - market_map, - oracle, + slinky_genesis: SlinkyGenesis { + market_map, + oracle, + }, }; this.ensure_all_addresses_have_base_prefix() .map_err(Self::Error::address_does_not_match_base)?; @@ -275,8 +394,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - market_map, - oracle, + slinky_genesis, } = self; Self::Raw { address_prefixes: Some(address_prefixes.to_raw()), @@ -289,8 +407,7 @@ impl Protobuf for GenesisAppState { ibc_parameters: Some(ibc_parameters.to_raw()), allowed_fee_assets: allowed_fee_assets.iter().map(ToString::to_string).collect(), fees: Some(fees.to_raw()), - market_map_genesis: Some(market_map.clone().into_raw()), - oracle_genesis: Some(oracle.clone().into_raw()), + slinky_genesis: Some(slinky_genesis.clone().into_raw()), } } } @@ -775,26 +892,25 @@ mod tests { bridge_sudo_change_fee: Some(24.into()), ics20_withdrawal_base_fee: Some(24.into()), }), - market_map_genesis: Some( - MarketMapGenesisState { - market_map: MarketMap { - markets: std::collections::HashMap::new(), + slinky_genesis: Some( + SlinkyGenesis { + market_map: MarketMapGenesisState { + market_map: MarketMap { + markets: std::collections::HashMap::new(), + }, + last_updated: 0, + params: Params { + market_authorities: vec![], + admin: alice(), + }, }, - last_updated: 0, - params: Params { - market_authorities: vec![], - admin: alice(), + oracle: OracleGenesisState { + currency_pair_genesis: vec![], + next_id: 0, }, } .into_raw(), ), - oracle_genesis: Some( - OracleGenesisState { - currency_pair_genesis: vec![], - next_id: 0, - } - .into_raw(), - ), } } diff --git a/crates/astria-core/src/slinky/market_map.rs b/crates/astria-core/src/slinky/market_map.rs index 293b58f412..fba4d53090 100644 --- a/crates/astria-core/src/slinky/market_map.rs +++ b/crates/astria-core/src/slinky/market_map.rs @@ -11,6 +11,7 @@ pub mod v1 { AddressError, }, slinky::types::v1::CurrencyPair, + Protobuf, }; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -21,7 +22,14 @@ pub mod v1 { pub params: Params, } - impl GenesisState { + impl Protobuf for GenesisState { + type Error = GenesisStateError; + type Raw = raw::GenesisState; + + fn try_from_raw_ref(raw: &raw::GenesisState) -> Result { + Self::try_from_raw(raw.clone()) + } + /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. /// /// # Errors @@ -30,7 +38,7 @@ pub mod v1 { /// - if the `market_map` field is invalid /// - if the `params` field is missing /// - if the `params` field is invalid - pub fn try_from_raw(raw: raw::GenesisState) -> Result { + fn try_from_raw(raw: raw::GenesisState) -> Result { let Some(market_map) = raw .market_map .map(MarketMap::try_from_raw) @@ -55,8 +63,12 @@ pub mod v1 { }) } + fn to_raw(&self) -> raw::GenesisState { + self.clone().into_raw() + } + #[must_use] - pub fn into_raw(self) -> raw::GenesisState { + fn into_raw(self) -> raw::GenesisState { raw::GenesisState { market_map: Some(self.market_map.into_raw()), last_updated: self.last_updated, diff --git a/crates/astria-core/src/slinky/oracle.rs b/crates/astria-core/src/slinky/oracle.rs index c188af21ba..eb3ae2dd34 100644 --- a/crates/astria-core/src/slinky/oracle.rs +++ b/crates/astria-core/src/slinky/oracle.rs @@ -4,6 +4,7 @@ pub mod v1 { use crate::{ generated::astria_vendored::slinky::oracle::v1 as raw, slinky::types::v1::CurrencyPair, + Protobuf, }; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -250,13 +251,31 @@ pub mod v1 { pub next_id: u64, } - impl GenesisState { + impl Protobuf for GenesisState { + type Error = GenesisStateError; + type Raw = raw::GenesisState; + + fn try_from_raw_ref(raw: &raw::GenesisState) -> Result { + let currency_pair_genesis = raw + .currency_pair_genesis + .clone() + .into_iter() + .map(CurrencyPairGenesis::try_from_raw) + .collect::, _>>() + .map_err(GenesisStateError::currency_pair_genesis_parse_error)?; + let next_id = raw.next_id; + Ok(Self { + currency_pair_genesis, + next_id, + }) + } + /// Converts from a raw protobuf `GenesisState` to a native `GenesisState`. /// /// # Errors /// /// - if any of the `currency_pair_genesis` are invalid - pub fn try_from_raw(raw: raw::GenesisState) -> Result { + fn try_from_raw(raw: raw::GenesisState) -> Result { let currency_pair_genesis = raw .currency_pair_genesis .into_iter() @@ -270,8 +289,20 @@ pub mod v1 { }) } + fn to_raw(&self) -> raw::GenesisState { + raw::GenesisState { + currency_pair_genesis: self + .currency_pair_genesis + .clone() + .into_iter() + .map(CurrencyPairGenesis::into_raw) + .collect(), + next_id: self.next_id, + } + } + #[must_use] - pub fn into_raw(self) -> raw::GenesisState { + fn into_raw(self) -> raw::GenesisState { raw::GenesisState { currency_pair_genesis: self .currency_pair_genesis diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 5e52ca4847..214522900a 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -156,52 +156,62 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1: } .into_raw(), ), - market_map_genesis: Some( - MarketMapGenesisState { - market_map: MarketMap { - markets: genesis_state_markets(), - }, - last_updated: 0, - params: Params { - market_authorities: vec![alice(), bob()], - admin: alice(), - }, - } - .into_raw(), - ), - oracle_genesis: Some( - OracleGenesisState { - currency_pair_genesis: vec![ - CurrencyPairGenesis { - id: 0, - nonce: 0, - currency_pair_price: QuotePrice { - price: 5_834_065_777, - block_height: 0, - block_timestamp: pbjson_types::Timestamp { - seconds: 1_720_122_395, - nanos: 0, - }, + slinky_genesis: Some( + astria_core::generated::protocol::genesis::v1alpha1::SlinkyGenesis { + market_map_genesis: Some( + MarketMapGenesisState { + market_map: MarketMap { + markets: genesis_state_markets(), }, - currency_pair: CurrencyPair::new("BTC".to_string(), "USD".to_string()), - }, - CurrencyPairGenesis { - id: 1, - nonce: 0, - currency_pair_price: QuotePrice { - price: 3_138_872_234, - block_height: 0, - block_timestamp: pbjson_types::Timestamp { - seconds: 1_720_122_395, - nanos: 0, - }, + last_updated: 0, + params: Params { + market_authorities: vec![alice(), bob()], + admin: alice(), }, - currency_pair: CurrencyPair::new("ETH".to_string(), "USD".to_string()), - }, - ], - next_id: 2, - } - .into_raw(), + } + .into_raw(), + ), + oracle_genesis: Some( + OracleGenesisState { + currency_pair_genesis: vec![ + CurrencyPairGenesis { + id: 0, + nonce: 0, + currency_pair_price: QuotePrice { + price: 5_834_065_777, + block_height: 0, + block_timestamp: pbjson_types::Timestamp { + seconds: 1_720_122_395, + nanos: 0, + }, + }, + currency_pair: CurrencyPair::new( + "BTC".to_string(), + "USD".to_string(), + ), + }, + CurrencyPairGenesis { + id: 1, + nonce: 0, + currency_pair_price: QuotePrice { + price: 3_138_872_234, + block_height: 0, + block_timestamp: pbjson_types::Timestamp { + seconds: 1_720_122_395, + nanos: 0, + }, + }, + currency_pair: CurrencyPair::new( + "ETH".to_string(), + "USD".to_string(), + ), + }, + ], + next_id: 2, + } + .into_raw(), + ), + }, ), } } diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 4aa16fa176..0e96997cc9 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -103,6 +103,7 @@ pub(crate) fn proto_genesis_state() generated::protocol::genesis::v1alpha1::{ GenesisAppState, IbcParameters, + SlinkyGenesis, }, slinky::{ market_map::v1::{ @@ -132,26 +133,28 @@ pub(crate) fn proto_genesis_state() }), allowed_fee_assets: vec![crate::test_utils::nria().to_string()], fees: Some(default_fees().to_raw()), - market_map_genesis: Some( - MarketMapGenesisState { - market_map: MarketMap { - markets: std::collections::HashMap::new(), - }, - last_updated: 0, - params: Params { - market_authorities: vec![], - admin: astria_address_from_hex_string(ALICE_ADDRESS), - }, - } - .into_raw(), - ), - oracle_genesis: Some( - OracleGenesisState { - currency_pair_genesis: vec![], - next_id: 0, - } - .into_raw(), - ), + slinky_genesis: Some(SlinkyGenesis { + market_map_genesis: Some( + MarketMapGenesisState { + market_map: MarketMap { + markets: std::collections::HashMap::new(), + }, + last_updated: 0, + params: Params { + market_authorities: vec![], + admin: astria_address_from_hex_string(ALICE_ADDRESS), + }, + } + .into_raw(), + ), + oracle_genesis: Some( + OracleGenesisState { + currency_pair_genesis: vec![], + next_id: 0, + } + .into_raw(), + ), + }), } } diff --git a/crates/astria-sequencer/src/slinky/marketmap/component.rs b/crates/astria-sequencer/src/slinky/marketmap/component.rs index 92face4201..020ff55977 100644 --- a/crates/astria-sequencer/src/slinky/marketmap/component.rs +++ b/crates/astria-sequencer/src/slinky/marketmap/component.rs @@ -27,10 +27,10 @@ impl Component for MarketMapComponent { // only required for related actions however state - .put_market_map(app_state.market_map().market_map.clone()) + .put_market_map(app_state.slinky_genesis().market_map().market_map.clone()) .context("failed to put market map")?; state - .put_params(app_state.market_map().params.clone()) + .put_params(app_state.slinky_genesis().market_map().params.clone()) .context("failed to put params")?; Ok(()) } diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index b95a1d972a..960edb05ad 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -26,7 +26,7 @@ impl Component for OracleComponent { #[instrument(name = "OracleComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { - for currency_pair in &app_state.oracle().currency_pair_genesis { + for currency_pair in &app_state.slinky_genesis().oracle().currency_pair_genesis { let currency_pair_state = CurrencyPairState { id: currency_pair.id(), nonce: currency_pair.nonce(), @@ -42,10 +42,16 @@ impl Component for OracleComponent { } state - .put_next_currency_pair_id(app_state.oracle().next_id) + .put_next_currency_pair_id(app_state.slinky_genesis().oracle().next_id) .context("failed to put next currency pair id")?; state - .put_num_currency_pairs(app_state.oracle().currency_pair_genesis.len() as u64) + .put_num_currency_pairs( + app_state + .slinky_genesis() + .oracle() + .currency_pair_genesis + .len() as u64, + ) .context("failed to put number of currency pairs")?; state .put_num_removed_currency_pairs(0) diff --git a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto index 7bb61d866f..aee184268b 100644 --- a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto +++ b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto @@ -17,8 +17,7 @@ message GenesisAppState { IbcParameters ibc_parameters = 8; repeated string allowed_fee_assets = 9; Fees fees = 10; - astria_vendored.slinky.marketmap.v1.GenesisState market_map_genesis = 11; - astria_vendored.slinky.oracle.v1.GenesisState oracle_genesis = 12; + SlinkyGenesis slinky_genesis = 11; } message Account { @@ -49,3 +48,8 @@ message Fees { astria.primitive.v1.Uint128 bridge_sudo_change_fee = 6; astria.primitive.v1.Uint128 ics20_withdrawal_base_fee = 7; } + +message SlinkyGenesis { + astria_vendored.slinky.marketmap.v1.GenesisState market_map_genesis = 1; + astria_vendored.slinky.oracle.v1.GenesisState oracle_genesis = 2; +} diff --git a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto b/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto index 249fbb95be..39423b4bac 100644 --- a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto +++ b/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto @@ -1,7 +1,6 @@ syntax = "proto3"; package astria_vendored.slinky.oracle.v1; -import "cosmos_sdk/cosmos_proto/cosmos.proto"; import "google/protobuf/timestamp.proto"; import "astria_vendored/slinky/types/v1/currency_pair.proto"; @@ -10,7 +9,7 @@ option go_package = "github.com/skip-mev/slinky/x/oracle/types"; // QuotePrice is the representation of the aggregated prices for a CurrencyPair, // where price represents the price of Base in terms of Quote message QuotePrice { - string price = 1 [(cosmos_proto.scalar) = "cosmos.Int"]; + string price = 1; // BlockTimestamp tracks the block height associated with this price update. // We include block timestamp alongside the price to ensure that smart diff --git a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto b/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto index 9577a31144..6b4b9122ca 100644 --- a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto +++ b/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto @@ -1,7 +1,6 @@ syntax = "proto3"; package astria_vendored.slinky.service.v1; -import "cosmos_sdk/cosmos_proto/cosmos.proto"; import "google/api/annotations.proto"; import "google/protobuf/timestamp.proto"; import "astria_vendored/slinky/marketmap/v1/market.proto"; diff --git a/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto b/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto deleted file mode 100644 index d3f331ac6e..0000000000 --- a/proto/vendored/cosmos_sdk/cosmos_proto/cosmos.proto +++ /dev/null @@ -1,106 +0,0 @@ -syntax = "proto3"; -package cosmos_proto; - -import "google/protobuf/descriptor.proto"; - -option go_package = "github.com/cosmos/cosmos-proto;cosmos_proto"; - -extend google.protobuf.MethodOptions { - // method_added_in is used to indicate from which version the method was added. - string method_added_in = 93001; -} - -extend google.protobuf.MessageOptions { - // implements_interface is used to indicate the type name of the interface - // that a message implements so that it can be used in google.protobuf.Any - // fields that accept that interface. A message can implement multiple - // interfaces. Interfaces should be declared using a declare_interface - // file option. - repeated string implements_interface = 93001; - - // message_added_in is used to indicate from which version the message was added. - string message_added_in = 93002; -} - -extend google.protobuf.FieldOptions { - // accepts_interface is used to annotate that a google.protobuf.Any - // field accepts messages that implement the specified interface. - // Interfaces should be declared using a declare_interface file option. - string accepts_interface = 93001; - - // scalar is used to indicate that this field follows the formatting defined - // by the named scalar which should be declared with declare_scalar. Code - // generators may choose to use this information to map this field to a - // language-specific type representing the scalar. - string scalar = 93002; - - // field_added_in is used to indicate from which version the field was added. - string field_added_in = 93003; -} - -extend google.protobuf.FileOptions { - // declare_interface declares an interface type to be used with - // accepts_interface and implements_interface. Interface names are - // expected to follow the following convention such that their declaration - // can be discovered by tools: for a given interface type a.b.C, it is - // expected that the declaration will be found in a protobuf file named - // a/b/interfaces.proto in the file descriptor set. - repeated InterfaceDescriptor declare_interface = 793021; - - // declare_scalar declares a scalar type to be used with - // the scalar field option. Scalar names are - // expected to follow the following convention such that their declaration - // can be discovered by tools: for a given scalar type a.b.C, it is - // expected that the declaration will be found in a protobuf file named - // a/b/scalars.proto in the file descriptor set. - repeated ScalarDescriptor declare_scalar = 793022; - - // file_added_in is used to indicate from which the version the file was added. - string file_added_in = 793023; -} - -// InterfaceDescriptor describes an interface type to be used with -// accepts_interface and implements_interface and declared by declare_interface. -message InterfaceDescriptor { - // name is the name of the interface. It should be a short-name (without - // a period) such that the fully qualified name of the interface will be - // package.name, ex. for the package a.b and interface named C, the - // fully-qualified name will be a.b.C. - string name = 1; - - // description is a human-readable description of the interface and its - // purpose. - string description = 2; -} - -// ScalarDescriptor describes an scalar type to be used with -// the scalar field option and declared by declare_scalar. -// Scalars extend simple protobuf built-in types with additional -// syntax and semantics, for instance to represent big integers. -// Scalars should ideally define an encoding such that there is only one -// valid syntactical representation for a given semantic meaning, -// i.e. the encoding should be deterministic. -message ScalarDescriptor { - // name is the name of the scalar. It should be a short-name (without - // a period) such that the fully qualified name of the scalar will be - // package.name, ex. for the package a.b and scalar named C, the - // fully-qualified name will be a.b.C. - string name = 1; - - // description is a human-readable description of the scalar and its - // encoding format. For instance a big integer or decimal scalar should - // specify precisely the expected encoding format. - string description = 2; - - // field_type is the type of field with which this scalar can be used. - // Scalars can be used with one and only one type of field so that - // encoding standards and simple and clear. Currently only string and - // bytes fields are supported for scalars. - repeated ScalarType field_type = 3; -} - -enum ScalarType { - SCALAR_TYPE_UNSPECIFIED = 0; - SCALAR_TYPE_STRING = 1; - SCALAR_TYPE_BYTES = 2; -} diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index a42a3ba329..a72fe44545 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -74,7 +74,6 @@ fn main() { .client_mod_attribute(".", "#[cfg(feature=\"client\")]") .server_mod_attribute(".", "#[cfg(feature=\"server\")]") .extern_path(".astria_vendored.penumbra", "::penumbra-proto") - //.extern_path(".astria_vendored.slinky", "crate::generated::astria_vendored::slinky") // .extern_path( // ".astria_vendored.slinky.marketmap.v1.GenesisState", // "crate::generated::astria_vendored::slinky::marketmap::v1::GenesisState", From 64ad809cde2e6813f53b5151b61d4be647496b2d Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 23 Aug 2024 14:26:22 -0400 Subject: [PATCH 39/89] address comments, var name cleanup --- Cargo.lock | 1 + charts/sequencer/templates/configmaps.yaml | 2 +- .../astria.protocol.genesis.v1alpha1.rs | 6 +- .../astria.protocol.genesis.v1alpha1.serde.rs | 76 +++++++++---------- ...a1__tests__genesis_state_is_unchanged.snap | 6 +- .../src/protocol/genesis/v1alpha1.rs | 52 ++++++------- crates/astria-core/src/slinky/abci.rs | 8 +- .../src/genesis_example.rs | 6 +- crates/astria-sequencer/Cargo.toml | 1 + crates/astria-sequencer/local.env.example | 6 +- crates/astria-sequencer/src/app/test_utils.rs | 6 +- .../src/app/vote_extension.rs | 3 +- crates/astria-sequencer/src/config.rs | 6 +- crates/astria-sequencer/src/sequencer.rs | 6 +- .../src/slinky/marketmap/component.rs | 4 +- .../src/slinky/oracle/component.rs | 12 +-- .../protocol/genesis/v1alpha1/types.proto | 6 +- 17 files changed, 101 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09897bfe3f..898bb6e215 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -809,6 +809,7 @@ dependencies = [ "hex", "ibc-proto", "ibc-types", + "indexmap 2.4.0", "insta", "is_sorted", "matchit", diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index d0db3b365c..9d4a8004e4 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -77,6 +77,6 @@ data: ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" {{- if not .Values.global.dev }} {{- else }} - ASTRIA_SEQUENCER_ORACLE_ENABLED: "false" + ASTRIA_SEQUENCER_NO_ORACLE: "true" {{- end }} --- diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs index c5ce8cf844..c384edc447 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs @@ -28,7 +28,7 @@ pub struct GenesisAppState { #[prost(message, optional, tag = "10")] pub fees: ::core::option::Option, #[prost(message, optional, tag = "11")] - pub slinky_genesis: ::core::option::Option, + pub slinky: ::core::option::Option, } impl ::prost::Name for GenesisAppState { const NAME: &'static str = "GenesisAppState"; @@ -130,11 +130,11 @@ impl ::prost::Name for Fees { #[derive(Clone, PartialEq, ::prost::Message)] pub struct SlinkyGenesis { #[prost(message, optional, tag = "1")] - pub market_map_genesis: ::core::option::Option< + pub market_map: ::core::option::Option< super::super::super::astria_vendored::slinky::marketmap::v1::GenesisState, >, #[prost(message, optional, tag = "2")] - pub oracle_genesis: ::core::option::Option< + pub oracle: ::core::option::Option< super::super::super::astria_vendored::slinky::oracle::v1::GenesisState, >, } 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 index 087b7138d9..f540df9939 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs @@ -435,7 +435,7 @@ impl serde::Serialize for GenesisAppState { if self.fees.is_some() { len += 1; } - if self.slinky_genesis.is_some() { + if self.slinky.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.GenesisAppState", len)?; @@ -469,8 +469,8 @@ impl serde::Serialize for GenesisAppState { if let Some(v) = self.fees.as_ref() { struct_ser.serialize_field("fees", v)?; } - if let Some(v) = self.slinky_genesis.as_ref() { - struct_ser.serialize_field("slinkyGenesis", v)?; + if let Some(v) = self.slinky.as_ref() { + struct_ser.serialize_field("slinky", v)?; } struct_ser.end() } @@ -500,8 +500,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "allowed_fee_assets", "allowedFeeAssets", "fees", - "slinky_genesis", - "slinkyGenesis", + "slinky", ]; #[allow(clippy::enum_variant_names)] @@ -516,7 +515,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { IbcParameters, AllowedFeeAssets, Fees, - SlinkyGenesis, + Slinky, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -548,7 +547,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "ibcParameters" | "ibc_parameters" => Ok(GeneratedField::IbcParameters), "allowedFeeAssets" | "allowed_fee_assets" => Ok(GeneratedField::AllowedFeeAssets), "fees" => Ok(GeneratedField::Fees), - "slinkyGenesis" | "slinky_genesis" => Ok(GeneratedField::SlinkyGenesis), + "slinky" => Ok(GeneratedField::Slinky), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -578,7 +577,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { let mut ibc_parameters__ = None; let mut allowed_fee_assets__ = None; let mut fees__ = None; - let mut slinky_genesis__ = None; + let mut slinky__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::ChainId => { @@ -641,11 +640,11 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { } fees__ = map_.next_value()?; } - GeneratedField::SlinkyGenesis => { - if slinky_genesis__.is_some() { - return Err(serde::de::Error::duplicate_field("slinkyGenesis")); + GeneratedField::Slinky => { + if slinky__.is_some() { + return Err(serde::de::Error::duplicate_field("slinky")); } - slinky_genesis__ = map_.next_value()?; + slinky__ = map_.next_value()?; } } } @@ -660,7 +659,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { ibc_parameters: ibc_parameters__, allowed_fee_assets: allowed_fee_assets__.unwrap_or_default(), fees: fees__, - slinky_genesis: slinky_genesis__, + slinky: slinky__, }) } } @@ -803,18 +802,18 @@ impl serde::Serialize for SlinkyGenesis { { use serde::ser::SerializeStruct; let mut len = 0; - if self.market_map_genesis.is_some() { + if self.market_map.is_some() { len += 1; } - if self.oracle_genesis.is_some() { + if self.oracle.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.SlinkyGenesis", len)?; - if let Some(v) = self.market_map_genesis.as_ref() { - struct_ser.serialize_field("marketMapGenesis", v)?; + if let Some(v) = self.market_map.as_ref() { + struct_ser.serialize_field("marketMap", v)?; } - if let Some(v) = self.oracle_genesis.as_ref() { - struct_ser.serialize_field("oracleGenesis", v)?; + if let Some(v) = self.oracle.as_ref() { + struct_ser.serialize_field("oracle", v)?; } struct_ser.end() } @@ -826,16 +825,15 @@ impl<'de> serde::Deserialize<'de> for SlinkyGenesis { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "market_map_genesis", - "marketMapGenesis", - "oracle_genesis", - "oracleGenesis", + "market_map", + "marketMap", + "oracle", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - MarketMapGenesis, - OracleGenesis, + MarketMap, + Oracle, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -857,8 +855,8 @@ impl<'de> serde::Deserialize<'de> for SlinkyGenesis { E: serde::de::Error, { match value { - "marketMapGenesis" | "market_map_genesis" => Ok(GeneratedField::MarketMapGenesis), - "oracleGenesis" | "oracle_genesis" => Ok(GeneratedField::OracleGenesis), + "marketMap" | "market_map" => Ok(GeneratedField::MarketMap), + "oracle" => Ok(GeneratedField::Oracle), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -878,27 +876,27 @@ impl<'de> serde::Deserialize<'de> for SlinkyGenesis { where V: serde::de::MapAccess<'de>, { - let mut market_map_genesis__ = None; - let mut oracle_genesis__ = None; + let mut market_map__ = None; + let mut oracle__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::MarketMapGenesis => { - if market_map_genesis__.is_some() { - return Err(serde::de::Error::duplicate_field("marketMapGenesis")); + GeneratedField::MarketMap => { + if market_map__.is_some() { + return Err(serde::de::Error::duplicate_field("marketMap")); } - market_map_genesis__ = map_.next_value()?; + market_map__ = map_.next_value()?; } - GeneratedField::OracleGenesis => { - if oracle_genesis__.is_some() { - return Err(serde::de::Error::duplicate_field("oracleGenesis")); + GeneratedField::Oracle => { + if oracle__.is_some() { + return Err(serde::de::Error::duplicate_field("oracle")); } - oracle_genesis__ = map_.next_value()?; + oracle__ = map_.next_value()?; } } } Ok(SlinkyGenesis { - market_map_genesis: market_map_genesis__, - oracle_genesis: oracle_genesis__, + market_map: market_map__, + oracle: oracle__, }) } } diff --git a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 index d87c3b0a7e..5c0d1b9588 100644 --- a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 @@ -79,13 +79,13 @@ expression: genesis_state() "lo": "24" } }, - "slinkyGenesis": { - "marketMapGenesis": { + "slinky": { + "marketMap": { "marketMap": {}, "params": { "admin": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" } }, - "oracleGenesis": {} + "oracle": {} } } diff --git a/crates/astria-core/src/protocol/genesis/v1alpha1.rs b/crates/astria-core/src/protocol/genesis/v1alpha1.rs index 26957227cc..a954f7f3eb 100644 --- a/crates/astria-core/src/protocol/genesis/v1alpha1.rs +++ b/crates/astria-core/src/protocol/genesis/v1alpha1.rs @@ -56,18 +56,18 @@ impl Protobuf for SlinkyGenesis { fn try_from_raw_ref(raw: &Self::Raw) -> Result { let Self::Raw { - market_map_genesis, - oracle_genesis, + market_map, + oracle, } = raw; - let market_map = market_map_genesis + let market_map = market_map .as_ref() - .ok_or_else(|| Self::Error::field_not_set("market_map_genesis")) + .ok_or_else(|| Self::Error::field_not_set("market_map")) .and_then(|market_map| { MarketMapGenesisState::try_from_raw_ref(market_map).map_err(Self::Error::market_map) })?; - let oracle = oracle_genesis + let oracle = oracle .as_ref() - .ok_or_else(|| Self::Error::field_not_set("oracle_genesis")) + .ok_or_else(|| Self::Error::field_not_set("oracle")) .and_then(|oracle| { OracleGenesisState::try_from_raw_ref(oracle).map_err(Self::Error::oracle) })?; @@ -83,8 +83,8 @@ impl Protobuf for SlinkyGenesis { oracle, } = self; Self::Raw { - market_map_genesis: Some(market_map.to_raw()), - oracle_genesis: Some(oracle.to_raw()), + market_map: Some(market_map.to_raw()), + oracle: Some(oracle.to_raw()), } } } @@ -159,7 +159,7 @@ pub struct GenesisAppState { ibc_parameters: IBCParameters, allowed_fee_assets: Vec, fees: Fees, - slinky_genesis: SlinkyGenesis, + slinky: SlinkyGenesis, } impl GenesisAppState { @@ -214,8 +214,8 @@ impl GenesisAppState { } #[must_use] - pub fn slinky_genesis(&self) -> &SlinkyGenesis { - &self.slinky_genesis + pub fn slinky(&self) -> &SlinkyGenesis { + &self.slinky } fn ensure_address_has_base_prefix( @@ -250,7 +250,7 @@ impl GenesisAppState { } for (i, address) in self - .slinky_genesis + .slinky .market_map .params .market_authorities @@ -263,7 +263,7 @@ impl GenesisAppState { )?; } self.ensure_address_has_base_prefix( - &self.slinky_genesis.market_map.params.admin, + &self.slinky.market_map.params.admin, ".market_map.params.admin", )?; @@ -287,7 +287,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - slinky_genesis, + slinky, } = raw; let address_prefixes = address_prefixes .as_ref() @@ -340,23 +340,23 @@ impl Protobuf for GenesisAppState { .ok_or_else(|| Self::Error::field_not_set("fees")) .and_then(|fees| Fees::try_from_raw_ref(fees).map_err(Self::Error::fees))?; - let slinky_genesis = slinky_genesis + let slinky = slinky .as_ref() - .ok_or_else(|| Self::Error::field_not_set("slinky_genesis"))?; + .ok_or_else(|| Self::Error::field_not_set("slinky"))?; - let market_map = slinky_genesis - .market_map_genesis + let market_map = slinky + .market_map .as_ref() - .ok_or_else(|| Self::Error::field_not_set("market_map_genesis")) + .ok_or_else(|| Self::Error::field_not_set("market_map")) .and_then(|market_map| { MarketMapGenesisState::try_from_raw(market_map.clone()) .map_err(Self::Error::market_map) })?; - let oracle = slinky_genesis - .oracle_genesis + let oracle = slinky + .oracle .as_ref() - .ok_or_else(|| Self::Error::field_not_set("oracle_genesis")) + .ok_or_else(|| Self::Error::field_not_set("oracle")) .and_then(|oracle| { OracleGenesisState::try_from_raw(oracle.clone()).map_err(Self::Error::oracle) })?; @@ -372,7 +372,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - slinky_genesis: SlinkyGenesis { + slinky: SlinkyGenesis { market_map, oracle, }, @@ -394,7 +394,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - slinky_genesis, + slinky, } = self; Self::Raw { address_prefixes: Some(address_prefixes.to_raw()), @@ -407,7 +407,7 @@ impl Protobuf for GenesisAppState { ibc_parameters: Some(ibc_parameters.to_raw()), allowed_fee_assets: allowed_fee_assets.iter().map(ToString::to_string).collect(), fees: Some(fees.to_raw()), - slinky_genesis: Some(slinky_genesis.clone().into_raw()), + slinky: Some(slinky.to_raw()), } } } @@ -892,7 +892,7 @@ mod tests { bridge_sudo_change_fee: Some(24.into()), ics20_withdrawal_base_fee: Some(24.into()), }), - slinky_genesis: Some( + slinky: Some( SlinkyGenesis { market_map: MarketMapGenesisState { market_map: MarketMap { diff --git a/crates/astria-core/src/slinky/abci.rs b/crates/astria-core/src/slinky/abci.rs index f5002e40ff..f6c755a284 100644 --- a/crates/astria-core/src/slinky/abci.rs +++ b/crates/astria-core/src/slinky/abci.rs @@ -1,25 +1,25 @@ pub mod v1 { - use std::collections::HashMap; + use indexmap::IndexMap; use crate::generated::astria_vendored::slinky::abci::v1 as raw; #[derive(Debug, Clone, PartialEq, Eq)] pub struct OracleVoteExtension { - pub prices: HashMap, + pub prices: IndexMap, } impl OracleVoteExtension { #[must_use] pub fn from_raw(raw: raw::OracleVoteExtension) -> Self { Self { - prices: raw.prices, + prices: raw.prices.into_iter().collect(), } } #[must_use] pub fn into_raw(self) -> raw::OracleVoteExtension { raw::OracleVoteExtension { - prices: self.prices, + prices: self.prices.into_iter().collect(), } } } diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 214522900a..f2abf35040 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -156,9 +156,9 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1: } .into_raw(), ), - slinky_genesis: Some( + slinky: Some( astria_core::generated::protocol::genesis::v1alpha1::SlinkyGenesis { - market_map_genesis: Some( + market_map: Some( MarketMapGenesisState { market_map: MarketMap { markets: genesis_state_markets(), @@ -171,7 +171,7 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1: } .into_raw(), ), - oracle_genesis: Some( + oracle: Some( OracleGenesisState { currency_pair_genesis: vec![ CurrencyPairGenesis { diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index e0431ce343..6ac9ebb98d 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -47,6 +47,7 @@ divan = { workspace = true, optional = true } futures = { workspace = true } hex = { workspace = true, features = ["serde"] } ibc-types = { workspace = true, features = ["with_serde"] } +indexmap = { workspace = true } penumbra-ibc = { workspace = true, features = ["component", "rpc"] } metrics = { workspace = true } pbjson-types = { workspace = true } diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index b345c58347..013b0e85aa 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -34,9 +34,9 @@ ASTRIA_SEQUENCER_METRICS_HTTP_LISTENER_ADDR="127.0.0.1:9000" # `ASTRIA_SEQUENCER_FORCE_STDOUT` is set to `true`. ASTRIA_SEQUENCER_PRETTY_PRINT=false -# If the oracle is enabled. If true, the oracle_grpc_addr must be set. -# Should be true for validator nodes and false for non-validator nodes. -ASTRIA_SEQUENCER_ORACLE_ENABLED=false +# If the oracle is disabled. If false, the oracle_grpc_addr must be set. +# Should be false for validator nodes and true for non-validator nodes. +ASTRIA_SEQUENCER_NO_ORACLE=true # The gRPC endpoint for the oracle sidecar. ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="http://127.0.0.1:8080" diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 0e96997cc9..8ec80a3c22 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -133,8 +133,8 @@ pub(crate) fn proto_genesis_state() }), allowed_fee_assets: vec![crate::test_utils::nria().to_string()], fees: Some(default_fees().to_raw()), - slinky_genesis: Some(SlinkyGenesis { - market_map_genesis: Some( + slinky: Some(SlinkyGenesis { + market_map: Some( MarketMapGenesisState { market_map: MarketMap { markets: std::collections::HashMap::new(), @@ -147,7 +147,7 @@ pub(crate) fn proto_genesis_state() } .into_raw(), ), - oracle_genesis: Some( + oracle: Some( OracleGenesisState { currency_pair_genesis: vec![], next_id: 0, diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 7c3387fddb..6268ae768d 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -21,6 +21,7 @@ use astria_core::{ types::v1::CurrencyPair, }, }; +use indexmap::IndexMap; use prost::Message as _; use tendermint::{ abci, @@ -149,7 +150,7 @@ async fn transform_oracle_service_prices( state: &S, prices: QueryPricesResponse, ) -> anyhow::Result { - let mut strategy_prices = HashMap::new(); + let mut strategy_prices = IndexMap::new(); for (currency_pair_id, price_string) in prices.prices { let currency_pair = currency_pair_id .parse() diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index f6395d3690..e1eb87eb6d 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -31,9 +31,9 @@ pub struct Config { pub metrics_http_listener_addr: String, /// Writes a human readable format to stdout instead of JSON formatted OTEL trace data. pub pretty_print: bool, - /// If the oracle is enabled. If true, the `oracle_grpc_addr` must be set. - /// Should be true for validator nodes and false for non-validator nodes. - pub oracle_enabled: bool, + /// If the oracle is disabled. If false, the `oracle_grpc_addr` must be set. + /// Should be false for validator nodes and true for non-validator nodes. + pub no_oracle: bool, /// The gRPC endpoint for the oracle sidecar. pub oracle_grpc_addr: String, /// The timeout for the responses from the oracle sidecar in milliseconds. diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index ca972f541a..6d7c709fd2 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -115,7 +115,9 @@ impl Sequencer { .context("failed to query state for base prefix")?; } - let oracle_client = if config.oracle_enabled { + let oracle_client = if config.no_oracle { + None + } else { let uri: Uri = config .oracle_grpc_addr .parse() @@ -137,8 +139,6 @@ impl Sequencer { debug!(uri = %uri, "oracle sidecar is reachable"); }; Some(oracle_client) - } else { - None }; let mempool = Mempool::new(); diff --git a/crates/astria-sequencer/src/slinky/marketmap/component.rs b/crates/astria-sequencer/src/slinky/marketmap/component.rs index 020ff55977..058c0270e7 100644 --- a/crates/astria-sequencer/src/slinky/marketmap/component.rs +++ b/crates/astria-sequencer/src/slinky/marketmap/component.rs @@ -27,10 +27,10 @@ impl Component for MarketMapComponent { // only required for related actions however state - .put_market_map(app_state.slinky_genesis().market_map().market_map.clone()) + .put_market_map(app_state.slinky().market_map().market_map.clone()) .context("failed to put market map")?; state - .put_params(app_state.slinky_genesis().market_map().params.clone()) + .put_params(app_state.slinky().market_map().params.clone()) .context("failed to put params")?; Ok(()) } diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index 960edb05ad..9c7dc6d242 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -26,7 +26,7 @@ impl Component for OracleComponent { #[instrument(name = "OracleComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { - for currency_pair in &app_state.slinky_genesis().oracle().currency_pair_genesis { + for currency_pair in &app_state.slinky().oracle().currency_pair_genesis { let currency_pair_state = CurrencyPairState { id: currency_pair.id(), nonce: currency_pair.nonce(), @@ -42,16 +42,10 @@ impl Component for OracleComponent { } state - .put_next_currency_pair_id(app_state.slinky_genesis().oracle().next_id) + .put_next_currency_pair_id(app_state.slinky().oracle().next_id) .context("failed to put next currency pair id")?; state - .put_num_currency_pairs( - app_state - .slinky_genesis() - .oracle() - .currency_pair_genesis - .len() as u64, - ) + .put_num_currency_pairs(app_state.slinky().oracle().currency_pair_genesis.len() as u64) .context("failed to put number of currency pairs")?; state .put_num_removed_currency_pairs(0) diff --git a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto index aee184268b..3962280f69 100644 --- a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto +++ b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto @@ -17,7 +17,7 @@ message GenesisAppState { IbcParameters ibc_parameters = 8; repeated string allowed_fee_assets = 9; Fees fees = 10; - SlinkyGenesis slinky_genesis = 11; + SlinkyGenesis slinky = 11; } message Account { @@ -50,6 +50,6 @@ message Fees { } message SlinkyGenesis { - astria_vendored.slinky.marketmap.v1.GenesisState market_map_genesis = 1; - astria_vendored.slinky.oracle.v1.GenesisState oracle_genesis = 2; + astria_vendored.slinky.marketmap.v1.GenesisState market_map = 1; + astria_vendored.slinky.oracle.v1.GenesisState oracle = 2; } From c8437f10b0a01abc81464c62ab1ee3fe58341768 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 23 Aug 2024 21:31:25 +0200 Subject: [PATCH 40/89] fix(core): restructure modules to match proto (#1405) The module structure for the generated rust code was messed up and didn't match the protobuf package definitions. That messed up type references created by prost. --- .../astria.protocol.genesis.v1alpha1.rs | 4 +- .../astria.protocol.transactions.v1alpha1.rs | 2 +- crates/astria-core/src/generated/mod.rs | 149 +++++++++--------- tools/protobuf-compiler/src/main.rs | 12 -- 4 files changed, 80 insertions(+), 87 deletions(-) diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs index c384edc447..d59f9c9bf5 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs @@ -131,11 +131,11 @@ impl ::prost::Name for Fees { pub struct SlinkyGenesis { #[prost(message, optional, tag = "1")] pub market_map: ::core::option::Option< - super::super::super::astria_vendored::slinky::marketmap::v1::GenesisState, + super::super::super::super::astria_vendored::slinky::marketmap::v1::GenesisState, >, #[prost(message, optional, tag = "2")] pub oracle: ::core::option::Option< - super::super::super::astria_vendored::slinky::oracle::v1::GenesisState, + super::super::super::super::astria_vendored::slinky::oracle::v1::GenesisState, >, } impl ::prost::Name for SlinkyGenesis { diff --git a/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs index fd8f2cf0a0..6ee6592aac 100644 --- a/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs @@ -92,7 +92,7 @@ pub mod action { SudoAddressChangeAction(super::SudoAddressChangeAction), #[prost(message, tag = "51")] ValidatorUpdateAction( - crate::generated::astria_vendored::tendermint::abci::ValidatorUpdate, + super::super::super::super::super::astria_vendored::tendermint::abci::ValidatorUpdate, ), #[prost(message, tag = "52")] IbcRelayerChangeAction(super::IbcRelayerChangeAction), diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index 182a1ae275..06030164c3 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -11,6 +11,8 @@ //! [`buf`]: https://buf.build //! [`tools/protobuf-compiler`]: ../../../../tools/protobuf-compiler +pub use astria::*; + #[path = ""] pub mod astria_vendored { #[path = ""] @@ -101,108 +103,111 @@ pub mod astria_vendored { } #[path = ""] -pub mod execution { - #[path = "astria.execution.v1alpha1.rs"] - pub mod v1alpha1; - - pub mod v1alpha2 { - include!("astria.execution.v1alpha2.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.execution.v1alpha2.serde.rs"); - } - } -} +pub mod astria { + #[path = ""] + pub mod execution { + #[path = "astria.execution.v1alpha1.rs"] + pub mod v1alpha1; -#[path = ""] -pub mod primitive { - pub mod v1 { - include!("astria.primitive.v1.rs"); + pub mod v1alpha2 { + include!("astria.execution.v1alpha2.rs"); - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.primitive.v1.serde.rs"); + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("astria.execution.v1alpha2.serde.rs"); + } } } -} -#[path = ""] -pub mod protocol { #[path = ""] - pub mod accounts { - #[path = "astria.protocol.accounts.v1alpha1.rs"] - pub mod v1alpha1; - } - #[path = ""] - pub mod asset { - #[path = "astria.protocol.asset.v1alpha1.rs"] - pub mod v1alpha1; - } - #[path = ""] - pub mod bridge { - #[path = "astria.protocol.bridge.v1alpha1.rs"] - pub mod v1alpha1; - } - #[path = ""] - pub mod genesis { - pub mod v1alpha1 { - include!("astria.protocol.genesis.v1alpha1.rs"); + pub mod primitive { + pub mod v1 { + include!("astria.primitive.v1.rs"); #[cfg(feature = "serde")] - mod _serde_impls { + mod _serde_impl { use super::*; - include!("astria.protocol.genesis.v1alpha1.serde.rs"); + include!("astria.primitive.v1.serde.rs"); } } } + #[path = ""] - pub mod memos { - pub mod v1alpha1 { - include!("astria.protocol.memos.v1alpha1.rs"); + pub mod protocol { + #[path = ""] + pub mod accounts { + #[path = "astria.protocol.accounts.v1alpha1.rs"] + pub mod v1alpha1; + } + #[path = ""] + pub mod asset { + #[path = "astria.protocol.asset.v1alpha1.rs"] + pub mod v1alpha1; + } + #[path = ""] + pub mod bridge { + #[path = "astria.protocol.bridge.v1alpha1.rs"] + 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.memos.v1alpha1.serde.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"); + + #[cfg(feature = "serde")] + mod _serde_impls { + use super::*; + include!("astria.protocol.memos.v1alpha1.serde.rs"); + } + } + } + #[path = ""] + pub mod transactions { + pub mod v1alpha1 { + include!("astria.protocol.transactions.v1alpha1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("astria.protocol.transactions.v1alpha1.serde.rs"); + } } } } + #[path = ""] - pub mod transactions { + pub mod sequencerblock { pub mod v1alpha1 { - include!("astria.protocol.transactions.v1alpha1.rs"); + include!("astria.sequencerblock.v1alpha1.rs"); #[cfg(feature = "serde")] mod _serde_impl { use super::*; - include!("astria.protocol.transactions.v1alpha1.serde.rs"); + include!("astria.sequencerblock.v1alpha1.serde.rs"); } } } -} - -#[path = ""] -pub mod sequencerblock { - pub mod v1alpha1 { - include!("astria.sequencerblock.v1alpha1.rs"); - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.sequencerblock.v1alpha1.serde.rs"); - } + #[path = ""] + pub mod composer { + #[path = "astria.composer.v1alpha1.rs"] + pub mod v1alpha1; } } -#[path = ""] -pub mod composer { - #[path = "astria.composer.v1alpha1.rs"] - pub mod v1alpha1; -} - #[path = ""] pub mod celestia { #[path = "celestia.blob.v1.rs"] diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index a72fe44545..b6fe382a15 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -74,18 +74,6 @@ fn main() { .client_mod_attribute(".", "#[cfg(feature=\"client\")]") .server_mod_attribute(".", "#[cfg(feature=\"server\")]") .extern_path(".astria_vendored.penumbra", "::penumbra-proto") - // .extern_path( - // ".astria_vendored.slinky.marketmap.v1.GenesisState", - // "crate::generated::astria_vendored::slinky::marketmap::v1::GenesisState", - // ) - // .extern_path( - // ".astria_vendored.slinky.oracle.v1.GenesisState", - // "crate::generated::astria_vendored::slinky::oracle::v1::GenesisState", - // ) - .extern_path( - ".astria_vendored.tendermint.abci.ValidatorUpdate", - "crate::generated::astria_vendored::tendermint::abci::ValidatorUpdate", - ) .type_attribute(".astria.primitive.v1.Uint128", "#[derive(Copy)]") .type_attribute( ".astria.protocol.genesis.v1alpha1.IbcParameters", From 65fdc28cd5c3799b8ca6836408ece654aa5767e4 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Sun, 25 Aug 2024 22:21:57 -0400 Subject: [PATCH 41/89] fmt protos --- .../astria_vendored/slinky/marketmap/v1/query.proto | 2 +- .../protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto | 2 +- proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto | 2 +- .../protocolapis/astria_vendored/slinky/service/v1/oracle.proto | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto index e00b091f56..ec1ecffc55 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto +++ b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto @@ -1,10 +1,10 @@ syntax = "proto3"; package astria_vendored.slinky.marketmap.v1; -import "google/api/annotations.proto"; import "astria_vendored/slinky/marketmap/v1/market.proto"; import "astria_vendored/slinky/marketmap/v1/params.proto"; import "astria_vendored/slinky/types/v1/currency_pair.proto"; +import "google/api/annotations.proto"; option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; diff --git a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto b/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto index 39423b4bac..3a030f629a 100644 --- a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto +++ b/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto @@ -1,8 +1,8 @@ syntax = "proto3"; package astria_vendored.slinky.oracle.v1; -import "google/protobuf/timestamp.proto"; import "astria_vendored/slinky/types/v1/currency_pair.proto"; +import "google/protobuf/timestamp.proto"; option go_package = "github.com/skip-mev/slinky/x/oracle/types"; diff --git a/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto b/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto index d856069dcc..1cb6baee7e 100644 --- a/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto +++ b/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto @@ -1,9 +1,9 @@ syntax = "proto3"; package astria_vendored.slinky.oracle.v1; -import "google/api/annotations.proto"; import "astria_vendored/slinky/oracle/v1/genesis.proto"; import "astria_vendored/slinky/types/v1/currency_pair.proto"; +import "google/api/annotations.proto"; option go_package = "github.com/skip-mev/slinky/x/oracle/types"; diff --git a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto b/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto index 6b4b9122ca..c5ec0658cd 100644 --- a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto +++ b/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto @@ -1,9 +1,9 @@ syntax = "proto3"; package astria_vendored.slinky.service.v1; +import "astria_vendored/slinky/marketmap/v1/market.proto"; import "google/api/annotations.proto"; import "google/protobuf/timestamp.proto"; -import "astria_vendored/slinky/marketmap/v1/market.proto"; option go_package = "github.com/skip-mev/slinky/service/servers/oracle/types"; From 8c8d0eb34ab82751e73aac4e0f2d1033715641bc Mon Sep 17 00:00:00 2001 From: elizabeth Date: Sun, 25 Aug 2024 22:27:32 -0400 Subject: [PATCH 42/89] update buf cfg to ignore slinky lint warnings --- buf.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/buf.yaml b/buf.yaml index ffedb39540..2b55e50345 100644 --- a/buf.yaml +++ b/buf.yaml @@ -62,6 +62,12 @@ modules: ignore_only: PACKAGE_VERSION_SUFFIX: - proto/protocolapis/astria_vendored/tendermint + FIELD_LOWER_SNAKE_CASE: + - proto/protocolapis/astria_vendored/slinky + SERVICE_SUFFIX: + - proto/protocolapis/astria_vendored/slinky + RPC_REQUEST_STANDARD_NAME: + - proto/protocolapis/astria_vendored/slinky disallow_comment_ignores: true breaking: use: From fb1dbf7d55b421df63b0fbb0ba565ce51462cb74 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 29 Aug 2024 17:47:47 -0400 Subject: [PATCH 43/89] address comments --- Cargo.lock | 1 - charts/sequencer/values.yaml | 2 +- crates/astria-core/src/lib.rs | 1 + crates/astria-core/src/slinky/market_map.rs | 20 ++- crates/astria-core/src/slinky/types.rs | 33 ++-- .../src/genesis_example.rs | 48 ++++-- .../src/genesis_parser.rs | 2 +- crates/astria-sequencer/Cargo.toml | 1 - .../astria-sequencer/app-genesis-state.json | 156 ------------------ .../src/app/vote_extension.rs | 56 ++++++- .../src/slinky/oracle/component.rs | 4 - .../slinky/abci/v1/vote_extensions.proto | 1 + .../slinky/marketmap/v1/genesis.proto | 1 + .../slinky/marketmap/v1/market.proto | 1 + .../slinky/marketmap/v1/params.proto | 1 + .../slinky/marketmap/v1/query.proto | 1 + .../slinky/oracle/v1/genesis.proto | 1 + .../slinky/oracle/v1/query.proto | 1 + .../slinky/service/v1/oracle.proto | 1 + .../slinky/types/v1/currency_pair.proto | 1 + tools/protobuf-compiler/src/main.rs | 2 +- 21 files changed, 144 insertions(+), 191 deletions(-) delete mode 100644 crates/astria-sequencer/app-genesis-state.json diff --git a/Cargo.lock b/Cargo.lock index 13fa19a570..ed2aee1432 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -814,7 +814,6 @@ dependencies = [ "is_sorted", "matchit", "metrics", - "pbjson-types", "penumbra-ibc", "penumbra-proto", "penumbra-tower-trace", diff --git a/charts/sequencer/values.yaml b/charts/sequencer/values.yaml index c8c7bcbf2d..7c4eaf90a3 100644 --- a/charts/sequencer/values.yaml +++ b/charts/sequencer/values.yaml @@ -228,7 +228,7 @@ ports: cometbftRpc: 26657 cometbftMetrics: 26660 sequencerABCI: 26658 - # TODO: change to 9090 if running with oracle sidecar + # note: the oracle sidecar also uses 8080 by default but can be changed with --port sequencerGrpc: 8080 relayerRpc: 2450 sequencerMetrics: 9000 diff --git a/crates/astria-core/src/lib.rs b/crates/astria-core/src/lib.rs index 73a13e7044..144fd3c6da 100644 --- a/crates/astria-core/src/lib.rs +++ b/crates/astria-core/src/lib.rs @@ -1,3 +1,4 @@ +pub use pbjson_types::Timestamp; use prost::Name; #[cfg(not(target_pointer_width = "64"))] diff --git a/crates/astria-core/src/slinky/market_map.rs b/crates/astria-core/src/slinky/market_map.rs index fba4d53090..6cba35599d 100644 --- a/crates/astria-core/src/slinky/market_map.rs +++ b/crates/astria-core/src/slinky/market_map.rs @@ -14,7 +14,11 @@ pub mod v1 { Protobuf, }; - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::GenesisState", into = "raw::GenesisState") + )] #[derive(Debug, Clone, PartialEq, Eq)] pub struct GenesisState { pub market_map: MarketMap, @@ -22,6 +26,20 @@ pub mod v1 { pub params: Params, } + impl TryFrom for GenesisState { + type Error = GenesisStateError; + + fn try_from(raw: raw::GenesisState) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::GenesisState { + fn from(genesis_state: GenesisState) -> Self { + genesis_state.into_raw() + } + } + impl Protobuf for GenesisState { type Error = GenesisStateError; type Raw = raw::GenesisState; diff --git a/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/slinky/types.rs index b539c9b8ee..7053961154 100644 --- a/crates/astria-core/src/slinky/types.rs +++ b/crates/astria-core/src/slinky/types.rs @@ -1,7 +1,11 @@ pub mod v1 { use crate::generated::astria_vendored::slinky::types::v1 as raw; - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(from = "raw::CurrencyPair", into = "raw::CurrencyPair") + )] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CurrencyPair { base: String, @@ -9,14 +13,6 @@ pub mod v1 { } impl CurrencyPair { - #[must_use] - pub fn new(base: String, quote: String) -> Self { - Self { - base, - quote, - } - } - #[must_use] pub fn base(&self) -> &str { &self.base @@ -44,6 +40,18 @@ pub mod v1 { } } + impl From for CurrencyPair { + fn from(raw: raw::CurrencyPair) -> Self { + Self::from_raw(raw) + } + } + + impl From for raw::CurrencyPair { + fn from(currency_pair: CurrencyPair) -> Self { + currency_pair.into_raw() + } + } + impl std::fmt::Display for CurrencyPair { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}/{}", self.base, self.quote) @@ -55,7 +63,12 @@ pub mod v1 { fn from_str(s: &str) -> Result { s.split_once('/') - .map(|(base, quote)| Self::new(base.to_string(), quote.to_string())) + .map(|(base, quote)| { + Self::from_raw(raw::CurrencyPair { + base: base.to_string(), + quote: quote.to_string(), + }) + }) .ok_or_else(|| CurrencyPairParseError::invalid_currency_pair_string(s)) } } diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index f2abf35040..6583b3239a 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -6,7 +6,10 @@ use std::{ }; use astria_core::{ - generated::protocol::genesis::v1alpha1::IbcParameters, + generated::{ + astria_vendored::slinky::types::v1::CurrencyPair as RawCurrencyPair, + protocol::genesis::v1alpha1::IbcParameters, + }, primitive::v1::Address, protocol::genesis::v1alpha1::{ Account, @@ -28,7 +31,6 @@ use astria_core::{ GenesisState as OracleGenesisState, QuotePrice, }, - types::v1::CurrencyPair, }, Protobuf, }; @@ -69,7 +71,11 @@ fn genesis_state_markets() -> HashMap { "BTC/USD".to_string(), Market { ticker: Ticker { - currency_pair: CurrencyPair::new("BTC".to_string(), "USD".to_string()), + currency_pair: RawCurrencyPair { + base: "BTC".to_string(), + quote: "USD".to_string(), + } + .into(), decimals: 8, min_provider_count: 3, enabled: true, @@ -78,7 +84,11 @@ fn genesis_state_markets() -> HashMap { provider_configs: vec![ProviderConfig { name: "coingecko_api".to_string(), off_chain_ticker: "bitcoin/usd".to_string(), - normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), + normalize_by_pair: RawCurrencyPair { + base: "USDT".to_string(), + quote: "USD".to_string(), + } + .into(), invert: false, metadata_json: String::new(), }], @@ -88,7 +98,11 @@ fn genesis_state_markets() -> HashMap { "ETH/USD".to_string(), Market { ticker: Ticker { - currency_pair: CurrencyPair::new("ETH".to_string(), "USD".to_string()), + currency_pair: RawCurrencyPair { + base: "ETH".to_string(), + quote: "USD".to_string(), + } + .into(), decimals: 8, min_provider_count: 3, enabled: true, @@ -97,7 +111,11 @@ fn genesis_state_markets() -> HashMap { provider_configs: vec![ProviderConfig { name: "coingecko_api".to_string(), off_chain_ticker: "ethereum/usd".to_string(), - normalize_by_pair: CurrencyPair::new("USDT".to_string(), "USD".to_string()), + normalize_by_pair: RawCurrencyPair { + base: "USDT".to_string(), + quote: "USD".to_string(), + } + .into(), invert: false, metadata_json: String::new(), }], @@ -185,10 +203,11 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1: nanos: 0, }, }, - currency_pair: CurrencyPair::new( - "BTC".to_string(), - "USD".to_string(), - ), + currency_pair: RawCurrencyPair { + base: "BTC".to_string(), + quote: "USD".to_string(), + } + .into(), }, CurrencyPairGenesis { id: 1, @@ -201,10 +220,11 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1: nanos: 0, }, }, - currency_pair: CurrencyPair::new( - "ETH".to_string(), - "USD".to_string(), - ), + currency_pair: RawCurrencyPair { + base: "ETH".to_string(), + quote: "USD".to_string(), + } + .into(), }, ], next_id: 2, diff --git a/crates/astria-sequencer-utils/src/genesis_parser.rs b/crates/astria-sequencer-utils/src/genesis_parser.rs index 4139474978..f79c18b3bd 100644 --- a/crates/astria-sequencer-utils/src/genesis_parser.rs +++ b/crates/astria-sequencer-utils/src/genesis_parser.rs @@ -83,7 +83,7 @@ fn insert_app_state_and_chain_id(dst: &mut Value, app_state: &Value, chain_id: S dst.get_mut("consensus_params") .expect("consensus_params field exists in cometbft genesis") .as_object_mut() - .expect("consensus_params field is an object") + .expect("consensus_params field is a JSON object") .insert( "abci".to_string(), serde_json::json!({ "vote_extensions_enable_height": "1" }), diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index 6d7c96a126..8283a40736 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -50,7 +50,6 @@ ibc-types = { workspace = true, features = ["with_serde"] } indexmap = { workspace = true } penumbra-ibc = { workspace = true, features = ["component", "rpc"] } metrics = { workspace = true } -pbjson-types = { workspace = true } penumbra-proto = { workspace = true } penumbra-tower-trace = { workspace = true } prost = { workspace = true } diff --git a/crates/astria-sequencer/app-genesis-state.json b/crates/astria-sequencer/app-genesis-state.json deleted file mode 100644 index 3e1100d9f0..0000000000 --- a/crates/astria-sequencer/app-genesis-state.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "address_prefixes": { - "base": "astria" - }, - "accounts": [ - { - "address": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "balance": 1000000000000000000 - }, - { - "address": { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - }, - "balance": 1000000000000000000 - }, - { - "address": { - "bech32m": "astria1vpcfutferpjtwv457r63uwr6hdm8gwr3pxt5ny" - }, - "balance": 1000000000000000000 - } - ], - "authority_sudo_address": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "ibc_sudo_address": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "ibc_relayer_addresses": [ - { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - } - ], - "native_asset_base_denomination": "nria", - "ibc_params": { - "ibcEnabled": true, - "inboundIcs20TransfersEnabled": true, - "outboundIcs20TransfersEnabled": true - }, - "allowed_fee_assets": [ - "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 - }, - "market_map": { - "market_map": { - "markets": { - "ETH/USD": { - "ticker": { - "currency_pair": { - "base": "ETH", - "quote": "USD" - }, - "decimals": 8, - "min_provider_count": 3, - "enabled": true, - "metadata_json": "" - }, - "provider_configs": [ - { - "name": "coingecko_api", - "off_chain_ticker": "ethereum/usd", - "normalize_by_pair": { - "base": "USDT", - "quote": "USD" - }, - "invert": false, - "metadata_json": "" - } - ] - }, - "BTC/USD": { - "ticker": { - "currency_pair": { - "base": "BTC", - "quote": "USD" - }, - "decimals": 8, - "min_provider_count": 3, - "enabled": true, - "metadata_json": "" - }, - "provider_configs": [ - { - "name": "coingecko_api", - "off_chain_ticker": "bitcoin/usd", - "normalize_by_pair": { - "base": "USDT", - "quote": "USD" - }, - "invert": false, - "metadata_json": "" - } - ] - } - } - }, - "last_updated": 0, - "params": { - "market_authorities": [ - { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - } - ], - "admin": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - } - } - }, - "oracle": { - "currency_pair_genesis": [ - { - "currency_pair": { - "base": "BTC", - "quote": "USD" - }, - "currency_pair_price": { - "price": 5834065777, - "block_timestamp": "2024-07-04T19:46:35+00:00", - "block_height": 0 - }, - "id": 0, - "nonce": 0 - }, - { - "currency_pair": { - "base": "ETH", - "quote": "USD" - }, - "currency_pair_price": { - "price": 3138872234, - "block_timestamp": "2024-07-04T19:46:35+00:00", - "block_height": 0 - }, - "id": 1, - "nonce": 0 - } - ], - "next_id": 2 - } -} \ No newline at end of file diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 6268ae768d..c2b5bb2523 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -434,7 +434,7 @@ pub(crate) async fn apply_prices_from_vote_extensions( for (currency_pair, price) in prices { let price = QuotePrice { price, - block_timestamp: pbjson_types::Timestamp { + block_timestamp: astria_core::Timestamp { seconds: timestamp.seconds, nanos: timestamp.nanos, }, @@ -517,7 +517,25 @@ async fn aggregate_oracle_votes( #[cfg(test)] mod test { + use astria_core::{ + crypto::SigningKey, + protocol::transaction::v1alpha1::action::ValidatorUpdate, + }; + use cnidarium::StateDelta; + use tendermint::abci::types::{ + ExtendedVoteInfo, + Validator, + }; + use super::*; + use crate::{ + address::StateWriteExt as _, + authority::{ + StateWriteExt as _, + ValidatorSet, + }, + state_ext::StateWriteExt as _, + }; #[tokio::test] async fn verify_vote_extension_proposal_phase_ok() { @@ -536,4 +554,40 @@ mod test { .await .unwrap(); } + + #[tokio::test] + async fn validate_vote_extensions_insufficient_voting_power() { + let storage = cnidarium::TempStorage::new().await.unwrap(); + let snapshot = storage.latest_snapshot(); + let mut state = StateDelta::new(&snapshot); + state.put_chain_id_and_revision_number("test-0".try_into().unwrap()); + let validator_set = ValidatorSet::new_from_updates(vec![ + ValidatorUpdate { + power: 1u16.into(), + verification_key: SigningKey::from([0; 32]).verification_key(), + }, + ValidatorUpdate { + power: 2u16.into(), + verification_key: SigningKey::from([1; 32]).verification_key(), + }, + ]); + state.put_validator_set(validator_set).unwrap(); + state.put_base_prefix("astria").unwrap(); + + let extended_commit_info = ExtendedCommitInfo { + round: 1u16.into(), + votes: vec![ExtendedVoteInfo { + validator: Validator { + address: SigningKey::from([0; 32]).verification_key().address_bytes(), + power: 1u16.into(), + }, + sig_info: Flag(tendermint::block::BlockIdFlag::Commit), + extension_signature: None, + vote_extension: vec![].into(), + }], + }; + validate_vote_extensions(&state, 1, &extended_commit_info) + .await + .unwrap(); + } } diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index 9c7dc6d242..19c1fa1f8b 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -35,10 +35,6 @@ impl Component for OracleComponent { state .put_currency_pair_state(currency_pair.currency_pair(), currency_pair_state) .context("failed to put currency pair")?; - tracing::info!( - "put currency pair: {}", - currency_pair.currency_pair().to_string() - ); } state diff --git a/proto/protocolapis/astria_vendored/slinky/abci/v1/vote_extensions.proto b/proto/protocolapis/astria_vendored/slinky/abci/v1/vote_extensions.proto index bc3e11abc4..eeb4da3a31 100644 --- a/proto/protocolapis/astria_vendored/slinky/abci/v1/vote_extensions.proto +++ b/proto/protocolapis/astria_vendored/slinky/abci/v1/vote_extensions.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/abci/v1/vote_extensions.proto syntax = "proto3"; package astria_vendored.slinky.abci.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/genesis.proto b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/genesis.proto index 6ef6aaf8b8..befe34e2f4 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/genesis.proto +++ b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/genesis.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/genesis.proto syntax = "proto3"; package astria_vendored.slinky.marketmap.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/market.proto b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/market.proto index e13b885f65..af7b314408 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/market.proto +++ b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/market.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/market.proto syntax = "proto3"; package astria_vendored.slinky.marketmap.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/params.proto b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/params.proto index 100ef18f77..b71a2ae56c 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/params.proto +++ b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/params.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/params.proto syntax = "proto3"; package astria_vendored.slinky.marketmap.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto index ec1ecffc55..26daa6ce58 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto +++ b/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/query.proto syntax = "proto3"; package astria_vendored.slinky.marketmap.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto b/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto index 3a030f629a..bd9b806b8c 100644 --- a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto +++ b/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/oracle/v1/genesis.proto syntax = "proto3"; package astria_vendored.slinky.oracle.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto b/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto index 1cb6baee7e..6a1986d1da 100644 --- a/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto +++ b/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/oracle/v1/query.proto syntax = "proto3"; package astria_vendored.slinky.oracle.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto b/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto index c5ec0658cd..d0e36965c1 100644 --- a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto +++ b/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/service/v1/oracle.proto syntax = "proto3"; package astria_vendored.slinky.service.v1; diff --git a/proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto b/proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto index a323c54c0c..1facb29653 100644 --- a/proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto +++ b/proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto @@ -1,3 +1,4 @@ +// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/types/v1/currency_pair.proto syntax = "proto3"; package astria_vendored.slinky.types.v1; diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index b6fe382a15..996fd0e604 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -63,12 +63,12 @@ fn main() { .build_client(true) .build_server(true) .emit_rerun_if_changed(false) + .btree_map([".astria_vendored.slinky"]) .bytes([ ".astria", ".astria_vendored.slinky", ".celestia", ".cosmos", - ".slinky", ".tendermint", ]) .client_mod_attribute(".", "#[cfg(feature=\"client\")]") From ee820d98b3a08d0e88f4ddfe130e2ee07d060ab1 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 3 Sep 2024 12:30:10 -0400 Subject: [PATCH 44/89] recompile protos --- .../src/generated/astria_vendored.slinky.abci.v1.rs | 4 ++-- .../src/generated/astria_vendored.slinky.marketmap.v1.rs | 7 +++++-- .../src/generated/astria_vendored.slinky.oracle.v1.rs | 4 ++-- .../src/generated/astria_vendored.slinky.service.v1.rs | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.rs b/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.rs index 26baf1d855..404700652f 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.rs @@ -5,8 +5,8 @@ pub struct OracleVoteExtension { /// Prices defines a map of id(CurrencyPair) -> price.Bytes() . i.e. 1 -> /// 0x123.. (bytes). Notice the `id` function is determined by the /// `CurrencyPairIDStrategy` used in the VoteExtensionHandler. - #[prost(map = "uint64, bytes", tag = "1")] - pub prices: ::std::collections::HashMap, + #[prost(btree_map = "uint64, bytes", tag = "1")] + pub prices: ::prost::alloc::collections::BTreeMap, } impl ::prost::Name for OracleVoteExtension { const NAME: &'static str = "OracleVoteExtension"; diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.rs b/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.rs index 86d5b04931..1104ea451b 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.rs @@ -91,8 +91,11 @@ impl ::prost::Name for ProviderConfig { pub struct MarketMap { /// Markets is the full list of tickers and their associated configurations /// to be stored on-chain. - #[prost(map = "string, message", tag = "1")] - pub markets: ::std::collections::HashMap<::prost::alloc::string::String, Market>, + #[prost(btree_map = "string, message", tag = "1")] + pub markets: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + Market, + >, } impl ::prost::Name for MarketMap { const NAME: &'static str = "MarketMap"; diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.rs b/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.rs index 46d974a68e..56a5da8790 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.rs @@ -208,8 +208,8 @@ impl ::prost::Name for GetCurrencyPairMappingRequest { pub struct GetCurrencyPairMappingResponse { /// currency_pair_mapping is a mapping of the id representing the currency pair /// to the currency pair itself. - #[prost(map = "uint64, message", tag = "1")] - pub currency_pair_mapping: ::std::collections::HashMap< + #[prost(btree_map = "uint64, message", tag = "1")] + pub currency_pair_mapping: ::prost::alloc::collections::BTreeMap< u64, super::super::types::v1::CurrencyPair, >, diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.rs b/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.rs index c0cc5822df..4e4477d3ae 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.rs @@ -14,8 +14,8 @@ impl ::prost::Name for QueryPricesRequest { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryPricesResponse { /// prices defines the list of prices. - #[prost(map = "string, string", tag = "1")] - pub prices: ::std::collections::HashMap< + #[prost(btree_map = "string, string", tag = "1")] + pub prices: ::prost::alloc::collections::BTreeMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, From c18201cc52fc51236cc23437210c81ae1d0027de Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 3 Sep 2024 12:44:04 -0400 Subject: [PATCH 45/89] fix compiled protos --- .../src/generated/astria_vendored.slinky.abci.v1.serde.rs | 2 +- .../src/generated/astria_vendored.slinky.marketmap.v1.serde.rs | 2 +- .../src/generated/astria_vendored.slinky.oracle.v1.serde.rs | 2 +- .../src/generated/astria_vendored.slinky.service.v1.serde.rs | 2 +- tools/protobuf-compiler/src/main.rs | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.serde.rs index c7c6252744..d491536570 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.serde.rs @@ -80,7 +80,7 @@ impl<'de> serde::Deserialize<'de> for OracleVoteExtension { return Err(serde::de::Error::duplicate_field("prices")); } prices__ = Some( - map_.next_value::, ::pbjson::private::BytesDeserialize<_>>>()? + map_.next_value::, ::pbjson::private::BytesDeserialize<_>>>()? .into_iter().map(|(k,v)| (k.0, v.0)).collect() ); } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.serde.rs index 9e0004c6c5..a0effe4302 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.serde.rs @@ -483,7 +483,7 @@ impl<'de> serde::Deserialize<'de> for MarketMap { return Err(serde::de::Error::duplicate_field("markets")); } markets__ = Some( - map_.next_value::>()? + map_.next_value::>()? ); } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.serde.rs index 69861e5314..50e9193673 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.serde.rs @@ -707,7 +707,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingResponse { return Err(serde::de::Error::duplicate_field("currencyPairMapping")); } currency_pair_mapping__ = Some( - map_.next_value::, _>>()? + map_.next_value::, _>>()? .into_iter().map(|(k,v)| (k.0, v)).collect() ); } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.serde.rs index cb42a0d63e..188b44bfa2 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.serde.rs @@ -322,7 +322,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { return Err(serde::de::Error::duplicate_field("prices")); } prices__ = Some( - map_.next_value::>()? + map_.next_value::>()? ); } GeneratedField::Timestamp => { diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index 996fd0e604..29314b76fd 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -95,6 +95,7 @@ fn main() { pbjson_build::Builder::new() .register_descriptors(&descriptor_set) .unwrap() + .btree_map([".astria_vendored.slinky"]) .out_dir(&out_dir) .build(&[ ".astria", From 7a1838eee895af65e33f904c81bde887b1c4d12c Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 3 Sep 2024 17:12:50 -0400 Subject: [PATCH 46/89] address comments --- Cargo.lock | 1 + crates/astria-core/Cargo.toml | 1 + .../src/protocol/genesis/v1alpha1.rs | 4 +- crates/astria-core/src/slinky/market_map.rs | 121 ++++++++++++++++-- crates/astria-core/src/slinky/oracle.rs | 83 +++++++++++- crates/astria-core/src/slinky/types.rs | 44 +++++-- crates/astria-sequencer/src/app/mod.rs | 3 +- 7 files changed, 228 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e96d30899..fe6382f297 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,6 +717,7 @@ dependencies = [ "penumbra-proto", "prost", "rand 0.8.5", + "regex", "serde", "sha2 0.10.8", "tempfile", diff --git a/crates/astria-core/Cargo.toml b/crates/astria-core/Cargo.toml index 3c40345d58..42c71d6bd7 100644 --- a/crates/astria-core/Cargo.toml +++ b/crates/astria-core/Cargo.toml @@ -36,6 +36,7 @@ penumbra-ibc = { workspace = true } penumbra-proto = { workspace = true } prost = { workspace = true } rand = { workspace = true } +regex = { workspace = true } serde = { workspace = true, features = ["derive"], optional = true } sha2 = { workspace = true } tendermint = { workspace = true } diff --git a/crates/astria-core/src/protocol/genesis/v1alpha1.rs b/crates/astria-core/src/protocol/genesis/v1alpha1.rs index a954f7f3eb..8b663e2d4f 100644 --- a/crates/astria-core/src/protocol/genesis/v1alpha1.rs +++ b/crates/astria-core/src/protocol/genesis/v1alpha1.rs @@ -810,6 +810,8 @@ fn try_construct_dummy_address_from_prefix(prefix: &str) -> Result<(), AddressEr #[cfg(test)] mod tests { + use indexmap::IndexMap; + use super::*; use crate::{ primitive::v1::Address, @@ -896,7 +898,7 @@ mod tests { SlinkyGenesis { market_map: MarketMapGenesisState { market_map: MarketMap { - markets: std::collections::HashMap::new(), + markets: IndexMap::new(), }, last_updated: 0, params: Params { diff --git a/crates/astria-core/src/slinky/market_map.rs b/crates/astria-core/src/slinky/market_map.rs index 6cba35599d..1abf7fe955 100644 --- a/crates/astria-core/src/slinky/market_map.rs +++ b/crates/astria-core/src/slinky/market_map.rs @@ -1,8 +1,7 @@ pub mod v1 { - use std::{ - collections::HashMap, - str::FromStr, - }; + use std::str::FromStr; + + use indexmap::IndexMap; use crate::{ generated::astria_vendored::slinky::marketmap::v1 as raw, @@ -133,13 +132,31 @@ pub mod v1 { ParamsParseError(#[from] ParamsError), } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::Params", into = "raw::Params") + )] #[derive(Debug, Clone, PartialEq, Eq)] pub struct Params { pub market_authorities: Vec
, pub admin: Address, } + impl TryFrom for Params { + type Error = ParamsError; + + fn try_from(raw: raw::Params) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::Params { + fn from(params: Params) -> Self { + params.into_raw() + } + } + impl Params { /// Converts from a raw protobuf `Params` to a native `Params`. /// @@ -198,13 +215,31 @@ pub mod v1 { AdminParseError(#[source] AddressError), } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::Market", into = "raw::Market") + )] #[derive(Debug, Clone, PartialEq, Eq)] pub struct Market { pub ticker: Ticker, pub provider_configs: Vec, } + impl TryFrom for Market { + type Error = MarketError; + + fn try_from(raw: raw::Market) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::Market { + fn from(market: Market) -> Self { + market.into_raw() + } + } + impl Market { /// Converts from a raw protobuf `Market` to a native `Market`. /// @@ -279,7 +314,11 @@ pub mod v1 { ProviderConfigParseError(#[from] ProviderConfigError), } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::Ticker", into = "raw::Ticker") + )] #[derive(Debug, Clone, PartialEq, Eq)] pub struct Ticker { pub currency_pair: CurrencyPair, @@ -289,6 +328,20 @@ pub mod v1 { pub metadata_json: String, } + impl TryFrom for Ticker { + type Error = TickerError; + + fn try_from(raw: raw::Ticker) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::Ticker { + fn from(ticker: Ticker) -> Self { + ticker.into_raw() + } + } + impl Ticker { /// Converts from a raw protobuf `Ticker` to a native `Ticker`. /// @@ -300,6 +353,7 @@ pub mod v1 { let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { return Err(TickerError::missing_currency_pair()); }; + Ok(Self { currency_pair, decimals: raw.decimals, @@ -338,7 +392,11 @@ pub mod v1 { MissingCurrencyPair, } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::ProviderConfig", into = "raw::ProviderConfig") + )] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProviderConfig { pub name: String, @@ -348,6 +406,20 @@ pub mod v1 { pub metadata_json: String, } + impl TryFrom for ProviderConfig { + type Error = ProviderConfigError; + + fn try_from(raw: raw::ProviderConfig) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::ProviderConfig { + fn from(provider_config: ProviderConfig) -> Self { + provider_config.into_raw() + } + } + impl ProviderConfig { /// Converts from a raw protobuf `ProviderConfig` to a native `ProviderConfig`. /// @@ -396,10 +468,28 @@ pub mod v1 { MissingNormalizeByPair, } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::MarketMap", into = "raw::MarketMap") + )] #[derive(Debug, Clone, PartialEq, Eq)] pub struct MarketMap { - pub markets: HashMap, + pub markets: IndexMap, + } + + impl TryFrom for MarketMap { + type Error = MarketMapError; + + fn try_from(raw: raw::MarketMap) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::MarketMap { + fn from(market_map: MarketMap) -> Self { + market_map.into_raw() + } } impl MarketMap { @@ -410,7 +500,7 @@ pub mod v1 { /// - if any of the markets are invalid /// - if any of the market names are invalid pub fn try_from_raw(raw: raw::MarketMap) -> Result { - let mut markets = HashMap::new(); + let mut markets = IndexMap::new(); for (k, v) in raw.markets { let market = Market::try_from_raw(v) .map_err(|e| MarketMapError::invalid_market(k.clone(), e))?; @@ -441,13 +531,16 @@ pub mod v1 { impl MarketMapError { #[must_use] pub fn invalid_market(name: String, err: MarketError) -> Self { - Self(MarketMapErrorKind::InvalidMarket(name, err)) + Self(MarketMapErrorKind::InvalidMarket { + name, + source: err, + }) } } #[derive(Debug, thiserror::Error)] enum MarketMapErrorKind { - #[error("invalid market {0}")] - InvalidMarket(String, #[source] MarketError), + #[error("invalid market `{name}`")] + InvalidMarket { name: String, source: MarketError }, } } diff --git a/crates/astria-core/src/slinky/oracle.rs b/crates/astria-core/src/slinky/oracle.rs index eb3ae2dd34..2be324c1a7 100644 --- a/crates/astria-core/src/slinky/oracle.rs +++ b/crates/astria-core/src/slinky/oracle.rs @@ -7,7 +7,11 @@ pub mod v1 { Protobuf, }; - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::QuotePrice", into = "raw::QuotePrice") + )] #[derive(Debug, Clone)] pub struct QuotePrice { pub price: u128, @@ -15,6 +19,20 @@ pub mod v1 { pub block_height: u64, } + impl TryFrom for QuotePrice { + type Error = QuotePriceError; + + fn try_from(raw: raw::QuotePrice) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::QuotePrice { + fn from(quote_price: QuotePrice) -> Self { + quote_price.into_raw() + } + } + impl QuotePrice { /// Converts from a raw protobuf `QuotePrice` to a native `QuotePrice`. /// @@ -72,7 +90,11 @@ pub mod v1 { MissingBlockTimestamp, } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::CurrencyPairState", into = "raw::CurrencyPairState") + )] #[derive(Debug, Clone)] pub struct CurrencyPairState { pub price: QuotePrice, @@ -80,6 +102,20 @@ pub mod v1 { pub id: u64, } + impl TryFrom for CurrencyPairState { + type Error = CurrencyPairStateError; + + fn try_from(raw: raw::CurrencyPairState) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::CurrencyPairState { + fn from(currency_pair_state: CurrencyPairState) -> Self { + currency_pair_state.into_raw() + } + } + impl CurrencyPairState { /// Converts from a raw protobuf `CurrencyPairState` to a native `CurrencyPairState`. /// @@ -139,7 +175,14 @@ pub mod v1 { QuotePriceParseError(#[source] QuotePriceError), } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde( + try_from = "raw::CurrencyPairGenesis", + into = "raw::CurrencyPairGenesis" + ) + )] #[derive(Debug, Clone)] pub struct CurrencyPairGenesis { pub currency_pair: CurrencyPair, @@ -148,6 +191,20 @@ pub mod v1 { pub nonce: u64, } + impl TryFrom for CurrencyPairGenesis { + type Error = CurrencyPairGenesisError; + + fn try_from(raw: raw::CurrencyPairGenesis) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::CurrencyPairGenesis { + fn from(currency_pair_genesis: CurrencyPairGenesis) -> Self { + currency_pair_genesis.into_raw() + } + } + impl CurrencyPairGenesis { #[must_use] pub fn currency_pair(&self) -> &CurrencyPair { @@ -244,13 +301,31 @@ pub mod v1 { QuotePriceParseError(#[source] QuotePriceError), } - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(try_from = "raw::GenesisState", into = "raw::GenesisState") + )] #[derive(Debug, Clone)] pub struct GenesisState { pub currency_pair_genesis: Vec, pub next_id: u64, } + impl TryFrom for GenesisState { + type Error = GenesisStateError; + + fn try_from(raw: raw::GenesisState) -> Result { + Self::try_from_raw(raw) + } + } + + impl From for raw::GenesisState { + fn from(genesis_state: GenesisState) -> Self { + genesis_state.into_raw() + } + } + impl Protobuf for GenesisState { type Error = GenesisStateError; type Raw = raw::GenesisState; diff --git a/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/slinky/types.rs index 7053961154..dc62638227 100644 --- a/crates/astria-core/src/slinky/types.rs +++ b/crates/astria-core/src/slinky/types.rs @@ -62,14 +62,23 @@ pub mod v1 { type Err = CurrencyPairParseError; fn from_str(s: &str) -> Result { - s.split_once('/') - .map(|(base, quote)| { - Self::from_raw(raw::CurrencyPair { - base: base.to_string(), - quote: quote.to_string(), - }) - }) - .ok_or_else(|| CurrencyPairParseError::invalid_currency_pair_string(s)) + let re = regex::Regex::new(r"^([a-zA-Z]+)/([a-zA-Z]+)$").expect("valid regex"); + let caps = re + .captures(s) + .ok_or_else(|| CurrencyPairParseError::invalid_currency_pair_string(s))?; + let base = caps + .get(1) + .expect("must have base string, as regex captured it") + .as_str(); + let quote = caps + .get(2) + .expect("must have quote string, as regex captured it") + .as_str(); + + Ok(Self { + base: base.to_string(), + quote: quote.to_string(), + }) } } @@ -92,3 +101,22 @@ pub mod v1 { } } } + +#[cfg(test)] +mod test { + use super::v1::CurrencyPair; + + #[test] + fn currency_pair_parse() { + let currency_pair = "ETH/USD".parse::().unwrap(); + assert_eq!(currency_pair.base(), "ETH"); + assert_eq!(currency_pair.quote(), "USD"); + assert_eq!(currency_pair.to_string(), "ETH/USD"); + } + + #[test] + fn test_currency_pair_parse_invalid() { + let currency_pair = "ETHUSD".parse::(); + assert!(currency_pair.is_err()); + } +} diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 6754bfb239..aaa10c8602 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -906,8 +906,7 @@ impl App { ensure!( finalize_block.txs.len() >= 3, "block must contain at least three transactions: the extended commit info, the rollup \ - transactions commitment and - rollup IDs commitment" + transactions commitment and rollup IDs commitment" ); let extended_commit_info_bytes = &finalize_block.txs[0]; From faa117c6451a973be0d787354bb7de8d06f48858 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 3 Sep 2024 17:43:29 -0400 Subject: [PATCH 47/89] use indexmap everywhere --- .../files/cometbft/config/genesis.json | 2 +- charts/sequencer/templates/configmaps.yaml | 2 +- crates/astria-sequencer-utils/Cargo.toml | 1 + .../src/genesis_example.rs | 6 +- crates/astria-sequencer/local.env.example | 4 +- ...ransaction_with_every_action_snapshot.snap | 60 +++++++++--------- ..._changes__app_finalize_block_snapshot.snap | 62 +++++++++---------- ...reaking_changes__app_genesis_snapshot.snap | 60 +++++++++--------- crates/astria-sequencer/src/app/test_utils.rs | 3 +- 9 files changed, 101 insertions(+), 99 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 6f513eb0f9..a057415c66 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -50,7 +50,7 @@ "params": { "market_authorities": [], "admin": { - "bech32m": {{ include "sequencer.address" .Values.genesis.authoritySudoAddress }} + "bech32m": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} } } }, diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index 9d4a8004e4..a8d1ba3fc1 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,7 +73,7 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" - ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8080" + ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8081" ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" {{- if not .Values.global.dev }} {{- else }} diff --git a/crates/astria-sequencer-utils/Cargo.toml b/crates/astria-sequencer-utils/Cargo.toml index 36a4a2fde1..468c789b87 100644 --- a/crates/astria-sequencer-utils/Cargo.toml +++ b/crates/astria-sequencer-utils/Cargo.toml @@ -20,6 +20,7 @@ colour = "2.1.0" ethers-core = "2.0.14" hex = { workspace = true } indenter = "0.3.3" +indexmap = { workspace = true } itertools = { workspace = true } pbjson-types = { workspace = true } prost = { workspace = true } diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 6583b3239a..951ee7913d 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -1,5 +1,4 @@ use std::{ - collections::HashMap, fs::File, io::Write, path::PathBuf, @@ -38,6 +37,7 @@ use astria_eyre::eyre::{ Result, WrapErr as _, }; +use indexmap::IndexMap; const ASTRIA_ADDRESS_PREFIX: &str = "astria"; @@ -65,8 +65,8 @@ fn charlie() -> Address { .unwrap() } -fn genesis_state_markets() -> HashMap { - let mut markets = HashMap::new(); +fn genesis_state_markets() -> IndexMap { + let mut markets = IndexMap::new(); markets.insert( "BTC/USD".to_string(), Market { diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index 013b0e85aa..e01bbb48f9 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -11,7 +11,7 @@ ASTRIA_SEQUENCER_DB_FILEPATH="/tmp/astria_db" ASTRIA_SEQUENCER_ENABLE_MINT=false # Socket address for gRPC server -ASTRIA_SEQUENCER_GRPC_ADDR="127.0.0.1:9090" +ASTRIA_SEQUENCER_GRPC_ADDR="127.0.0.1:8080" # Log level for the sequencer ASTRIA_SEQUENCER_LOG="astria_sequencer=info" @@ -39,7 +39,7 @@ ASTRIA_SEQUENCER_PRETTY_PRINT=false ASTRIA_SEQUENCER_NO_ORACLE=true # The gRPC endpoint for the oracle sidecar. -ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="http://127.0.0.1:8080" +ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="http://127.0.0.1:8081" # The timeout for the responses from the oracle sidecar in milliseconds. ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS=1000 diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index b8d7653ffa..689eef7112 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 165, - 253, - 48, - 202, + 99, + 71, + 23, + 38, + 116, + 242, + 68, + 164, + 170, 100, + 218, + 71, + 226, + 242, + 182, + 99, + 74, + 189, + 134, + 98, + 89, + 25, + 194, + 62, + 123, 139, - 63, - 207, - 31, - 21, - 175, - 110, - 13, - 135, - 166, - 24, - 232, - 26, - 51, - 82, - 159, - 193, - 219, - 230, - 113, - 167, - 238, - 162, - 110, - 234, - 27, - 167 + 164, + 73, + 146, + 199, + 176, + 198 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap index 621d174e36..613000ccc5 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 126, - 92, - 212, - 84, - 55, - 90, - 111, - 147, - 194, - 235, - 46, - 94, - 102, - 14, - 213, - 177, - 144, - 212, + 143, + 10, + 105, + 95, + 247, + 210, + 181, + 87, + 187, + 107, + 181, + 71, + 93, + 168, + 188, + 53, + 254, + 10, + 23, + 25, + 241, + 157, + 243, + 211, 131, - 63, - 75, - 103, - 213, - 83, - 171, - 146, - 17, - 199, - 236, - 16, - 91, - 230 + 41, + 195, + 30, + 85, + 157, + 224, + 225 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap index 8b32e53780..a146e38c65 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 99, + 19, + 165, + 248, + 113, + 15, + 51, + 76, + 55, + 53, + 199, + 6, + 126, + 201, + 213, + 9, + 117, 169, - 38, - 100, - 158, - 161, - 67, - 125, - 233, - 37, - 70, - 185, - 10, - 116, - 21, - 195, - 214, - 182, + 50, + 209, + 41, 220, - 234, - 82, - 175, - 211, - 238, - 13, - 49, - 232, - 150, - 169, - 6, - 234, - 160 + 172, + 184, + 64, + 188, + 84, + 75, + 152, + 191, + 89, + 231, + 2 ] diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 0218af7a95..d08016b909 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -23,6 +23,7 @@ use astria_core::{ }; use bytes::Bytes; use cnidarium::Storage; +use indexmap::IndexMap; use telemetry::Metrics as _; use crate::{ @@ -138,7 +139,7 @@ pub(crate) fn proto_genesis_state() market_map: Some( MarketMapGenesisState { market_map: MarketMap { - markets: std::collections::HashMap::new(), + markets: IndexMap::new(), }, last_updated: 0, params: Params { From 2928513c8bdd4bc54f72be2007a8308887f3b0a3 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 14:24:36 -0400 Subject: [PATCH 48/89] fix chart genesis --- Cargo.lock | 1 + .../files/cometbft/config/genesis.json | 32 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe6382f297..6d42442b05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -928,6 +928,7 @@ dependencies = [ "ethers-core", "hex", "indenter", + "indexmap 2.4.0", "itertools 0.12.1", "pbjson-types", "predicates", diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index a057415c66..7b17e38f46 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -42,25 +42,27 @@ {{ include "sequencer.address" $value }} {{- end }} ], - "market_map": { + "slinky": { "market_map": { - "markets": {} - }, - "last_updated": 0, - "params": { - "market_authorities": [], - "admin": { - "bech32m": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} + "market_map": { + "markets": {} + }, + "last_updated": 0, + "params": { + "market_authorities": [], + "admin": { + "bech32m": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} + } } + }, + "oracle": { + "currency_pair_genesis": [], + "next_id": 0 } - }, - "oracle": { - "currency_pair_genesis": [], - "next_id": 0 + {{- if not .Values.global.dev }} + {{- else }} + {{- end}} } - {{- if not .Values.global.dev }} - {{- else }} - {{- end}} }, "chain_id": "{{ .Values.genesis.chainId }}", "consensus_params": { From 6170dbb8fa47f13a41c8c827310032e5fff3bf6e Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 14:28:39 -0400 Subject: [PATCH 49/89] fix merge issue --- crates/astria-sequencer/src/app/vote_extension.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 2c136c4225..22f07c4f4d 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -298,7 +298,7 @@ async fn validate_vote_extensions( submitted_voting_power = submitted_voting_power.saturating_add(vote.validator.power.value()); - let verification_key = validator_set + let verification_key = &validator_set .get(vote.validator.address) .context("validator not found")? .verification_key; From c2c7912ae89442f77021723098995cdc6eb824c7 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 15:02:29 -0400 Subject: [PATCH 50/89] maybe fix chart genesis --- charts/sequencer/files/cometbft/config/genesis.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 7b17e38f46..908c9f3651 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -43,21 +43,20 @@ {{- end }} ], "slinky": { - "market_map": { - "market_map": { + "marketMap": { + "marketMap": { "markets": {} }, - "last_updated": 0, "params": { - "market_authorities": [], + "marketAuthorities": [], "admin": { "bech32m": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} } } }, "oracle": { - "currency_pair_genesis": [], - "next_id": 0 + "currencyPairGenesis": [], + "nextId": "0", } {{- if not .Values.global.dev }} {{- else }} From 3faeb747c1c4d1802bfa1e77206e57ddc2d4dc7b Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 15:17:24 -0400 Subject: [PATCH 51/89] maybe fix chart genesis --- charts/sequencer/files/cometbft/config/genesis.json | 8 ++++---- crates/astria-sequencer/src/service/consensus.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 908c9f3651..697482d4d9 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -56,12 +56,12 @@ }, "oracle": { "currencyPairGenesis": [], - "nextId": "0", + "nextId": "0" } - {{- if not .Values.global.dev }} - {{- else }} - {{- end}} } + {{- if not .Values.global.dev }} + {{- else }} + {{- end}} }, "chain_id": "{{ .Values.genesis.chainId }}", "consensus_params": { diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 79651a9ef5..046958d428 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -137,6 +137,7 @@ impl Consensus { bail!("database already initialized"); } + println!("{}", serde_json::to_string_pretty(&init_chain.app_state_bytes).unwrap()); 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 From 17e91aa6c9fbf4aceafb7c3e2d9bcc8d5344a600 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 15:36:19 -0400 Subject: [PATCH 52/89] fix genesis log --- crates/astria-sequencer/src/service/consensus.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index 046958d428..748532e29e 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -137,7 +137,10 @@ impl Consensus { bail!("database already initialized"); } - println!("{}", serde_json::to_string_pretty(&init_chain.app_state_bytes).unwrap()); + println!( + "{}", + String::from_utf8(init_chain.app_state_bytes.to_vec()).unwrap() + ); 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 From 3207ff924b71e6e0a3adf211e840dee73302c205 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 15:48:57 -0400 Subject: [PATCH 53/89] maybe fix chart genesis --- charts/sequencer/files/cometbft/config/genesis.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 697482d4d9..8f3e5cd0d4 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -50,7 +50,7 @@ "params": { "marketAuthorities": [], "admin": { - "bech32m": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} + "bech32m": "{{ .Values.genesis.marketAdminAddress }}" } } }, From 6b1f665982683a679e7378242ec8f8d720346f5d Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 16:08:21 -0400 Subject: [PATCH 54/89] maybe fix charts --- charts/sequencer/files/cometbft/config/genesis.json | 4 +--- dev/values/validators/all.yml | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 8f3e5cd0d4..72f2eb3319 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -49,9 +49,7 @@ }, "params": { "marketAuthorities": [], - "admin": { - "bech32m": "{{ .Values.genesis.marketAdminAddress }}" - } + "admin": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} } }, "oracle": { diff --git a/dev/values/validators/all.yml b/dev/values/validators/all.yml index 0b482a4e62..d62c054191 100644 --- a/dev/values/validators/all.yml +++ b/dev/values/validators/all.yml @@ -7,6 +7,7 @@ genesis: addressPrefixes: base: "astria" authoritySudoAddress: astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm + marketAdminAddress: astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm nativeAssetBaseDenomination: nria allowedFeeAssets: - nria From 0db673a53758555dd7f7ee9402719e5a60e11414 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 4 Sep 2024 17:32:41 -0400 Subject: [PATCH 55/89] maybe fix chart genesis --- charts/sequencer/files/cometbft/config/genesis.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 72f2eb3319..c52ac3a2ee 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -49,7 +49,7 @@ }, "params": { "marketAuthorities": [], - "admin": {{ include "sequencer.address" .Values.genesis.marketAdminAddress }} + "admin": "{{ .Values.genesis.marketAdminAddress }}" } }, "oracle": { From 938df30a10e34e883aeb69da49690329fc3725be Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 5 Sep 2024 16:08:47 -0400 Subject: [PATCH 56/89] fix merge --- .../files/cometbft/config/genesis.json | 3 +- charts/sequencer/values.yaml | 1 + crates/astria-bridge-contracts/src/lib.rs | 3 + crates/astria-bridge-withdrawer/priv.key | 1 + .../helpers/test_bridge_withdrawer.rs | 2 + crates/astria-cli/src/commands/sequencer.rs | 3 +- .../astria.protocol.genesis.v1alpha1.rs | 5 + .../astria.protocol.genesis.v1alpha1.serde.rs | 18 + .../astria.protocol.transactions.v1alpha1.rs | 5 + ...ia.protocol.transactions.v1alpha1.serde.rs | 18 + crates/astria-core/src/primitive/v1/mod.rs | 344 +++++++++++++++--- ...re__primitive__v1__tests__snapshots-2.snap | 5 + ...a1__tests__genesis_state_is_unchanged.snap | 3 +- .../src/protocol/genesis/v1alpha1.rs | 40 +- .../protocol/transaction/v1alpha1/action.rs | 23 +- .../genesis-example.json | 161 ++++++++ .../src/genesis_example.rs | 9 +- .../astria-sequencer/src/address/state_ext.rs | 64 ++-- crates/astria-sequencer/src/app/mod.rs | 7 +- ...ransaction_with_every_action_snapshot.snap | 62 ++-- ..._changes__app_finalize_block_snapshot.snap | 62 ++-- ...reaking_changes__app_genesis_snapshot.snap | 62 ++-- crates/astria-sequencer/src/app/test_utils.rs | 10 +- .../src/app/vote_extension.rs | 57 ++- .../src/authority/state_ext.rs | 2 +- .../src/bridge/bridge_lock_action.rs | 2 +- .../src/bridge/bridge_sudo_change_action.rs | 4 +- .../src/bridge/bridge_unlock_action.rs | 62 +--- crates/astria-sequencer/src/bridge/query.rs | 2 +- .../src/ibc/ics20_transfer.rs | 275 +++++++++++--- .../src/ibc/ics20_withdrawal.rs | 51 ++- crates/astria-sequencer/src/ibc/state_ext.rs | 8 +- .../astria-sequencer/src/service/info/mod.rs | 2 +- crates/astria-sequencer/src/test_utils.rs | 11 + .../src/transaction/checks.rs | 5 +- .../protocol/genesis/v1alpha1/types.proto | 4 + .../transactions/v1alpha1/types.proto | 5 + 37 files changed, 1066 insertions(+), 335 deletions(-) create mode 100644 crates/astria-bridge-withdrawer/priv.key create mode 100644 crates/astria-core/src/primitive/v1/snapshots/astria_core__primitive__v1__tests__snapshots-2.snap create mode 100644 crates/astria-sequencer-utils/genesis-example.json diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index c52ac3a2ee..4d2ff71518 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -23,7 +23,8 @@ "outbound_ics20_transfers_enabled": {{ .Values.genesis.ibc.outboundEnabled }} }, "address_prefixes": { - "base": "{{ .Values.genesis.addressPrefixes.base }}" + "base": "{{ .Values.genesis.addressPrefixes.base }}", + "ibcCompat": "{{ .Values.genesis.addressPrefixes.ibcCompat }}" }, "accounts": [ {{- range $index, $value := .Values.genesis.genesisAccounts }} diff --git a/charts/sequencer/values.yaml b/charts/sequencer/values.yaml index 7c4eaf90a3..abe84a92d3 100644 --- a/charts/sequencer/values.yaml +++ b/charts/sequencer/values.yaml @@ -26,6 +26,7 @@ genesis: genesisTime: "" # '2023-09-22T17:22:35.092832Z' addressPrefixes: base: "astria" + ibcCompat: "astriacompat" authoritySudoAddress: "" marketAdminAddress: "" nativeAssetBaseDenomination: nria diff --git a/crates/astria-bridge-contracts/src/lib.rs b/crates/astria-bridge-contracts/src/lib.rs index 157e809211..79c821167a 100644 --- a/crates/astria-bridge-contracts/src/lib.rs +++ b/crates/astria-bridge-contracts/src/lib.rs @@ -420,6 +420,9 @@ where timeout_time: timeout_in_5_min(), source_channel, bridge_address: Some(self.bridge_address), + // FIXME: this needs a way to determine when to use compat address + // https://github.com/astriaorg/astria/issues/1424 + use_compat_address: false, }; Ok(Action::Ics20Withdrawal(action)) } diff --git a/crates/astria-bridge-withdrawer/priv.key b/crates/astria-bridge-withdrawer/priv.key new file mode 100644 index 0000000000..e0cd2b4245 --- /dev/null +++ b/crates/astria-bridge-withdrawer/priv.key @@ -0,0 +1 @@ +2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90 diff --git a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs index 0847aa77cc..a301a26b69 100644 --- a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs +++ b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/test_bridge_withdrawer.rs @@ -412,6 +412,7 @@ impl From for SubsetOfIcs20Withdrawal { fee_asset, memo, bridge_address, + use_compat_address: _use_compat_address, } = value; Self { amount, @@ -464,6 +465,7 @@ pub fn make_ics20_withdrawal_action(receipt: &TransactionReceipt) -> Action { timeout_time, source_channel: "channel-0".parse().unwrap(), bridge_address: Some(default_bridge_address()), + use_compat_address: false, }; Action::Ics20Withdrawal(inner) diff --git a/crates/astria-cli/src/commands/sequencer.rs b/crates/astria-cli/src/commands/sequencer.rs index 63311c7df5..c7c601ee62 100644 --- a/crates/astria-cli/src/commands/sequencer.rs +++ b/crates/astria-cli/src/commands/sequencer.rs @@ -2,6 +2,7 @@ use astria_core::{ crypto::SigningKey, primitive::v1::{ Address, + Bech32m, ADDRESS_LEN, }, protocol::transaction::v1alpha1::{ @@ -167,7 +168,7 @@ pub(crate) fn make_bech32m(args: &Bech32mAddressArgs) -> eyre::Result<()> { use hex::FromHex as _; let bytes = <[u8; ADDRESS_LEN]>::from_hex(&args.bytes) .wrap_err("failed decoding provided hex bytes")?; - let address = Address::builder() + let address = Address::::builder() .array(bytes) .prefix(&args.prefix) .try_build() diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs index d59f9c9bf5..82c28d6629 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs @@ -55,8 +55,13 @@ impl ::prost::Name for Account { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AddressPrefixes { + /// The base prefix used for most Astria Sequencer addresses. #[prost(string, tag = "1")] pub base: ::prost::alloc::string::String, + /// The prefix used for sending ics20 transfers to IBC chains + /// that enforce a bech32 format of the packet sender. + #[prost(string, tag = "2")] + pub ibc_compat: ::prost::alloc::string::String, } impl ::prost::Name for AddressPrefixes { const NAME: &'static str = "AddressPrefixes"; 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 index f540df9939..62bddd2bd8 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs @@ -117,10 +117,16 @@ impl serde::Serialize for AddressPrefixes { if !self.base.is_empty() { len += 1; } + if !self.ibc_compat.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)?; } + if !self.ibc_compat.is_empty() { + struct_ser.serialize_field("ibcCompat", &self.ibc_compat)?; + } struct_ser.end() } } @@ -132,11 +138,14 @@ impl<'de> serde::Deserialize<'de> for AddressPrefixes { { const FIELDS: &[&str] = &[ "base", + "ibc_compat", + "ibcCompat", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Base, + IbcCompat, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -159,6 +168,7 @@ impl<'de> serde::Deserialize<'de> for AddressPrefixes { { match value { "base" => Ok(GeneratedField::Base), + "ibcCompat" | "ibc_compat" => Ok(GeneratedField::IbcCompat), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -179,6 +189,7 @@ impl<'de> serde::Deserialize<'de> for AddressPrefixes { V: serde::de::MapAccess<'de>, { let mut base__ = None; + let mut ibc_compat__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Base => { @@ -187,10 +198,17 @@ impl<'de> serde::Deserialize<'de> for AddressPrefixes { } base__ = Some(map_.next_value()?); } + GeneratedField::IbcCompat => { + if ibc_compat__.is_some() { + return Err(serde::de::Error::duplicate_field("ibcCompat")); + } + ibc_compat__ = Some(map_.next_value()?); + } } } Ok(AddressPrefixes { base: base__.unwrap_or_default(), + ibc_compat: ibc_compat__.unwrap_or_default(), }) } } diff --git a/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs index 768b6b830a..d9dfd29a80 100644 --- a/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.rs @@ -222,6 +222,11 @@ pub struct Ics20Withdrawal { pub bridge_address: ::core::option::Option< super::super::super::primitive::v1::Address, >, + /// whether to use a bech32-compatible format of the `.return_address` when generating + /// fungible token packets (as opposed to Astria-native bech32m addresses). This is + /// necessary for chains like noble which enforce a strict bech32 format. + #[prost(bool, tag = "11")] + pub use_compat_address: bool, } impl ::prost::Name for Ics20Withdrawal { const NAME: &'static str = "Ics20Withdrawal"; diff --git a/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.serde.rs b/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.serde.rs index f6bad8ea54..5e11f90655 100644 --- a/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.serde.rs +++ b/crates/astria-core/src/generated/astria.protocol.transactions.v1alpha1.serde.rs @@ -1321,6 +1321,9 @@ impl serde::Serialize for Ics20Withdrawal { if self.bridge_address.is_some() { len += 1; } + if self.use_compat_address { + len += 1; + } let mut struct_ser = serializer.serialize_struct("astria.protocol.transactions.v1alpha1.Ics20Withdrawal", len)?; if let Some(v) = self.amount.as_ref() { struct_ser.serialize_field("amount", v)?; @@ -1353,6 +1356,9 @@ impl serde::Serialize for Ics20Withdrawal { if let Some(v) = self.bridge_address.as_ref() { struct_ser.serialize_field("bridgeAddress", v)?; } + if self.use_compat_address { + struct_ser.serialize_field("useCompatAddress", &self.use_compat_address)?; + } struct_ser.end() } } @@ -1380,6 +1386,8 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { "memo", "bridge_address", "bridgeAddress", + "use_compat_address", + "useCompatAddress", ]; #[allow(clippy::enum_variant_names)] @@ -1394,6 +1402,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { FeeAsset, Memo, BridgeAddress, + UseCompatAddress, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1425,6 +1434,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { "feeAsset" | "fee_asset" => Ok(GeneratedField::FeeAsset), "memo" => Ok(GeneratedField::Memo), "bridgeAddress" | "bridge_address" => Ok(GeneratedField::BridgeAddress), + "useCompatAddress" | "use_compat_address" => Ok(GeneratedField::UseCompatAddress), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1454,6 +1464,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { let mut fee_asset__ = None; let mut memo__ = None; let mut bridge_address__ = None; + let mut use_compat_address__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Amount => { @@ -1518,6 +1529,12 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { } bridge_address__ = map_.next_value()?; } + GeneratedField::UseCompatAddress => { + if use_compat_address__.is_some() { + return Err(serde::de::Error::duplicate_field("useCompatAddress")); + } + use_compat_address__ = Some(map_.next_value()?); + } } } Ok(Ics20Withdrawal { @@ -1531,6 +1548,7 @@ impl<'de> serde::Deserialize<'de> for Ics20Withdrawal { fee_asset: fee_asset__.unwrap_or_default(), memo: memo__.unwrap_or_default(), bridge_address: bridge_address__, + use_compat_address: use_compat_address__.unwrap_or_default(), }) } } diff --git a/crates/astria-core/src/primitive/v1/mod.rs b/crates/astria-core/src/primitive/v1/mod.rs index bf88007ec9..e9e5ce2d55 100644 --- a/crates/astria-core/src/primitive/v1/mod.rs +++ b/crates/astria-core/src/primitive/v1/mod.rs @@ -1,7 +1,10 @@ pub mod asset; pub mod u128; -use std::str::FromStr; +use std::{ + marker::PhantomData, + str::FromStr, +}; use base64::{ display::Base64Display, @@ -241,8 +244,8 @@ pub struct IncorrectRollupIdLength { pub struct AddressError(AddressErrorKind); impl AddressError { - fn bech32m_decode(source: bech32::DecodeError) -> Self { - Self(AddressErrorKind::Bech32mDecode { + fn decode(source: bech32::primitives::decode::CheckedHrpstringError) -> Self { + Self(AddressErrorKind::Decode { source, }) } @@ -262,8 +265,10 @@ impl AddressError { #[derive(Debug, thiserror::Error, PartialEq)] enum AddressErrorKind { - #[error("failed decoding provided bech32m string")] - Bech32mDecode { source: bech32::DecodeError }, + #[error("failed decoding provided string")] + Decode { + source: bech32::primitives::decode::CheckedHrpstringError, + }, #[error("expected an address of 20 bytes, got `{received}`")] IncorrectAddressLength { received: usize }, #[error("the provided prefix was not a valid bech32 human readable prefix")] @@ -274,33 +279,50 @@ enum AddressErrorKind { pub struct NoBytes; pub struct NoPrefix; -pub struct WithBytes<'a>(BytesInner<'a>); -enum BytesInner<'a> { +pub struct WithBytes<'a, I>(WithBytesInner<'a, I>); +enum WithBytesInner<'a, I> { Array([u8; ADDRESS_LEN]), + Iter(I), Slice(std::borrow::Cow<'a, [u8]>), } pub struct WithPrefix<'a>(std::borrow::Cow<'a, str>); -pub struct AddressBuilder { +pub struct NoBytesIter; + +impl Iterator for NoBytesIter { + type Item = u8; + + fn next(&mut self) -> Option { + None + } +} + +pub struct AddressBuilder { bytes: TBytes, prefix: TPrefix, + format: PhantomData, } -impl AddressBuilder { +impl AddressBuilder { const fn new() -> Self { Self { bytes: NoBytes, prefix: NoPrefix, + format: PhantomData, } } } -impl AddressBuilder { +impl AddressBuilder { #[must_use = "the builder must be built to construct an address to be useful"] - pub fn array(self, array: [u8; ADDRESS_LEN]) -> AddressBuilder, TPrefix> { + pub fn array( + self, + array: [u8; ADDRESS_LEN], + ) -> AddressBuilder, TPrefix> { AddressBuilder { - bytes: WithBytes(BytesInner::Array(array)), + bytes: WithBytes(WithBytesInner::Array(array)), prefix: self.prefix, + format: self.format, } } @@ -308,10 +330,23 @@ impl AddressBuilder { pub fn slice<'a, T: Into>>( self, bytes: T, - ) -> AddressBuilder, TPrefix> { + ) -> AddressBuilder, TPrefix> { + AddressBuilder { + bytes: WithBytes(WithBytesInner::Slice(bytes.into())), + prefix: self.prefix, + format: self.format, + } + } + + #[must_use = "the builder must be built to construct an address to be useful"] + pub fn with_iter>( + self, + iter: T, + ) -> AddressBuilder, TPrefix> { AddressBuilder { - bytes: WithBytes(BytesInner::Slice(bytes.into())), + bytes: WithBytes(WithBytesInner::Iter(iter)), prefix: self.prefix, + format: self.format, } } @@ -324,7 +359,7 @@ impl AddressBuilder { pub fn verification_key( self, key: &crate::crypto::VerificationKey, - ) -> AddressBuilder, TPrefix> { + ) -> AddressBuilder, TPrefix> { let hash = Sha256::digest(key.as_bytes()); let array: [u8; ADDRESS_LEN] = hash[0..ADDRESS_LEN] .try_into() @@ -336,15 +371,19 @@ impl AddressBuilder { pub fn prefix<'a, T: Into>>( self, prefix: T, - ) -> AddressBuilder> { + ) -> AddressBuilder> { AddressBuilder { bytes: self.bytes, prefix: WithPrefix(prefix.into()), + format: self.format, } } } -impl<'a, 'b> AddressBuilder, WithPrefix<'b>> { +impl<'a, 'b, TFormat, TBytesIter> AddressBuilder, WithPrefix<'b>> +where + TBytesIter: IntoIterator, +{ /// Attempts to build an address from the configured prefix and bytes. /// /// # Errors @@ -352,39 +391,132 @@ impl<'a, 'b> AddressBuilder, WithPrefix<'b>> { /// + if the prefix shorter than 1 or longer than 83 characters, or contains characters outside /// 33-126 of ASCII characters. /// + if the provided bytes are not exactly 20 bytes. - pub fn try_build(self) -> Result { + pub fn try_build(self) -> Result, AddressError> { let Self { bytes: WithBytes(bytes), prefix: WithPrefix(prefix), + format, } = self; let bytes = match bytes { - BytesInner::Array(bytes) => bytes, - BytesInner::Slice(bytes) => <[u8; ADDRESS_LEN]>::try_from(bytes.as_ref()) + WithBytesInner::Array(bytes) => bytes, + WithBytesInner::Iter(bytes) => try_collect_to_array(bytes)?, + WithBytesInner::Slice(bytes) => <[u8; ADDRESS_LEN]>::try_from(bytes.as_ref()) .map_err(|_| AddressError::incorrect_address_length(bytes.len()))?, }; let prefix = bech32::Hrp::parse(&prefix).map_err(AddressError::invalid_prefix)?; Ok(Address { bytes, prefix, + format, }) } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr( - feature = "serde", - serde(into = "raw::Address", try_from = "raw::Address") -)] -pub struct Address { +fn try_collect_to_array>( + iter: I, +) -> Result<[u8; ADDRESS_LEN], AddressError> { + let mut arr = [0; ADDRESS_LEN]; + let mut iter = iter.into_iter(); + let mut i = 0; + loop { + if i >= ADDRESS_LEN { + break; + } + let Some(byte) = iter.next() else { + break; + }; + arr[i] = byte; + i = i.saturating_add(1); + } + let items_in_iterator = i.saturating_add(iter.count()); + if items_in_iterator != ADDRESS_LEN { + return Err(AddressError::incorrect_address_length(items_in_iterator)); + } + Ok(arr) +} + +#[derive(Clone, Copy, Debug)] +pub enum Bech32m {} +#[derive(Clone, Copy, Debug)] +pub enum Bech32 {} +#[derive(Clone, Copy, Debug)] +pub enum NoFormat {} + +pub trait Format: private::Sealed { + type Checksum: bech32::Checksum; +} + +impl Format for Bech32m { + type Checksum = bech32::Bech32m; +} + +impl Format for Bech32 { + type Checksum = bech32::Bech32; +} + +impl Format for NoFormat { + type Checksum = bech32::NoChecksum; +} + +mod private { + pub trait Sealed {} + impl Sealed for super::Bech32m {} + impl Sealed for super::Bech32 {} + impl Sealed for super::NoFormat {} +} + +#[derive(Debug, Hash)] +pub struct Address { bytes: [u8; ADDRESS_LEN], prefix: bech32::Hrp, + format: PhantomData, +} + +// The serde impls need to be manually implemented for Address because they +// only work for Address which cannot be expressed using serde +// attributes. +#[cfg(feature = "serde")] +mod _serde_impls { + use serde::de::Error as _; + impl serde::Serialize for super::Address { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.to_raw().serialize(serializer) + } + } + impl<'de> serde::Deserialize<'de> for super::Address { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + super::raw::Address::deserialize(deserializer) + .and_then(|raw| raw.try_into().map_err(D::Error::custom)) + } + } +} + +impl Clone for Address { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Address {} + +impl PartialEq for Address { + fn eq(&self, other: &Self) -> bool { + self.bytes.eq(&other.bytes) && self.prefix.eq(&other.prefix) + } } -impl Address { +impl Eq for Address {} + +impl Address { #[must_use = "the builder must be used to construct an address to be useful"] - pub fn builder() -> AddressBuilder { - AddressBuilder::new() + pub fn builder() -> AddressBuilder { + AddressBuilder::::new() } #[must_use] @@ -397,13 +529,42 @@ impl Address { self.prefix.as_str() } + /// Converts to a new address with the given `prefix`. + /// + /// # Errors + /// Returns an error if an address with `prefix` cannot be constructed. + /// The error conditions for this are the same as for [`AddressBuilder::try_build`]. + pub fn to_prefix(&self, prefix: &str) -> Result { + Self::builder() + .array(self.bytes()) + .prefix(prefix) + .try_build() + } + + /// Converts to a new address with the type argument `OtherFormat`. + /// + /// `OtherFormat` is usually [`Bech32`] or [`Bech32m`]. + #[must_use] + pub fn to_format(&self) -> Address { + Address { + bytes: self.bytes, + prefix: self.prefix, + format: PhantomData, + } + } +} + +impl Address { /// Convert [`Address`] to a [`raw::Address`]. // allow: panics are checked to not happen #[allow(clippy::missing_panics_doc)] #[must_use] pub fn to_raw(&self) -> raw::Address { - let bech32m = bech32::encode_lower::(self.prefix, &self.bytes()) - .expect("should not fail because len(prefix) + len(bytes) <= 63 < BECH32M::CODELENGTH"); + let bech32m = + bech32::encode_lower::<::Checksum>(self.prefix, &self.bytes()) + .expect( + "should not fail because len(prefix) + len(bytes) <= 63 < BECH32M::CODELENGTH", + ); // allow: the field is deprecated, but we must still fill it in #[allow(deprecated)] raw::Address { @@ -429,25 +590,27 @@ impl Address { } } -impl From
for raw::Address { - fn from(value: Address) -> Self { +impl From> for raw::Address { + fn from(value: Address) -> Self { value.into_raw() } } -impl FromStr for Address { +impl FromStr for Address { type Err = AddressError; fn from_str(s: &str) -> Result { - let (hrp, bytes) = bech32::decode(s).map_err(AddressError::bech32m_decode)?; + let checked = bech32::primitives::decode::CheckedHrpstring::new::(s) + .map_err(Self::Err::decode)?; + let hrp = checked.hrp(); Self::builder() - .slice(bytes) + .with_iter(checked.byte_iter()) .prefix(hrp.as_str()) .try_build() } } -impl TryFrom for Address { +impl TryFrom for Address { type Error = AddressError; fn try_from(value: raw::Address) -> Result { @@ -455,20 +618,30 @@ impl TryFrom for Address { } } -impl std::fmt::Display for Address { +impl std::fmt::Display for Address { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use bech32::EncodeError; - match bech32::encode_lower_to_fmt::(f, self.prefix, &self.bytes()) { + match bech32::encode_lower_to_fmt::(f, self.prefix, &self.bytes()) { Ok(()) => Ok(()), Err(EncodeError::Fmt(err)) => Err(err), Err(err) => panic!( "only formatting errors are valid when encoding astria addresses; all other error \ - variants (only TooLong at of bech32-0.11.0) are guaranteed to not \ - happen:\n{err:?}", + variants (only TooLong as of bech32-0.11.0) are guaranteed to not happen because \ + `Address` is length checked:\n{err:?}", ), } } } +/// Constructs a dummy address from a given `prefix`, otherwise fail. +pub(crate) fn try_construct_dummy_address_from_prefix( + prefix: &str, +) -> Result<(), AddressError> { + Address::::builder() + .array([0u8; ADDRESS_LEN]) + .prefix(prefix) + .try_build() + .map(|_| ()) +} /// Derive a [`merkle::Tree`] from an iterable. /// @@ -494,13 +667,16 @@ mod tests { Address, AddressError, AddressErrorKind, + Bech32m, ADDRESS_LEN, }; + use crate::primitive::v1::Bech32; const ASTRIA_ADDRESS_PREFIX: &str = "astria"; + const ASTRIA_COMPAT_ADDRESS_PREFIX: &str = "astriacompat"; #[track_caller] fn assert_wrong_address_bytes(bad_account: &[u8]) { - let error = Address::builder() + let error = Address::::builder() .slice(bad_account) .prefix(ASTRIA_ADDRESS_PREFIX) .try_build() @@ -528,12 +704,90 @@ mod tests { #[cfg(feature = "serde")] #[test] fn snapshots() { - let address = Address::builder() + use crate::primitive::v1::Bech32; + + let main_address = Address::builder() .array([42; 20]) .prefix(ASTRIA_ADDRESS_PREFIX) .try_build() .unwrap(); - insta::assert_json_snapshot!(address); + insta::assert_json_snapshot!(&main_address); + + let compat_address = main_address + .to_prefix(ASTRIA_COMPAT_ADDRESS_PREFIX) + .unwrap() + .to_format::(); + // We don't allow serializing non bech32m addresses due to + // its impl via the protobuf type. + insta::assert_snapshot!(&compat_address); + } + + #[test] + fn parse_bech32m_address() { + let expected = Address::builder() + .array([42; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let actual = expected.to_string().parse::
().unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn parse_bech32_address() { + let expected = Address::::builder() + .array([42; 20]) + .prefix(ASTRIA_COMPAT_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let actual = expected.to_string().parse::>().unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn parsing_bech32_address_as_bech32m_fails() { + let expected = Address::::builder() + .array([42; 20]) + .prefix(ASTRIA_COMPAT_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let err = expected + .to_string() + .parse::>() + .expect_err("this must not work"); + match err { + AddressError(AddressErrorKind::Decode { + .. + }) => {} + other => { + panic!( + "expected AddressError(AddressErrorKind::Decode {{ .. }}), but got {other:?}" + ) + } + } + } + + #[test] + fn parsing_bech32m_address_as_bech32_fails() { + let expected = Address::::builder() + .array([42; 20]) + .prefix(ASTRIA_ADDRESS_PREFIX) + .try_build() + .unwrap(); + let err = expected + .to_string() + .parse::>() + .expect_err("this must not work"); + match err { + AddressError(AddressErrorKind::Decode { + .. + }) => {} + other => { + panic!( + "expected AddressError(AddressErrorKind::Decode {{ .. }}), but got {other:?}" + ) + } + } } #[test] diff --git a/crates/astria-core/src/primitive/v1/snapshots/astria_core__primitive__v1__tests__snapshots-2.snap b/crates/astria-core/src/primitive/v1/snapshots/astria_core__primitive__v1__tests__snapshots-2.snap new file mode 100644 index 0000000000..afa9f15103 --- /dev/null +++ b/crates/astria-core/src/primitive/v1/snapshots/astria_core__primitive__v1__tests__snapshots-2.snap @@ -0,0 +1,5 @@ +--- +source: crates/astria-core/src/primitive/v1/mod.rs +expression: "&bech32_address" +--- +astriacompat19g4z52329g4z52329g4z52329g4z52322jspvr diff --git a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 index 5c0d1b9588..d62999a024 100644 --- a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 @@ -5,7 +5,8 @@ expression: genesis_state() { "chainId": "astria-1", "addressPrefixes": { - "base": "astria" + "base": "astria", + "ibcCompat": "astriacompat" }, "accounts": [ { diff --git a/crates/astria-core/src/protocol/genesis/v1alpha1.rs b/crates/astria-core/src/protocol/genesis/v1alpha1.rs index 8b663e2d4f..d9b7354370 100644 --- a/crates/astria-core/src/protocol/genesis/v1alpha1.rs +++ b/crates/astria-core/src/protocol/genesis/v1alpha1.rs @@ -10,9 +10,11 @@ use crate::{ denom::ParseTracePrefixedError, ParseDenomError, }, + try_construct_dummy_address_from_prefix, Address, AddressError, - ADDRESS_LEN, + Bech32, + Bech32m, }, slinky::{ market_map::v1::{ @@ -610,9 +612,26 @@ enum AccountErrorKind { FieldNotSet { name: &'static str }, } +/// The address prefixes used by the Sequencer. +/// +/// All prefixes are guaranteed to be between 1 and 83 bech32 human readable +/// characters in the ASCII range `[33, 126]`. #[derive(Clone, Debug)] pub struct AddressPrefixes { - pub base: String, + base: String, + ibc_compat: String, +} + +impl AddressPrefixes { + #[must_use] + pub fn base(&self) -> &str { + &self.base + } + + #[must_use] + pub fn ibc_compat(&self) -> &str { + &self.ibc_compat + } } impl Protobuf for AddressPrefixes { @@ -622,19 +641,24 @@ impl Protobuf for AddressPrefixes { fn try_from_raw_ref(raw: &Self::Raw) -> Result { let Self::Raw { base, + ibc_compat, } = raw; - try_construct_dummy_address_from_prefix(base).map_err(Self::Error::base)?; + try_construct_dummy_address_from_prefix::(base).map_err(Self::Error::base)?; + try_construct_dummy_address_from_prefix::(ibc_compat).map_err(Self::Error::base)?; Ok(Self { base: base.to_string(), + ibc_compat: ibc_compat.to_string(), }) } fn to_raw(&self) -> Self::Raw { let Self { base, + ibc_compat, } = self; Self::Raw { base: base.clone(), + ibc_compat: ibc_compat.clone(), } } } @@ -799,15 +823,6 @@ enum FeesErrorKind { 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 indexmap::IndexMap; @@ -873,6 +888,7 @@ mod tests { ], address_prefixes: Some(raw::AddressPrefixes { base: "astria".into(), + ibc_compat: "astriacompat".into(), }), authority_sudo_address: Some(alice().to_raw()), chain_id: "astria-1".to_string(), diff --git a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs index fe23ffd23a..91c747a2f5 100644 --- a/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs +++ b/crates/astria-core/src/protocol/transaction/v1alpha1/action.rs @@ -7,7 +7,6 @@ use ibc_types::{ IdentifierError, }; use penumbra_ibc::IbcRelay; -use penumbra_proto::penumbra::core::component::ibc::v1::FungibleTokenPacketData; use super::raw; use crate::{ @@ -787,6 +786,11 @@ pub struct Ics20Withdrawal { // if unset, and the transaction sender is a bridge account, the withdrawal is // treated as a bridge withdrawal (ie. the bridge account's withdrawer address is checked). pub bridge_address: Option
, + + // whether to use a bech32-compatible format of the `.return_address` when generating + // fungible token packets (as opposed to Astria-native bech32m addresses). This is + // necessary for chains like noble which enforce a strict bech32 format. + pub use_compat_address: bool, } impl Ics20Withdrawal { @@ -834,17 +838,6 @@ impl Ics20Withdrawal { pub fn memo(&self) -> &str { &self.memo } - - #[must_use] - pub fn to_fungible_token_packet_data(&self) -> FungibleTokenPacketData { - FungibleTokenPacketData { - amount: self.amount.to_string(), - denom: self.denom.to_string(), - sender: self.return_address.to_string(), - receiver: self.destination_chain_address.clone(), - memo: self.memo.clone(), - } - } } impl Protobuf for Ics20Withdrawal { @@ -864,6 +857,7 @@ impl Protobuf for Ics20Withdrawal { fee_asset: self.fee_asset.to_string(), memo: self.memo.clone(), bridge_address: self.bridge_address.as_ref().map(Address::to_raw), + use_compat_address: self.use_compat_address, } } @@ -880,6 +874,7 @@ impl Protobuf for Ics20Withdrawal { fee_asset: self.fee_asset.to_string(), memo: self.memo, bridge_address: self.bridge_address.map(Address::into_raw), + use_compat_address: self.use_compat_address, } } @@ -904,6 +899,7 @@ impl Protobuf for Ics20Withdrawal { fee_asset, memo, bridge_address, + use_compat_address, } = proto; let amount = amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?; let return_address = Address::try_from_raw( @@ -935,6 +931,7 @@ impl Protobuf for Ics20Withdrawal { .map_err(Ics20WithdrawalError::invalid_fee_asset)?, memo, bridge_address, + use_compat_address, }) } @@ -959,6 +956,7 @@ impl Protobuf for Ics20Withdrawal { fee_asset, memo, bridge_address, + use_compat_address, } = proto; let amount = amount.ok_or(Ics20WithdrawalError::field_not_set("amount"))?; let return_address = Address::try_from_raw( @@ -993,6 +991,7 @@ impl Protobuf for Ics20Withdrawal { .map_err(Ics20WithdrawalError::invalid_fee_asset)?, memo: memo.clone(), bridge_address, + use_compat_address: *use_compat_address, }) } } diff --git a/crates/astria-sequencer-utils/genesis-example.json b/crates/astria-sequencer-utils/genesis-example.json new file mode 100644 index 0000000000..959c689720 --- /dev/null +++ b/crates/astria-sequencer-utils/genesis-example.json @@ -0,0 +1,161 @@ +{ + "chainId": "test-1", + "addressPrefixes": { + "base": "astria" + }, + "accounts": [ + { + "address": { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + "balance": { + "lo": "1000000000000000000" + } + }, + { + "address": { + "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" + }, + "balance": { + "lo": "1000000000000000000" + } + }, + { + "address": { + "bech32m": "astria1vpcfutferpjtwv457r63uwr6hdm8gwr3pxt5ny" + }, + "balance": { + "lo": "1000000000000000000" + } + } + ], + "authoritySudoAddress": { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + "ibcSudoAddress": { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + "ibcRelayerAddresses": [ + { + "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + }, + { + "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" + } + ], + "nativeAssetBaseDenomination": "nria", + "ibcParameters": { + "ibcEnabled": true, + "inboundIcs20TransfersEnabled": true, + "outboundIcs20TransfersEnabled": true + }, + "allowedFeeAssets": [ + "nria" + ], + "fees": { + "transferBaseFee": { + "lo": "12" + }, + "sequenceBaseFee": { + "lo": "32" + }, + "sequenceByteCostMultiplier": { + "lo": "1" + }, + "initBridgeAccountBaseFee": { + "lo": "48" + }, + "bridgeLockByteCostMultiplier": { + "lo": "1" + }, + "bridgeSudoChangeFee": { + "lo": "24" + }, + "ics20WithdrawalBaseFee": { + "lo": "24" + } + }, + "slinky": { + "marketMap": { + "marketMap": { + "markets": { + "BTC/USD": { + "ticker": { + "currencyPair": { + "Base": "BTC", + "Quote": "USD" + }, + "decimals": "8", + "minProviderCount": "3", + "enabled": true + }, + "providerConfigs": [ + { + "name": "coingecko_api", + "offChainTicker": "bitcoin/usd", + "normalizeByPair": { + "Base": "USDT", + "Quote": "USD" + } + } + ] + }, + "ETH/USD": { + "ticker": { + "currencyPair": { + "Base": "ETH", + "Quote": "USD" + }, + "decimals": "8", + "minProviderCount": "3", + "enabled": true + }, + "providerConfigs": [ + { + "name": "coingecko_api", + "offChainTicker": "ethereum/usd", + "normalizeByPair": { + "Base": "USDT", + "Quote": "USD" + } + } + ] + } + } + }, + "params": { + "marketAuthorities": [ + "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm", + "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" + ], + "admin": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" + } + }, + "oracle": { + "currencyPairGenesis": [ + { + "currencyPair": { + "Base": "BTC", + "Quote": "USD" + }, + "currencyPairPrice": { + "price": "5834065777", + "blockTimestamp": "2024-07-04T19:46:35+00:00" + } + }, + { + "currencyPair": { + "Base": "ETH", + "Quote": "USD" + }, + "currencyPairPrice": { + "price": "3138872234", + "blockTimestamp": "2024-07-04T19:46:35+00:00" + }, + "id": "1" + } + ], + "nextId": "2" + } + } +} \ No newline at end of file diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 951ee7913d..c84b94c33b 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -7,12 +7,14 @@ use std::{ use astria_core::{ generated::{ astria_vendored::slinky::types::v1::CurrencyPair as RawCurrencyPair, - protocol::genesis::v1alpha1::IbcParameters, + protocol::genesis::v1alpha1::{ + AddressPrefixes, + IbcParameters, + }, }, primitive::v1::Address, protocol::genesis::v1alpha1::{ Account, - AddressPrefixes, Fees, GenesisAppState, }, @@ -144,13 +146,14 @@ fn accounts() -> Vec { fn address_prefixes() -> AddressPrefixes { AddressPrefixes { base: "astria".into(), + ibc_compat: "astriacompat".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()), + address_prefixes: Some(address_prefixes()), authority_sudo_address: Some(alice().to_raw()), chain_id: "test-1".into(), ibc_sudo_address: Some(alice().to_raw()), diff --git a/crates/astria-sequencer/src/address/state_ext.rs b/crates/astria-sequencer/src/address/state_ext.rs index 924479107a..3016dc36f1 100644 --- a/crates/astria-sequencer/src/address/state_ext.rs +++ b/crates/astria-sequencer/src/address/state_ext.rs @@ -4,7 +4,10 @@ use anyhow::{ Context as _, Result, }; -use astria_core::primitive::v1::Address; +use astria_core::primitive::v1::{ + Address, + Bech32m, +}; use async_trait::async_trait; use cnidarium::{ StateRead, @@ -16,9 +19,13 @@ fn base_prefix_key() -> &'static str { "prefixes/base" } +fn ibc_compat_prefix_key() -> &'static str { + "prefixes/ibc-compat" +} + #[async_trait] pub(crate) trait StateReadExt: StateRead { - async fn ensure_base_prefix(&self, address: &Address) -> anyhow::Result<()> { + async fn ensure_base_prefix(&self, address: &Address) -> anyhow::Result<()> { let prefix = self .get_base_prefix() .await @@ -48,9 +55,21 @@ pub(crate) trait StateReadExt: StateRead { let Some(bytes) = self .get_raw(base_prefix_key()) .await - .context("failed reading address base prefix")? + .context("failed reading address base prefix from state")? else { - bail!("no base prefix found"); + bail!("no base prefix found in state"); + }; + String::from_utf8(bytes).context("prefix retrieved from storage is not valid utf8") + } + + #[instrument(skip_all)] + async fn get_ibc_compat_prefix(&self) -> Result { + let Some(bytes) = self + .get_raw(ibc_compat_prefix_key()) + .await + .context("failed reading address ibc compat prefix from state")? + else { + bail!("no ibc compat prefix found in state") }; String::from_utf8(bytes).context("prefix retrieved from storage is not valid utf8") } @@ -61,28 +80,18 @@ impl StateReadExt for T {} #[async_trait] pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] - fn put_base_prefix(&mut self, prefix: &str) -> anyhow::Result<()> { - try_construct_dummy_address_from_prefix(prefix) - .context("failed constructing a dummy address from the provided prefix")?; + fn put_base_prefix(&mut self, prefix: &str) { self.put_raw(base_prefix_key().into(), prefix.into()); - Ok(()) + } + + #[instrument(skip_all)] + fn put_ibc_compat_prefix(&mut self, prefix: &str) { + self.put_raw(ibc_compat_prefix_key().into(), prefix.into()); } } impl StateWriteExt for T {} -fn try_construct_dummy_address_from_prefix( - s: &str, -) -> Result<(), astria_core::primitive::v1::AddressError> { - use astria_core::primitive::v1::ADDRESS_LEN; - // construct a dummy address to see if we can construct it; fail otherwise. - Address::builder() - .array([0u8; ADDRESS_LEN]) - .prefix(s) - .try_build() - .map(|_| ()) -} - #[cfg(test)] mod test { use cnidarium::StateDelta; @@ -98,7 +107,20 @@ mod test { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix("astria").unwrap(); + state.put_base_prefix("astria"); assert_eq!("astria", &state.get_base_prefix().await.unwrap()); } + + #[tokio::test] + async fn put_and_get_ibc_compat_prefix() { + let storage = cnidarium::TempStorage::new().await.unwrap(); + let snapshot = storage.latest_snapshot(); + let mut state = StateDelta::new(snapshot); + + state.put_ibc_compat_prefix("astriacompat"); + assert_eq!( + "astriacompat", + &state.get_ibc_compat_prefix().await.unwrap() + ); + } } diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index aaa10c8602..f22d9fa476 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -78,7 +78,7 @@ use crate::{ StateReadExt, StateWriteExt as _, }, - address::StateWriteExt as _, + address::StateWriteExt, api_state_ext::StateWriteExt as _, app::vote_extension::ProposalHandler, assets::{ @@ -233,9 +233,8 @@ impl App { .try_begin_transaction() .expect("state Arc should not be referenced elsewhere"); - state_tx - .put_base_prefix(&genesis_state.address_prefixes().base) - .context("failed to write base prefix to state")?; + state_tx.put_base_prefix(genesis_state.address_prefixes().base()); + state_tx.put_ibc_compat_prefix(genesis_state.address_prefixes().ibc_compat()); let native_asset = genesis_state.native_asset_base_denomination(); state_tx.put_native_asset(native_asset); diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index 689eef7112..3cac47e7d0 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 99, - 71, - 23, - 38, - 116, - 242, - 68, - 164, - 170, + 42, + 254, + 79, 100, - 218, - 71, - 226, - 242, - 182, - 99, - 74, - 189, - 134, - 98, - 89, - 25, - 194, - 62, - 123, - 139, - 164, - 73, - 146, - 199, - 176, - 198 + 14, + 88, + 249, + 140, + 60, + 68, + 103, + 60, + 211, + 237, + 133, + 6, + 37, + 228, + 124, + 234, + 4, + 192, + 137, + 18, + 168, + 178, + 210, + 169, + 237, + 205, + 94, + 12 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap index 613000ccc5..a55cf5e98a 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 143, - 10, - 105, - 95, - 247, - 210, + 63, + 24, 181, - 87, - 187, - 107, - 181, - 71, - 93, - 168, - 188, - 53, - 254, - 10, - 23, - 25, - 241, - 157, - 243, - 211, - 131, - 41, - 195, - 30, - 85, - 157, - 224, - 225 + 216, + 217, + 115, + 38, + 253, + 109, + 199, + 115, + 204, + 82, + 198, + 133, + 240, + 196, + 183, + 132, + 69, + 91, + 36, + 221, + 66, + 7, + 138, + 192, + 34, + 104, + 180, + 197, + 174 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap index a146e38c65..23bba5e4ad 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 19, - 165, - 248, - 113, - 15, - 51, - 76, - 55, - 53, - 199, - 6, - 126, - 201, - 213, - 9, - 117, - 169, - 50, - 209, - 41, - 220, - 172, - 184, - 64, - 188, - 84, - 75, - 152, - 191, - 89, + 87, 231, - 2 + 66, + 80, + 204, + 49, + 140, + 151, + 76, + 190, + 73, + 155, + 193, + 83, + 40, + 216, + 168, + 235, + 214, + 182, + 107, + 2, + 110, + 30, + 176, + 28, + 68, + 85, + 139, + 82, + 234, + 136 ] diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index d08016b909..3170d57a71 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -94,9 +94,13 @@ pub(crate) fn default_fees() -> astria_core::protocol::genesis::v1alpha1::Fees { } pub(crate) fn address_prefixes() -> AddressPrefixes { - AddressPrefixes { - base: crate::test_utils::ASTRIA_PREFIX.into(), - } + AddressPrefixes::try_from_raw( + astria_core::generated::protocol::genesis::v1alpha1::AddressPrefixes { + base: crate::test_utils::ASTRIA_PREFIX.into(), + ibc_compat: crate::test_utils::ASTRIA_COMPAT_PREFIX.into(), + }, + ) + .unwrap() } pub(crate) fn proto_genesis_state() diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 22f07c4f4d..b8f5e2333d 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -526,6 +526,7 @@ mod test { ExtendedVoteInfo, Validator, }; + use tendermint_proto::types::CanonicalVoteExtension; use super::*; use crate::{ @@ -572,7 +573,7 @@ mod test { }, ]); state.put_validator_set(validator_set).unwrap(); - state.put_base_prefix("astria").unwrap(); + state.put_base_prefix("astria"); let extended_commit_info = ExtendedCommitInfo { round: 1u16.into(), @@ -594,4 +595,58 @@ mod test { .contains("submitted voting power is less than required voting power") ); } + + #[tokio::test] + async fn validate_vote_extensions_ok() { + let storage = cnidarium::TempStorage::new().await.unwrap(); + let snapshot = storage.latest_snapshot(); + let mut state = StateDelta::new(&snapshot); + + let chain_id: tendermint::chain::Id = "test-0".try_into().unwrap(); + state.put_chain_id_and_revision_number(chain_id.clone()); + let validator_set = ValidatorSet::new_from_updates(vec![ + ValidatorUpdate { + power: 1u16.into(), + verification_key: SigningKey::from([0; 32]).verification_key(), + }, + ValidatorUpdate { + power: 2u16.into(), + verification_key: SigningKey::from([1; 32]).verification_key(), + }, + ]); + state.put_validator_set(validator_set).unwrap(); + state.put_base_prefix("astria"); + + let round = 1u16; + let vote_extension_height = 1; + let vote_extension = CanonicalVoteExtension { + extension: vec![], + height: vote_extension_height, + round: i64::from(round), + chain_id: chain_id.to_string(), + }; + + let message = vote_extension.encode_length_delimited_to_vec(); + let signature = SigningKey::from([0; 32]).sign(&message); + + let extended_commit_info = ExtendedCommitInfo { + round: round.into(), + votes: vec![ExtendedVoteInfo { + validator: Validator { + address: SigningKey::from([0; 32]).verification_key().address_bytes(), + power: 1u16.into(), + }, + sig_info: Flag(tendermint::block::BlockIdFlag::Commit), + extension_signature: Some(signature.to_bytes().to_vec().try_into().unwrap()), + vote_extension: message.into(), + }], + }; + validate_vote_extensions( + &state, + vote_extension_height as u64 + 1, + &extended_commit_info, + ) + .await + .unwrap() + } } diff --git a/crates/astria-sequencer/src/authority/state_ext.rs b/crates/astria-sequencer/src/authority/state_ext.rs index f98d18a78f..2219b4ca49 100644 --- a/crates/astria-sequencer/src/authority/state_ext.rs +++ b/crates/astria-sequencer/src/authority/state_ext.rs @@ -150,7 +150,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // doesn't exist at first state diff --git a/crates/astria-sequencer/src/bridge/bridge_lock_action.rs b/crates/astria-sequencer/src/bridge/bridge_lock_action.rs index b2375aae24..7c9afa822c 100644 --- a/crates/astria-sequencer/src/bridge/bridge_lock_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_lock_action.rs @@ -188,7 +188,7 @@ mod tests { state.put_current_source(TransactionContext { address_bytes: from_address.bytes(), }); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); state.put_transfer_base_fee(transfer_fee).unwrap(); state.put_bridge_lock_byte_cost_multiplier(2); diff --git a/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs b/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs index 1ebe568d53..2dfd2cd16b 100644 --- a/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_sudo_change_action.rs @@ -131,7 +131,7 @@ mod tests { state.put_current_source(TransactionContext { address_bytes: [1; 20], }); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); let asset = test_asset(); state.put_allowed_fee_asset(&asset); @@ -167,7 +167,7 @@ mod tests { state.put_current_source(TransactionContext { address_bytes: sudo_address.bytes(), }); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); state.put_bridge_sudo_change_base_fee(10); let fee_asset = test_asset(); diff --git a/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs b/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs index 615f77b9e2..88bab91a13 100644 --- a/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs +++ b/crates/astria-sequencer/src/bridge/bridge_unlock_action.rs @@ -141,7 +141,7 @@ mod tests { state.put_current_source(TransactionContext { address_bytes: [1; 20], }); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); let asset = test_asset(); let transfer_amount = 100; @@ -178,7 +178,7 @@ mod tests { state.put_current_source(TransactionContext { address_bytes: [1; 20], }); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); let asset = test_asset(); let transfer_amount = 100; @@ -208,62 +208,6 @@ mod tests { ); } - #[tokio::test] - async fn execute_with_bridge_address_set() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - let bridge_address = astria_address(&[1; 20]); - state.put_current_source(TransactionContext { - address_bytes: bridge_address.bytes(), - }); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); - - let asset = test_asset(); - let transfer_fee = 10; - let transfer_amount = 100; - state.put_transfer_base_fee(transfer_fee).unwrap(); - - let to_address = astria_address(&[2; 20]); - let rollup_id = RollupId::from_unhashed_bytes(b"test_rollup_id"); - - state.put_bridge_account_rollup_id(bridge_address, &rollup_id); - state - .put_bridge_account_ibc_asset(bridge_address, &asset) - .unwrap(); - state.put_bridge_account_withdrawer_address(bridge_address, bridge_address); - state.put_allowed_fee_asset(&asset); - - let bridge_unlock = BridgeUnlockAction { - to: to_address, - amount: transfer_amount, - fee_asset: asset.clone(), - memo: String::new(), - bridge_address, - rollup_block_number: 1, - rollup_withdrawal_event_id: "a-rollup-defined-hash-3".to_string(), - }; - - // not enough balance; should fail - state - .put_account_balance(bridge_address, &asset, transfer_amount) - .unwrap(); - assert_anyhow_error( - &bridge_unlock - .check_and_execute(&mut state) - .await - .unwrap_err(), - "insufficient funds for transfer and fee payment", - ); - - // enough balance; should pass - state - .put_account_balance(bridge_address, &asset, transfer_amount + transfer_fee) - .unwrap(); - bridge_unlock.check_and_execute(&mut state).await.unwrap(); - } - #[tokio::test] async fn execute_with_duplicated_withdrawal_event_id() { let storage = cnidarium::TempStorage::new().await.unwrap(); @@ -274,7 +218,7 @@ mod tests { state.put_current_source(TransactionContext { address_bytes: bridge_address.bytes(), }); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); let asset = test_asset(); let transfer_fee = 10; diff --git a/crates/astria-sequencer/src/bridge/query.rs b/crates/astria-sequencer/src/bridge/query.rs index 7807ebb188..9b7050f8ce 100644 --- a/crates/astria-sequencer/src/bridge/query.rs +++ b/crates/astria-sequencer/src/bridge/query.rs @@ -314,7 +314,7 @@ mod test { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); let asset: astria_core::primitive::v1::asset::Denom = "test".parse().unwrap(); let rollup_id = RollupId::from_unhashed_bytes("test"); diff --git a/crates/astria-sequencer/src/ibc/ics20_transfer.rs b/crates/astria-sequencer/src/ibc/ics20_transfer.rs index 9149859455..7372d284e5 100644 --- a/crates/astria-sequencer/src/ibc/ics20_transfer.rs +++ b/crates/astria-sequencer/src/ibc/ics20_transfer.rs @@ -24,6 +24,8 @@ use astria_core::{ Denom, }, Address, + Bech32, + Bech32m, }, protocol::memos, sequencerblock::v1alpha1::block::Deposit, @@ -57,9 +59,12 @@ use penumbra_ibc::component::app_handler::{ AppHandlerExecute, }; use penumbra_proto::penumbra::core::component::ibc::v1::FungibleTokenPacketData; +use tokio::try_join; +use tracing::instrument; use crate::{ accounts::StateWriteExt as _, + address::StateReadExt as _, assets::{ StateReadExt as _, StateWriteExt as _, @@ -376,16 +381,27 @@ async fn execute_ics20_transfer( ) -> Result<()> { let packet_data: FungibleTokenPacketData = serde_json::from_slice(data).context("failed to decode FungibleTokenPacketData")?; + + // if the memo deserializes into an `Ics20WithdrawalFromRollupMemo`, + // we can assume this is a refund from an attempted withdrawal from + // a rollup directly to another IBC chain via the sequencer. + // + // in this case, we lock the tokens back in the bridge account and + // emit a `Deposit` event to send the tokens back to the rollup. + if is_refund + && serde_json::from_str::(&packet_data.memo) + .is_ok() + { + execute_withdrawal_refund_to_rollup(state, packet_data) + .await + .context("failed to execute rollup withdrawal refund")?; + return Ok(()); + } + let packet_amount: u128 = packet_data .amount .parse() .context("failed to parse packet data amount to u128")?; - let recipient = if is_refund { - packet_data.sender.clone() - } else { - packet_data.receiver - }; - let mut denom_trace = { let denom = packet_data .denom @@ -398,33 +414,12 @@ async fn execute_ics20_transfer( .context("failed to convert denomination if ibc/ prefixed")? }; - // if the memo deserializes into an `Ics20WithdrawalFromRollupMemo`, - // we can assume this is a refund from an attempted withdrawal from - // a rollup directly to another IBC chain via the sequencer. - // - // in this case, we lock the tokens back in the bridge account and - // emit a `Deposit` event to send the tokens back to the rollup. - if is_refund - && serde_json::from_str::(&packet_data.memo) - .is_ok() - { - let bridge_account = packet_data.sender.parse().context( - "sender not an Astria Address: for refunds of ics20 withdrawals that came from a \ - rollup, the sender must be a valid Astria Address (usually the bridge account)", - )?; - execute_rollup_withdrawal_refund( - state, - bridge_account, - &denom_trace, - packet_amount, - recipient, - ) - .await - .context("failed to execute rollup withdrawal refund")?; - return Ok(()); - } - // the IBC packet should have the address as a bech32 string + let recipient = if is_refund { + packet_data.sender.clone() + } else { + packet_data.receiver + }; let recipient = recipient.parse().context("invalid recipient address")?; let is_prefixed = denom_trace.starts_with_str(&format!("{source_port}/{source_channel}")); @@ -513,31 +508,109 @@ async fn execute_ics20_transfer( Ok(()) } -/// execute a refund of tokens that were withdrawn from a rollup to another -/// IBC-enabled chain via the sequencer using an `Ics20Withdrawal`, but were not -/// transferred to the destination IBC chain successfully. +/// Execute a refund for a failed ics20 transfer from a rollup to a remote IBC chain. +/// +/// A withdrawal of tokens from a rollup to a remote IBC chain is started with a +/// `Ics20Withdrawal` action via a (rollup's) bridge account on the sequencer. +/// +/// This function then sends the tokens back to the rollup via a `Deposit` event, +/// and again locks the tokens in the specified bridge account. +/// +/// This function must only be called if the following conditions hold: +/// 1. The ics20 tranfer is a refund; +/// 2. The memo contained in the ics20 transfer packet can be parsed as a Ics20WithdrawalFromRollup. /// -/// this functions sends the tokens back to the rollup via a `Deposit` event, -/// and locks the tokens back in the specified bridge account. -async fn execute_rollup_withdrawal_refund( +/// The function then assumes that the `packet_data.sender` is the bridge account, and +/// attempts to parse it as either a base-prefixed bech32m address, or as an ibc-compat-prefixed +/// bech32 address. If the sender is an ibc-compat address, then it will be converted to its +/// base-prefixed version. +/// +/// The emitted deposit as seen by the rollup will *never* be the ibc-compat prefixed version. +// TODO: Add `err` with https://github.com/astriaorg/astria/issues/1386 being done +#[instrument(skip_all)] +async fn execute_withdrawal_refund_to_rollup( state: &mut S, - bridge_address: Address, - denom: &denom::TracePrefixed, - amount: u128, - destination_address: String, + packet_data: FungibleTokenPacketData, ) -> Result<()> { - execute_deposit(state, bridge_address, denom, amount, destination_address).await?; + let amount: u128 = packet_data + .amount + .parse() + .context("failed to parse packet data amount to u128")?; + let denom = { + let denom = packet_data + .denom + .parse::() + .context("failed parsing denom in packet data as Denom")?; + // convert denomination if it's prefixed with `ibc/` + // note: this denomination might have a prefix, but it wasn't prefixed by us right now. + convert_denomination_if_ibc_prefixed(state, denom) + .await + .context("failed to convert denomination if ibc/ prefixed")? + }; + let bridge_address = parse_refund_sender(&*state, &packet_data.sender) + .await + .context("failed to parse ibc packet sender as the refund target address")?; + execute_deposit( + state, + bridge_address, + &denom, + amount, + bridge_address.to_string(), + ) + .await + .context("failed to emit deposit")?; state .increase_balance(bridge_address, denom, amount) .await - .context( - "failed to update bridge account account balance in execute_rollup_withdrawal_refund", - )?; + .context("failed to update bridge account account balance")?; Ok(()) } +async fn parse_refund_sender(state: &S, sender: &str) -> anyhow::Result
{ + use futures::TryFutureExt as _; + let (base_prefix, compat_prefix) = match try_join!( + state + .get_base_prefix() + .map_err(|e| e.context("failed to read base prefix from state")), + state + .get_ibc_compat_prefix() + .map_err(|e| e.context("failed to read ibc compat prefix from state")) + ) { + Ok(prefixes) => prefixes, + Err(err) => return Err(err), + }; + sender + .parse::>() + .context("failed to parse address in bech32m format") + .and_then(|addr| { + ensure!( + addr.prefix() == base_prefix, + "address prefix is not base prefix stored in state" + ); + Ok(addr) + }) + .or_else(|_| { + sender + .parse::>() + .context("failed to parse address in bech32/compat format") + .and_then(|addr| { + ensure!( + addr.prefix() == compat_prefix, + "address prefix is not base prefix stored in state" + ); + addr.to_prefix(&base_prefix) + .context( + "failed to convert ibc compat prefixed address to standard base \ + prefixed address", + ) + .map(|addr| addr.to_format::()) + }) + }) + // "sender address was neither base nor ibc-compat prefixed; returning last error", +} + /// execute an ics20 transfer where the recipient is a bridge account. /// /// if the recipient is not a bridge account, or the incoming packet is a refund, @@ -649,10 +722,14 @@ mod test { use super::*; use crate::{ accounts::StateReadExt as _, + address::StateWriteExt, ibc::StateWriteExt as _, test_utils::{ astria_address, astria_address_from_hex_string, + astria_compat_address, + ASTRIA_COMPAT_PREFIX, + ASTRIA_PREFIX, }, }; @@ -991,7 +1068,11 @@ mod test { let snapshot = storage.latest_snapshot(); let mut state_tx = StateDelta::new(snapshot.clone()); + state_tx.put_base_prefix(ASTRIA_PREFIX); + state_tx.put_ibc_compat_prefix(ASTRIA_COMPAT_PREFIX); + let bridge_address = astria_address(&[99u8; 20]); + let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); let denom = "dest_port/dest_channel/nootasset" .parse::() @@ -1003,16 +1084,18 @@ mod test { .unwrap(); let amount = 100; - let destination_address = "destinationaddress".to_string(); - execute_rollup_withdrawal_refund( - &mut state_tx, - bridge_address, - &denom, - amount, - destination_address, - ) - .await - .expect("valid rollup withdrawal refund"); + let address_on_rollup = "address_on_rollup".to_string(); + + let packet = FungibleTokenPacketData { + denom: denom.to_string(), + sender: bridge_address.to_string(), + amount: amount.to_string(), + receiver: address_on_rollup.to_string(), + memo: String::new(), + }; + execute_withdrawal_refund_to_rollup(&mut state_tx, packet) + .await + .expect("valid rollup withdrawal refund"); let balance = state_tx .get_account_balance(bridge_address, denom) @@ -1028,11 +1111,14 @@ mod test { } #[tokio::test] - async fn execute_ics20_transfer_rollup_withdrawal_refund() { + async fn rollup_withdrawal_refund_succeeds() { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); let mut state_tx = StateDelta::new(snapshot.clone()); + state_tx.put_base_prefix(ASTRIA_PREFIX); + state_tx.put_ibc_compat_prefix(ASTRIA_COMPAT_PREFIX); + let bridge_address = astria_address(&[99u8; 20]); let destination_chain_address = bridge_address.to_string(); let denom = "nootasset".parse::().unwrap(); @@ -1095,4 +1181,79 @@ mod test { ); assert_eq!(deposit, &expected_deposit); } + + #[tokio::test] + async fn rollup_withdrawal_refund_with_compat_address_succeeds() { + let storage = cnidarium::TempStorage::new().await.unwrap(); + let snapshot = storage.latest_snapshot(); + let mut state_tx = StateDelta::new(snapshot.clone()); + + state_tx.put_base_prefix(ASTRIA_PREFIX); + state_tx.put_ibc_compat_prefix(ASTRIA_COMPAT_PREFIX); + + let bridge_address = astria_address(&[99u8; 20]); + let bridge_address_compat = astria_compat_address(&[99u8; 20]); + let denom = "nootasset".parse::().unwrap(); + let rollup_id = RollupId::from_unhashed_bytes(b"testchainid"); + + state_tx.put_bridge_account_rollup_id(bridge_address, &rollup_id); + state_tx + .put_bridge_account_ibc_asset(bridge_address, &denom) + .unwrap(); + + let packet = FungibleTokenPacketData { + denom: denom.to_string(), + sender: bridge_address_compat.to_string(), + amount: "100".to_string(), + receiver: "other-chain-address".to_string(), + memo: serde_json::to_string(&memos::v1alpha1::Ics20WithdrawalFromRollup { + memo: String::new(), + rollup_block_number: 1, + rollup_return_address: "rollup-defined".to_string(), + rollup_withdrawal_event_id: hex::encode([1u8; 32]), + }) + .unwrap(), + }; + let packet_bytes = serde_json::to_vec(&packet).unwrap(); + + execute_ics20_transfer( + &mut state_tx, + &packet_bytes, + &"source_port".to_string().parse().unwrap(), + &"source_channel".to_string().parse().unwrap(), + &"source_port".to_string().parse().unwrap(), + &"source_channel".to_string().parse().unwrap(), + true, + ) + .await + .expect("valid ics20 transfer refund; recipient, memo, and asset ID are valid"); + + let balance = state_tx + .get_account_balance(bridge_address, &denom) + .await + .expect( + "ics20 transfer refunding to rollup should succeed and balance should be added to \ + the bridge account", + ); + assert_eq!(balance, 100); + + let deposits = state_tx + .get_block_deposits() + .await + .expect("a deposit should exist as a result of the rollup withdrawal refund"); + assert_eq!(deposits.len(), 1); + + let deposit = deposits.get(&rollup_id).unwrap().first().unwrap(); + let expected_deposit = Deposit::new( + bridge_address, + rollup_id, + 100, + denom, + bridge_address.to_string(), /* NOTE: this is the non-compat address because it will + * be converted from the compat bech32 to the + * standard/non-compat bech32m version before emitting + * the deposit event */ + ); + assert_eq!(deposit, &expected_deposit); + } } diff --git a/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs b/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs index 515befa6ba..ee91e52eee 100644 --- a/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs +++ b/crates/astria-sequencer/src/ibc/ics20_withdrawal.rs @@ -8,6 +8,7 @@ use astria_core::{ primitive::v1::{ asset::Denom, Address, + Bech32, }, protocol::{ memos::v1alpha1::Ics20WithdrawalFromRollup, @@ -29,6 +30,7 @@ use penumbra_ibc::component::packet::{ SendPacketWrite as _, Unchecked, }; +use penumbra_proto::core::component::ibc::v1::FungibleTokenPacketData; use crate::{ accounts::{ @@ -50,20 +52,42 @@ use crate::{ transaction::StateReadExt as _, }; -fn withdrawal_to_unchecked_ibc_packet( +async fn create_ibc_packet_from_withdrawal( withdrawal: &action::Ics20Withdrawal, -) -> IBCPacket { - let packet_data = withdrawal.to_fungible_token_packet_data(); + state: S, +) -> anyhow::Result> { + let sender = if withdrawal.use_compat_address { + let ibc_compat_prefix = state.get_ibc_compat_prefix().await.context( + "need to construct bech32 compatible address for IBC communication but failed reading \ + required prefix from state", + )?; + withdrawal + .return_address() + .to_prefix(&ibc_compat_prefix) + .context("failed to convert the address to the bech32 compatible prefix")? + .to_format::() + .to_string() + } else { + withdrawal.return_address.to_string() + }; + let packet = FungibleTokenPacketData { + amount: withdrawal.amount.to_string(), + denom: withdrawal.denom.to_string(), + sender, + receiver: withdrawal.destination_chain_address.clone(), + memo: withdrawal.memo.clone(), + }; + let serialized_packet_data = - serde_json::to_vec(&packet_data).expect("can serialize FungibleTokenPacketData as JSON"); + serde_json::to_vec(&packet).context("failed to serialize fungible token packet as JSON")?; - IBCPacket::new( + Ok(IBCPacket::new( PortId::transfer(), withdrawal.source_channel().clone(), *withdrawal.timeout_height(), withdrawal.timeout_time(), serialized_packet_data, - ) + )) } /// Establishes the withdrawal target. @@ -193,7 +217,9 @@ impl ActionHandler for action::Ics20Withdrawal { .await .context("failed to get block timestamp")?; let packet = { - let packet = withdrawal_to_unchecked_ibc_packet(self); + let packet = create_ibc_packet_from_withdrawal(self, &state) + .await + .context("failed converting the withdrawal action into IBC packet")?; state .send_packet_check(packet, current_timestamp) .await @@ -282,6 +308,7 @@ mod tests { source_channel: "channel-0".to_string().parse().unwrap(), fee_asset: denom.clone(), memo: String::new(), + use_compat_address: false, }; assert_eq!( @@ -298,7 +325,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // sender is a bridge address, which is also the withdrawer, so it's ok let bridge_address = [1u8; 20]; @@ -320,6 +347,7 @@ mod tests { source_channel: "channel-0".to_string().parse().unwrap(), fee_asset: denom.clone(), memo: String::new(), + use_compat_address: false, }; assert_anyhow_error( @@ -353,6 +381,7 @@ mod tests { source_channel: "channel-0".to_string().parse().unwrap(), fee_asset: denom(), memo: String::new(), + use_compat_address: false, } } @@ -361,7 +390,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // withdraw is *not* the bridge address, Ics20Withdrawal must be sent by the withdrawer state.put_bridge_account_rollup_id( @@ -395,7 +424,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // sender the withdrawer address, so it's ok let bridge_address = [1u8; 20]; @@ -418,6 +447,7 @@ mod tests { source_channel: "channel-0".to_string().parse().unwrap(), fee_asset: denom.clone(), memo: String::new(), + use_compat_address: false, }; assert_eq!( @@ -449,6 +479,7 @@ mod tests { source_channel: "channel-0".to_string().parse().unwrap(), fee_asset: denom.clone(), memo: String::new(), + use_compat_address: false, }; assert_anyhow_error( diff --git a/crates/astria-sequencer/src/ibc/state_ext.rs b/crates/astria-sequencer/src/ibc/state_ext.rs index 880f86f6ba..b5c230bf98 100644 --- a/crates/astria-sequencer/src/ibc/state_ext.rs +++ b/crates/astria-sequencer/src/ibc/state_ext.rs @@ -227,7 +227,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // can write new let mut address = [42u8; 20]; @@ -264,7 +264,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // unset address returns false let address = astria_address(&[42u8; 20]); @@ -283,7 +283,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // can write let address = astria_address(&[42u8; 20]); @@ -313,7 +313,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state = StateDelta::new(snapshot); - state.put_base_prefix(ASTRIA_PREFIX).unwrap(); + state.put_base_prefix(ASTRIA_PREFIX); // can write let address = astria_address(&[42u8; 20]); diff --git a/crates/astria-sequencer/src/service/info/mod.rs b/crates/astria-sequencer/src/service/info/mod.rs index c0135c13ea..11e1348036 100644 --- a/crates/astria-sequencer/src/service/info/mod.rs +++ b/crates/astria-sequencer/src/service/info/mod.rs @@ -216,7 +216,7 @@ mod test { let mut state = StateDelta::new(storage.latest_snapshot()); state.put_storage_version_by_height(height, version); - state.put_base_prefix("astria").unwrap(); + state.put_base_prefix("astria"); state.put_native_asset(&crate::test_utils::nria()); let address = state diff --git a/crates/astria-sequencer/src/test_utils.rs b/crates/astria-sequencer/src/test_utils.rs index 7684f8f02f..f39ee12d71 100644 --- a/crates/astria-sequencer/src/test_utils.rs +++ b/crates/astria-sequencer/src/test_utils.rs @@ -1,9 +1,11 @@ use astria_core::primitive::v1::{ asset::TracePrefixed, Address, + Bech32, }; pub(crate) const ASTRIA_PREFIX: &str = "astria"; +pub(crate) const ASTRIA_COMPAT_PREFIX: &str = "astriacompat"; pub(crate) fn astria_address(bytes: &[u8]) -> Address { Address::builder() @@ -13,6 +15,15 @@ pub(crate) fn astria_address(bytes: &[u8]) -> Address { .unwrap() } +#[cfg_attr(feature = "benchmark", allow(dead_code))] +pub(crate) fn astria_compat_address(bytes: &[u8]) -> Address { + Address::builder() + .prefix(ASTRIA_COMPAT_PREFIX) + .slice(bytes) + .try_build() + .unwrap() +} + pub(crate) fn astria_address_from_hex_string(s: &str) -> Address { let bytes = hex::decode(s).unwrap(); Address::builder() diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index 77aad88236..47e3986bf9 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -324,6 +324,7 @@ mod tests { bridge::StateWriteExt as _, ibc::StateWriteExt as _, sequence::StateWriteExt as _, + test_utils::ASTRIA_PREFIX, }; #[tokio::test] @@ -332,7 +333,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state_tx = StateDelta::new(snapshot); - state_tx.put_base_prefix("astria").unwrap(); + state_tx.put_base_prefix("astria"); state_tx.put_native_asset(&crate::test_utils::nria()); state_tx.put_transfer_base_fee(12).unwrap(); state_tx.put_sequence_action_base_fee(0); @@ -409,7 +410,7 @@ mod tests { let snapshot = storage.latest_snapshot(); let mut state_tx = StateDelta::new(snapshot); - state_tx.put_base_prefix("nria").unwrap(); + state_tx.put_base_prefix(ASTRIA_PREFIX); state_tx.put_native_asset(&crate::test_utils::nria()); state_tx.put_transfer_base_fee(12).unwrap(); state_tx.put_sequence_action_base_fee(0); diff --git a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto index 3962280f69..8c6329e428 100644 --- a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto +++ b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto @@ -26,7 +26,11 @@ message Account { } message AddressPrefixes { + // The base prefix used for most Astria Sequencer addresses. string base = 1; + // The prefix used for sending ics20 transfers to IBC chains + // that enforce a bech32 format of the packet sender. + string ibc_compat = 2; } // IBC configuration data. diff --git a/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto b/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto index 05f40dc21b..46342da5ef 100644 --- a/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto +++ b/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto @@ -130,6 +130,11 @@ message Ics20Withdrawal { // if unset, and the transaction sender is a bridge account, the withdrawal is // treated as a bridge withdrawal (ie. the bridge account's withdrawer address is checked). astria.primitive.v1.Address bridge_address = 10; + + // whether to use a bech32-compatible format of the `.return_address` when generating + // fungible token packets (as opposed to Astria-native bech32m addresses). This is + // necessary for chains like noble which enforce a strict bech32 format. + bool use_compat_address = 11; } message IbcHeight { From f8db7155a7a408adfa957f48103f7b5e1d07d715 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 5 Sep 2024 16:08:53 -0400 Subject: [PATCH 57/89] add unit test --- crates/astria-sequencer/src/app/vote_extension.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index b8f5e2333d..e2a71a27ab 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -606,7 +606,7 @@ mod test { state.put_chain_id_and_revision_number(chain_id.clone()); let validator_set = ValidatorSet::new_from_updates(vec![ ValidatorUpdate { - power: 1u16.into(), + power: 5u16.into(), verification_key: SigningKey::from([0; 32]).verification_key(), }, ValidatorUpdate { @@ -619,8 +619,9 @@ mod test { let round = 1u16; let vote_extension_height = 1; + let vote_extension_message = b"noot".to_vec(); let vote_extension = CanonicalVoteExtension { - extension: vec![], + extension: vote_extension_message.clone(), height: vote_extension_height, round: i64::from(round), chain_id: chain_id.to_string(), @@ -638,7 +639,7 @@ mod test { }, sig_info: Flag(tendermint::block::BlockIdFlag::Commit), extension_signature: Some(signature.to_bytes().to_vec().try_into().unwrap()), - vote_extension: message.into(), + vote_extension: vote_extension_message.into(), }], }; validate_vote_extensions( From f6d43dcb809c840130cb3ed54a7c74e65e8c8675 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 5 Sep 2024 16:23:12 -0400 Subject: [PATCH 58/89] cleanup --- crates/astria-bridge-withdrawer/priv.key | 1 - 1 file changed, 1 deletion(-) delete mode 100644 crates/astria-bridge-withdrawer/priv.key diff --git a/crates/astria-bridge-withdrawer/priv.key b/crates/astria-bridge-withdrawer/priv.key deleted file mode 100644 index e0cd2b4245..0000000000 --- a/crates/astria-bridge-withdrawer/priv.key +++ /dev/null @@ -1 +0,0 @@ -2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90 From 1ecfc145346711196d8f4d34df6bc2ae325616c8 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 5 Sep 2024 16:23:57 -0400 Subject: [PATCH 59/89] cleanup --- .../genesis-example.json | 161 ------------------ 1 file changed, 161 deletions(-) delete mode 100644 crates/astria-sequencer-utils/genesis-example.json diff --git a/crates/astria-sequencer-utils/genesis-example.json b/crates/astria-sequencer-utils/genesis-example.json deleted file mode 100644 index 959c689720..0000000000 --- a/crates/astria-sequencer-utils/genesis-example.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "chainId": "test-1", - "addressPrefixes": { - "base": "astria" - }, - "accounts": [ - { - "address": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "balance": { - "lo": "1000000000000000000" - } - }, - { - "address": { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - }, - "balance": { - "lo": "1000000000000000000" - } - }, - { - "address": { - "bech32m": "astria1vpcfutferpjtwv457r63uwr6hdm8gwr3pxt5ny" - }, - "balance": { - "lo": "1000000000000000000" - } - } - ], - "authoritySudoAddress": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "ibcSudoAddress": { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - "ibcRelayerAddresses": [ - { - "bech32m": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - }, - { - "bech32m": "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - } - ], - "nativeAssetBaseDenomination": "nria", - "ibcParameters": { - "ibcEnabled": true, - "inboundIcs20TransfersEnabled": true, - "outboundIcs20TransfersEnabled": true - }, - "allowedFeeAssets": [ - "nria" - ], - "fees": { - "transferBaseFee": { - "lo": "12" - }, - "sequenceBaseFee": { - "lo": "32" - }, - "sequenceByteCostMultiplier": { - "lo": "1" - }, - "initBridgeAccountBaseFee": { - "lo": "48" - }, - "bridgeLockByteCostMultiplier": { - "lo": "1" - }, - "bridgeSudoChangeFee": { - "lo": "24" - }, - "ics20WithdrawalBaseFee": { - "lo": "24" - } - }, - "slinky": { - "marketMap": { - "marketMap": { - "markets": { - "BTC/USD": { - "ticker": { - "currencyPair": { - "Base": "BTC", - "Quote": "USD" - }, - "decimals": "8", - "minProviderCount": "3", - "enabled": true - }, - "providerConfigs": [ - { - "name": "coingecko_api", - "offChainTicker": "bitcoin/usd", - "normalizeByPair": { - "Base": "USDT", - "Quote": "USD" - } - } - ] - }, - "ETH/USD": { - "ticker": { - "currencyPair": { - "Base": "ETH", - "Quote": "USD" - }, - "decimals": "8", - "minProviderCount": "3", - "enabled": true - }, - "providerConfigs": [ - { - "name": "coingecko_api", - "offChainTicker": "ethereum/usd", - "normalizeByPair": { - "Base": "USDT", - "Quote": "USD" - } - } - ] - } - } - }, - "params": { - "marketAuthorities": [ - "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm", - "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" - ], - "admin": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - } - }, - "oracle": { - "currencyPairGenesis": [ - { - "currencyPair": { - "Base": "BTC", - "Quote": "USD" - }, - "currencyPairPrice": { - "price": "5834065777", - "blockTimestamp": "2024-07-04T19:46:35+00:00" - } - }, - { - "currencyPair": { - "Base": "ETH", - "Quote": "USD" - }, - "currencyPairPrice": { - "price": "3138872234", - "blockTimestamp": "2024-07-04T19:46:35+00:00" - }, - "id": "1" - } - ], - "nextId": "2" - } - } -} \ No newline at end of file From 0506bba943af0cdfe5c5918b9e81c1b2afb95e07 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 5 Sep 2024 17:14:39 -0400 Subject: [PATCH 60/89] clippy --- charts/sequencer/Chart.yaml | 2 +- crates/astria-sequencer/src/app/vote_extension.rs | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/charts/sequencer/Chart.yaml b/charts/sequencer/Chart.yaml index 70e4b040dd..6d9bb9ce19 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.22.1 +version: 0.22.2 # 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/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index e2a71a27ab..be6ef26089 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -618,11 +618,11 @@ mod test { state.put_base_prefix("astria"); let round = 1u16; - let vote_extension_height = 1; + let vote_extension_height = 1u64; let vote_extension_message = b"noot".to_vec(); let vote_extension = CanonicalVoteExtension { extension: vote_extension_message.clone(), - height: vote_extension_height, + height: vote_extension_height.try_into().unwrap(), round: i64::from(round), chain_id: chain_id.to_string(), }; @@ -642,12 +642,8 @@ mod test { vote_extension: vote_extension_message.into(), }], }; - validate_vote_extensions( - &state, - vote_extension_height as u64 + 1, - &extended_commit_info, - ) - .await - .unwrap() + validate_vote_extensions(&state, vote_extension_height + 1, &extended_commit_info) + .await + .unwrap(); } } From 1b8df4979ff0fd856f1121e4dd003eea1ee65ecf Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 9 Sep 2024 18:43:33 +0200 Subject: [PATCH 61/89] refactor(sequencer): use streams for fetching slinky data from state (#1470) Extensions on `cnidarium::StateRead` should implement a stream rather than allocate and collect into datastructures. --- Cargo.lock | 1 + crates/astria-sequencer/Cargo.toml | 1 + crates/astria-sequencer/src/grpc/slinky.rs | 54 ++++--- .../src/slinky/oracle/state_ext.rs | 149 +++++++++++++----- 4 files changed, 143 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d42442b05..5dd90eb391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -814,6 +814,7 @@ dependencies = [ "penumbra-ibc", "penumbra-proto", "penumbra-tower-trace", + "pin-project-lite", "prost", "rand 0.8.5", "rand_chacha 0.3.1", diff --git a/crates/astria-sequencer/Cargo.toml b/crates/astria-sequencer/Cargo.toml index 21fed7cb77..28d86548ac 100644 --- a/crates/astria-sequencer/Cargo.toml +++ b/crates/astria-sequencer/Cargo.toml @@ -51,6 +51,7 @@ indexmap = { workspace = true } penumbra-ibc = { workspace = true, features = ["component", "rpc"] } penumbra-proto = { workspace = true } penumbra-tower-trace = { workspace = true } +pin-project-lite = { workspace = true } prost = { workspace = true } rand = { workspace = true } regex = { workspace = true } diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index 24190e03eb..e42e2b75d5 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -31,6 +31,10 @@ use astria_core::{ slinky::types::v1::CurrencyPair, }; use cnidarium::Storage; +use futures::{ + TryFutureExt as _, + TryStreamExt as _, +}; use tonic::{ Request, Response, @@ -41,7 +45,10 @@ use tracing::instrument; use crate::{ slinky::{ marketmap::state_ext::StateReadExt as _, - oracle::state_ext::StateReadExt as _, + oracle::state_ext::{ + CurrencyPairWithId, + StateReadExt as _, + }, }, state_ext::StateReadExt as _, }; @@ -143,16 +150,18 @@ impl OracleService for SequencerServer { _request: Request, ) -> Result, Status> { let snapshot = self.storage.latest_snapshot(); - let currency_pairs = snapshot.get_all_currency_pairs().await.map_err(|e| { - Status::internal(format!( - "failed to get all currency pairs from storage: {e:#}" - )) - })?; + let currency_pairs = snapshot + .currency_pairs() + .map_ok(CurrencyPair::into_raw) + .try_collect() + .map_err(|err| { + Status::internal(format!( + "failed to get all currency pairs from storage: {err:#}" + )) + }) + .await?; Ok(Response::new(GetAllCurrencyPairsResponse { - currency_pairs: currency_pairs - .into_iter() - .map(CurrencyPair::into_raw) - .collect(), + currency_pairs, })) } @@ -266,16 +275,21 @@ impl OracleService for SequencerServer { _request: Request, ) -> Result, Status> { let snapshot = self.storage.latest_snapshot(); - let currency_pair_mapping = snapshot.get_currency_pair_mapping().await.map_err(|e| { - Status::internal(format!( - "failed to get currency pair mapping from storage: {e:#}" - )) - })?; - let currency_pair_mapping = currency_pair_mapping - .into_iter() - .map(|(k, v)| (k, v.into_raw())) - .collect(); - + let currency_pair_mapping = snapshot + .currency_pairs_with_ids() + .map_ok( + |CurrencyPairWithId { + id, + currency_pair, + }| (id, currency_pair.into_raw()), + ) + .try_collect() + .map_err(|err| { + Status::internal(format!( + "failed to get currency pair mapping from storage: {err:#}" + )) + }) + .await?; Ok(Response::new(GetCurrencyPairMappingResponse { currency_pair_mapping, })) diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs index ac5aebf45c..839072560e 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs @@ -1,8 +1,15 @@ -use std::collections::HashMap; +use std::{ + pin::Pin, + task::{ + ready, + Context, + Poll, + }, +}; use anyhow::{ bail, - Context, + Context as _, Result, }; use astria_core::slinky::{ @@ -21,7 +28,8 @@ use cnidarium::{ StateRead, StateWrite, }; -use futures::StreamExt as _; +use futures::Stream; +use pin_project_lite::pin_project; use tracing::instrument; const CURRENCY_PAIR_TO_ID_PREFIX: &str = "oraclecpid"; @@ -52,6 +60,92 @@ struct Id(u64); #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Count(u64); +pin_project! { + pub(crate) struct CurrencyPairsWithIdsStream { + #[pin] + underlying: St, + } +} + +pub(crate) struct CurrencyPairWithId { + pub(crate) id: u64, + pub(crate) currency_pair: CurrencyPair, +} + +impl Stream for CurrencyPairsWithIdsStream +where + St: Stream)>>, +{ + type Item = anyhow::Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let (key, bytes) = match ready!(this.underlying.as_mut().poll_next(cx)) { + Some(Ok(item)) => item, + Some(Err(err)) => { + return Poll::Ready(Some(Err(err).context("failed reading from state"))); + } + None => return Poll::Ready(None), + }; + let Id(id) = Id::try_from_slice(&bytes).with_context(|| { + "failed decoding bytes read from state as currency pair ID for key `{key}`" + })?; + let currency_pair = match extract_currency_pair_from_key(&key) { + Err(err) => { + return Poll::Ready(Some(Err(err).with_context(|| { + format!("failed to extract currency pair from key `{key}`") + }))); + } + Ok(parsed) => parsed, + }; + Poll::Ready(Some(Ok(CurrencyPairWithId { + id, + currency_pair, + }))) + } +} + +pin_project! { + pub(crate) struct CurrencyPairsStream { + #[pin] + underlying: St, + } +} + +impl Stream for CurrencyPairsStream +where + St: Stream>, +{ + type Item = anyhow::Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let key = match ready!(this.underlying.as_mut().poll_next(cx)) { + Some(Ok(item)) => item, + Some(Err(err)) => { + return Poll::Ready(Some(Err(err).context("failed reading from state"))); + } + None => return Poll::Ready(None), + }; + let currency_pair = match extract_currency_pair_from_key(&key) { + Err(err) => { + return Poll::Ready(Some(Err(err).with_context(|| { + format!("failed to extract currency pair from key `{key}`") + }))); + } + Ok(parsed) => parsed, + }; + Poll::Ready(Some(Ok(currency_pair))) + } +} + +fn extract_currency_pair_from_key(key: &str) -> anyhow::Result { + key.strip_prefix(CURRENCY_PAIR_TO_ID_PREFIX) + .context("failed to strip prefix from currency pair state key")? + .parse::() + .context("failed to parse storage key suffix as currency pair") +} + #[async_trait] pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] @@ -84,29 +178,17 @@ pub(crate) trait StateReadExt: StateRead { } #[instrument(skip_all)] - async fn get_currency_pair_mapping(&self) -> Result> { - let prefix = format!("{CURRENCY_PAIR_TO_ID_PREFIX}/"); - let mut currency_pairs = HashMap::new(); - - let mut stream = std::pin::pin!(self.prefix_keys(&prefix)); - while let Some(Ok(key)) = stream.next().await { - let Some(bytes) = self - .get_raw(&key) - .await - .context("failed reading currency pair id from state")? - else { - bail!("currency pair not found in state; this is a bug") - }; - let Id(id) = Id::try_from_slice(&bytes).context("invalid currency pair id bytes")?; - - let currency_pair = key - .strip_prefix(&prefix) - .context("failed to strip prefix from currency pair state key")? - .parse::() - .context("failed to parse storage key suffix as currency pair")?; - currency_pairs.insert(id, currency_pair); + fn currency_pairs_with_ids(&self) -> CurrencyPairsWithIdsStream { + CurrencyPairsWithIdsStream { + underlying: self.prefix_raw(CURRENCY_PAIR_TO_ID_PREFIX), + } + } + + #[instrument(skip_all)] + fn currency_pairs(&self) -> CurrencyPairsStream { + CurrencyPairsStream { + underlying: self.prefix_keys(CURRENCY_PAIR_STATE_PREFIX), } - Ok(currency_pairs) } #[instrument(skip_all)] @@ -156,23 +238,6 @@ pub(crate) trait StateReadExt: StateRead { } } - #[instrument(skip_all)] - async fn get_all_currency_pairs(&self) -> Result> { - let prefix = format!("{CURRENCY_PAIR_STATE_PREFIX}/"); - let mut currency_pairs: Vec = Vec::new(); - - let mut stream = std::pin::pin!(self.prefix_keys(&prefix)); - while let Some(Ok(key)) = stream.next().await { - let currency_pair = key - .strip_prefix(&prefix) - .context("failed to strip prefix from currency pair state key")? - .parse::() - .context("failed to parse storage key suffix as currency pair")?; - currency_pairs.push(currency_pair); - } - Ok(currency_pairs) - } - #[instrument(skip_all)] async fn get_next_currency_pair_id(&self) -> Result { let Some(bytes) = self From dca2e526a38dbbf654bb8d5b6db2a9c115e37eb7 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 19 Sep 2024 21:49:30 +0200 Subject: [PATCH 62/89] rework oracle types (#1484) This patch enforces more type strictes on slinky oracle domain types: 1. Nonces, Ids, and Prices are new-type wrappers around their respective primtives; This is to avoid using `u128` in place of a `Price`, for example. 2. All oracle types written to state get their own types that themselves derive the borsh serialization traits. 3. All json is removed from the oracle state read and write extension trait. 4. A lot of validation code is moved from sequencer to core so that validation happens closer to the boundary. 5. Functions in the public interface for constructing various validation errors were removed. 6. Constructors that would allow violating invariants (specifically invariants around the contents of currency pairs) were removed. A bug was fixed in the `oracle::StateReadExt::put_price_for_currency_pair` trait method, which was not updating the nonce. --- Cargo.lock | 8 +- crates/astria-core/src/display.rs | 57 ++++ crates/astria-core/src/lib.rs | 1 + crates/astria-core/src/primitive/mod.rs | 1 + .../src/protocol/genesis/v1alpha1.rs | 66 ++-- crates/astria-core/src/slinky/abci.rs | 70 ++++- crates/astria-core/src/slinky/market_map.rs | 68 +++- crates/astria-core/src/slinky/mod.rs | 1 + crates/astria-core/src/slinky/oracle.rs | 132 ++++---- crates/astria-core/src/slinky/service.rs | 85 +++++ crates/astria-core/src/slinky/types.rs | 282 +++++++++++++++-- crates/astria-sequencer-utils/Cargo.toml | 2 +- .../src/genesis_example.rs | 181 +++++------ crates/astria-sequencer/src/app/test_utils.rs | 17 +- .../src/app/vote_extension.rs | 133 ++++---- crates/astria-sequencer/src/grpc/slinky.rs | 20 +- .../src/slinky/oracle/component.rs | 4 +- .../slinky/oracle/currency_pair_strategy.rs | 26 +- .../src/slinky/oracle/state_ext.rs | 295 ++++++++++++++---- 19 files changed, 1046 insertions(+), 403 deletions(-) create mode 100644 crates/astria-core/src/display.rs create mode 100644 crates/astria-core/src/slinky/service.rs diff --git a/Cargo.lock b/Cargo.lock index 5efd2e3bad..0d502eeb80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -929,8 +929,8 @@ dependencies = [ "ethers-core", "hex", "indenter", - "indexmap 2.4.0", "itertools 0.12.1", + "maplit", "pbjson-types", "predicates", "prost", @@ -4805,6 +4805,12 @@ dependencies = [ "libc", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matchers" version = "0.1.0" diff --git a/crates/astria-core/src/display.rs b/crates/astria-core/src/display.rs new file mode 100644 index 0000000000..112a041393 --- /dev/null +++ b/crates/astria-core/src/display.rs @@ -0,0 +1,57 @@ +use std::fmt::{ + Display, + Formatter, + Result, +}; + +/// Format `bytes` using standard base64 formatting. +/// +/// See the [`base64::engine::general_purpose::STANDARD`] for the formatting definition. +/// +/// # Example +/// ``` +/// use astria_core::display; +/// let signature = vec![1u8, 2, 3, 4, 5, 6, 7, 8]; +/// println!("received signature: {}", display::base64(&signature)); +/// ``` +pub fn base64 + ?Sized>(bytes: &T) -> Base64<'_> { + Base64(bytes.as_ref()) +} + +pub struct Base64<'a>(&'a [u8]); + +impl<'a> Display for Base64<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + use base64::{ + display::Base64Display, + engine::general_purpose::STANDARD, + }; + Base64Display::new(self.0, &STANDARD).fmt(f) + } +} + +/// A newtype wrapper of a byte slice that implements [`std::fmt::Display`]. +/// +/// To be used in tracing contexts. See the [`self::hex`] utility. +pub struct Hex<'a>(&'a [u8]); + +/// Format `bytes` as lower-cased hex. +/// +/// # Example +/// ``` +/// use astria_core::display; +/// let signature = vec![1u8, 2, 3, 4, 5, 6, 7, 8]; +/// println!("received signature: {}", display::hex(&signature)); +/// ``` +pub fn hex + ?Sized>(bytes: &T) -> Hex<'_> { + Hex(bytes.as_ref()) +} + +impl<'a> Display for Hex<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + for byte in self.0 { + f.write_fmt(format_args!("{byte:02x}"))?; + } + Ok(()) + } +} diff --git a/crates/astria-core/src/lib.rs b/crates/astria-core/src/lib.rs index 144fd3c6da..9d1c3d6829 100644 --- a/crates/astria-core/src/lib.rs +++ b/crates/astria-core/src/lib.rs @@ -10,6 +10,7 @@ compile_error!( pub mod generated; pub mod crypto; +pub mod display; pub mod execution; pub mod primitive; pub mod protocol; diff --git a/crates/astria-core/src/primitive/mod.rs b/crates/astria-core/src/primitive/mod.rs index a3a6d96c3f..f0044fc672 100644 --- a/crates/astria-core/src/primitive/mod.rs +++ b/crates/astria-core/src/primitive/mod.rs @@ -1 +1,2 @@ +pub use pbjson_types::Timestamp; pub mod v1; diff --git a/crates/astria-core/src/protocol/genesis/v1alpha1.rs b/crates/astria-core/src/protocol/genesis/v1alpha1.rs index d9b7354370..868ff8c67e 100644 --- a/crates/astria-core/src/protocol/genesis/v1alpha1.rs +++ b/crates/astria-core/src/protocol/genesis/v1alpha1.rs @@ -17,14 +17,8 @@ use crate::{ Bech32m, }, slinky::{ - market_map::v1::{ - GenesisState as MarketMapGenesisState, - GenesisStateError as MarketMapGenesisStateError, - }, - oracle::v1::{ - GenesisState as OracleGenesisState, - GenesisStateError as OracleGenesisStateError, - }, + market_map, + oracle, }, Protobuf, }; @@ -36,18 +30,18 @@ use crate::{ serde(try_from = "raw::SlinkyGenesis", into = "raw::SlinkyGenesis") )] pub struct SlinkyGenesis { - market_map: MarketMapGenesisState, - oracle: OracleGenesisState, + market_map: market_map::v1::GenesisState, + oracle: oracle::v1::GenesisState, } impl SlinkyGenesis { #[must_use] - pub fn market_map(&self) -> &MarketMapGenesisState { + pub fn market_map(&self) -> &market_map::v1::GenesisState { &self.market_map } #[must_use] - pub fn oracle(&self) -> &OracleGenesisState { + pub fn oracle(&self) -> &oracle::v1::GenesisState { &self.oracle } } @@ -65,13 +59,14 @@ impl Protobuf for SlinkyGenesis { .as_ref() .ok_or_else(|| Self::Error::field_not_set("market_map")) .and_then(|market_map| { - MarketMapGenesisState::try_from_raw_ref(market_map).map_err(Self::Error::market_map) + market_map::v1::GenesisState::try_from_raw_ref(market_map) + .map_err(Self::Error::market_map) })?; let oracle = oracle .as_ref() .ok_or_else(|| Self::Error::field_not_set("oracle")) .and_then(|oracle| { - OracleGenesisState::try_from_raw_ref(oracle).map_err(Self::Error::oracle) + oracle::v1::GenesisState::try_from_raw_ref(oracle).map_err(Self::Error::oracle) })?; Ok(Self { market_map, @@ -116,13 +111,13 @@ impl SlinkyGenesisError { }) } - fn market_map(source: MarketMapGenesisStateError) -> Self { + fn market_map(source: market_map::v1::GenesisStateError) -> Self { Self(SlinkyGenesisErrorKind::MarketMap { source, }) } - fn oracle(source: OracleGenesisStateError) -> Self { + fn oracle(source: oracle::v1::GenesisStateError) -> Self { Self(SlinkyGenesisErrorKind::Oracle { source, }) @@ -135,9 +130,13 @@ enum SlinkyGenesisErrorKind { #[error("field was not set: `{name}`")] FieldNotSet { name: &'static str }, #[error("`market_map` field was invalid")] - MarketMap { source: MarketMapGenesisStateError }, + MarketMap { + source: market_map::v1::GenesisStateError, + }, #[error("`oracle` field was invalid")] - Oracle { source: OracleGenesisStateError }, + Oracle { + source: oracle::v1::GenesisStateError, + }, } /// The genesis state of Astria's Sequencer. @@ -351,7 +350,7 @@ impl Protobuf for GenesisAppState { .as_ref() .ok_or_else(|| Self::Error::field_not_set("market_map")) .and_then(|market_map| { - MarketMapGenesisState::try_from_raw(market_map.clone()) + market_map::v1::GenesisState::try_from_raw(market_map.clone()) .map_err(Self::Error::market_map) })?; @@ -360,7 +359,7 @@ impl Protobuf for GenesisAppState { .as_ref() .ok_or_else(|| Self::Error::field_not_set("oracle")) .and_then(|oracle| { - OracleGenesisState::try_from_raw(oracle.clone()).map_err(Self::Error::oracle) + oracle::v1::GenesisState::try_from_raw(oracle.clone()).map_err(Self::Error::oracle) })?; let this = Self { @@ -493,13 +492,13 @@ impl GenesisAppStateError { }) } - fn market_map(source: MarketMapGenesisStateError) -> Self { + fn market_map(source: market_map::v1::GenesisStateError) -> Self { Self(GenesisAppStateErrorKind::MarketMap { source, }) } - fn oracle(source: OracleGenesisStateError) -> Self { + fn oracle(source: oracle::v1::GenesisStateError) -> Self { Self(GenesisAppStateErrorKind::Oracle { source, }) @@ -532,9 +531,13 @@ enum GenesisAppStateErrorKind { #[error("`native_asset_base_denomination` field was invalid")] NativeAssetBaseDenomination { source: ParseTracePrefixedError }, #[error("`market_map` field was invalid")] - MarketMap { source: MarketMapGenesisStateError }, + MarketMap { + source: market_map::v1::GenesisStateError, + }, #[error("`oracle` field was invalid")] - Oracle { source: OracleGenesisStateError }, + Oracle { + source: oracle::v1::GenesisStateError, + }, } #[derive(Debug, thiserror::Error)] @@ -830,9 +833,12 @@ mod tests { use super::*; use crate::{ primitive::v1::Address, - slinky::market_map::v1::{ - MarketMap, - Params, + slinky::{ + market_map::v1::{ + MarketMap, + Params, + }, + types::v1::CurrencyPairId, }, }; @@ -912,7 +918,7 @@ mod tests { }), slinky: Some( SlinkyGenesis { - market_map: MarketMapGenesisState { + market_map: market_map::v1::GenesisState { market_map: MarketMap { markets: IndexMap::new(), }, @@ -922,9 +928,9 @@ mod tests { admin: alice(), }, }, - oracle: OracleGenesisState { + oracle: oracle::v1::GenesisState { currency_pair_genesis: vec![], - next_id: 0, + next_id: CurrencyPairId::new(0), }, } .into_raw(), diff --git a/crates/astria-core/src/slinky/abci.rs b/crates/astria-core/src/slinky/abci.rs index f6c755a284..cb5aa77686 100644 --- a/crates/astria-core/src/slinky/abci.rs +++ b/crates/astria-core/src/slinky/abci.rs @@ -1,32 +1,74 @@ pub mod v1 { + use bytes::Bytes; use indexmap::IndexMap; - use crate::generated::astria_vendored::slinky::abci::v1 as raw; + use crate::{ + generated::astria_vendored::slinky::abci::v1 as raw, + slinky::types::v1::{ + CurrencyPairId, + Price, + }, + }; + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct OracleVoteExtensionError(#[from] OracleVoteExtensionErrorKind); + + #[derive(Debug, thiserror::Error)] + #[error("failed to validate astria_vendored.slinky.abci.v1.OracleVoteExtension")] + enum OracleVoteExtensionErrorKind { + #[error("failed decoding price value in .prices field for key `{id}`")] + DecodePrice { + id: u64, + source: crate::slinky::types::v1::DecodePriceError, + }, + } #[derive(Debug, Clone, PartialEq, Eq)] pub struct OracleVoteExtension { - pub prices: IndexMap, + pub prices: IndexMap, } impl OracleVoteExtension { - #[must_use] - pub fn from_raw(raw: raw::OracleVoteExtension) -> Self { - Self { - prices: raw.prices.into_iter().collect(), - } + /// Converts an on-wire [`raw::OracleVoteExtension`] to a validated domain type + /// [`OracleVoteExtension`]. + /// + /// # Errors + /// Returns an error if a value in the `.prices` map could not be validated. + pub fn try_from_raw( + raw: raw::OracleVoteExtension, + ) -> Result { + let prices = raw + .prices + .into_iter() + .map(|(id, price)| { + let price = Price::try_from(price).map_err(|source| { + OracleVoteExtensionErrorKind::DecodePrice { + id, + source, + } + })?; + Ok::<_, OracleVoteExtensionErrorKind>((CurrencyPairId::new(id), price)) + }) + .collect::>()?; + Ok(Self { + prices, + }) } #[must_use] pub fn into_raw(self) -> raw::OracleVoteExtension { - raw::OracleVoteExtension { - prices: self.prices.into_iter().collect(), + fn encode_price(input: Price) -> Bytes { + Bytes::copy_from_slice(&input.get().to_be_bytes()) } - } - } - impl From for OracleVoteExtension { - fn from(raw: raw::OracleVoteExtension) -> Self { - Self::from_raw(raw) + raw::OracleVoteExtension { + prices: self + .prices + .into_iter() + .map(|(id, price)| (id.get(), encode_price(price))) + .collect(), + } } } } diff --git a/crates/astria-core/src/slinky/market_map.rs b/crates/astria-core/src/slinky/market_map.rs index 1abf7fe955..3dd9f85511 100644 --- a/crates/astria-core/src/slinky/market_map.rs +++ b/crates/astria-core/src/slinky/market_map.rs @@ -9,7 +9,10 @@ pub mod v1 { Address, AddressError, }, - slinky::types::v1::CurrencyPair, + slinky::types::v1::{ + CurrencyPair, + CurrencyPairError, + }, Protobuf, }; @@ -350,9 +353,11 @@ pub mod v1 { /// - if the `currency_pair` field is missing /// - if the `currency_pair` field is invalid pub fn try_from_raw(raw: raw::Ticker) -> Result { - let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { - return Err(TickerError::missing_currency_pair()); - }; + let currency_pair = raw + .currency_pair + .ok_or_else(|| TickerError::field_not_set("currency_pair"))? + .try_into() + .map_err(TickerError::invalid_currency_pair)?; Ok(Self { currency_pair, @@ -377,19 +382,33 @@ pub mod v1 { #[derive(Debug, thiserror::Error)] #[error(transparent)] - pub struct TickerError(TickerErrorKind); + pub struct TickerError(#[from] TickerErrorKind); impl TickerError { #[must_use] - pub fn missing_currency_pair() -> Self { - Self(TickerErrorKind::MissingCurrencyPair) + fn field_not_set(name: &'static str) -> Self { + TickerErrorKind::FieldNotSet { + name, + } + .into() + } + + #[must_use] + fn invalid_currency_pair(source: CurrencyPairError) -> Self { + TickerErrorKind::InvalidCurrencyPair { + source, + } + .into() } } #[derive(Debug, thiserror::Error)] + #[error("failed validating wire type `{}`", raw::Ticker::full_name())] enum TickerErrorKind { - #[error("missing currency pair")] - MissingCurrencyPair, + #[error("required field not set: .{name}")] + FieldNotSet { name: &'static str }, + #[error("field `.currency_pair` was invalid")] + InvalidCurrencyPair { source: CurrencyPairError }, } #[cfg_attr( @@ -427,9 +446,11 @@ pub mod v1 { /// /// - if the `normalize_by_pair` field is missing pub fn try_from_raw(raw: raw::ProviderConfig) -> Result { - let Some(normalize_by_pair) = raw.normalize_by_pair.map(CurrencyPair::from_raw) else { - return Err(ProviderConfigError::missing_normalize_by_pair()); - }; + let normalize_by_pair = raw + .normalize_by_pair + .ok_or_else(|| ProviderConfigError::field_not_set("normalize_by_pair"))? + .try_into() + .map_err(ProviderConfigError::invalid_normalize_by_pair)?; Ok(Self { name: raw.name, off_chain_ticker: raw.off_chain_ticker, @@ -453,19 +474,32 @@ pub mod v1 { #[derive(Debug, thiserror::Error)] #[error(transparent)] - pub struct ProviderConfigError(ProviderConfigErrorKind); + pub struct ProviderConfigError(#[from] ProviderConfigErrorKind); impl ProviderConfigError { #[must_use] - pub fn missing_normalize_by_pair() -> Self { - Self(ProviderConfigErrorKind::MissingNormalizeByPair) + fn field_not_set(name: &'static str) -> Self { + ProviderConfigErrorKind::FieldNotSet { + name, + } + .into() + } + + fn invalid_normalize_by_pair(source: CurrencyPairError) -> Self { + ProviderConfigErrorKind::InvalidNormalizeByPair { + source, + } + .into() } } #[derive(Debug, thiserror::Error)] + #[error("failed validating wire type `{}`", raw::ProviderConfig::full_name())] enum ProviderConfigErrorKind { - #[error("missing normalize by pair")] - MissingNormalizeByPair, + #[error("required field not set: .{name}")] + FieldNotSet { name: &'static str }, + #[error("field `.normalize_by_pair` was invalid")] + InvalidNormalizeByPair { source: CurrencyPairError }, } #[cfg_attr( diff --git a/crates/astria-core/src/slinky/mod.rs b/crates/astria-core/src/slinky/mod.rs index 619f008c3b..62eac09078 100644 --- a/crates/astria-core/src/slinky/mod.rs +++ b/crates/astria-core/src/slinky/mod.rs @@ -1,4 +1,5 @@ pub mod abci; pub mod market_map; pub mod oracle; +pub mod service; pub mod types; diff --git a/crates/astria-core/src/slinky/oracle.rs b/crates/astria-core/src/slinky/oracle.rs index 2be324c1a7..50ca26e9d5 100644 --- a/crates/astria-core/src/slinky/oracle.rs +++ b/crates/astria-core/src/slinky/oracle.rs @@ -3,7 +3,14 @@ pub mod v1 { use crate::{ generated::astria_vendored::slinky::oracle::v1 as raw, - slinky::types::v1::CurrencyPair, + slinky::types::v1::{ + CurrencyPair, + CurrencyPairError, + CurrencyPairId, + CurrencyPairNonce, + ParsePriceError, + Price, + }, Protobuf, }; @@ -14,7 +21,7 @@ pub mod v1 { )] #[derive(Debug, Clone)] pub struct QuotePrice { - pub price: u128, + pub price: Price, pub block_timestamp: Timestamp, pub block_height: u64, } @@ -41,10 +48,7 @@ pub mod v1 { /// - if the `price` field is invalid /// - if the `block_timestamp` field is missing pub fn try_from_raw(raw: raw::QuotePrice) -> Result { - let price = raw - .price - .parse() - .map_err(QuotePriceError::price_parse_error)?; + let price = raw.price.parse().map_err(QuotePriceError::parse_price)?; let Some(block_timestamp) = raw.block_timestamp else { return Err(QuotePriceError::missing_block_timestamp()); }; @@ -72,20 +76,23 @@ pub mod v1 { impl QuotePriceError { #[must_use] - pub fn price_parse_error(err: std::num::ParseIntError) -> Self { - Self(QuotePriceErrorKind::PriceParseError(err)) + fn parse_price(source: ParsePriceError) -> Self { + Self(QuotePriceErrorKind::Price { + source, + }) } #[must_use] - pub fn missing_block_timestamp() -> Self { + fn missing_block_timestamp() -> Self { Self(QuotePriceErrorKind::MissingBlockTimestamp) } } #[derive(Debug, thiserror::Error)] + #[error("failed to validate wire type `{}`", raw::QuotePrice::full_name())] enum QuotePriceErrorKind { - #[error("failed to parse price")] - PriceParseError(#[from] std::num::ParseIntError), + #[error("failed to parse `price` field")] + Price { source: ParsePriceError }, #[error("missing block timestamp")] MissingBlockTimestamp, } @@ -98,8 +105,8 @@ pub mod v1 { #[derive(Debug, Clone)] pub struct CurrencyPairState { pub price: QuotePrice, - pub nonce: u64, - pub id: u64, + pub nonce: CurrencyPairNonce, + pub id: CurrencyPairId, } impl TryFrom for CurrencyPairState { @@ -132,8 +139,8 @@ pub mod v1 { else { return Err(CurrencyPairStateError::missing_price()); }; - let nonce = raw.nonce; - let id = raw.id; + let nonce = CurrencyPairNonce::new(raw.nonce); + let id = CurrencyPairId::new(raw.id); Ok(Self { price, nonce, @@ -145,8 +152,8 @@ pub mod v1 { pub fn into_raw(self) -> raw::CurrencyPairState { raw::CurrencyPairState { price: Some(self.price.into_raw()), - nonce: self.nonce, - id: self.id, + nonce: self.nonce.get(), + id: self.id.get(), } } } @@ -157,12 +164,12 @@ pub mod v1 { impl CurrencyPairStateError { #[must_use] - pub fn missing_price() -> Self { + fn missing_price() -> Self { Self(CurrencyPairStateErrorKind::MissingPrice) } #[must_use] - pub fn quote_price_parse_error(err: QuotePriceError) -> Self { + fn quote_price_parse_error(err: QuotePriceError) -> Self { Self(CurrencyPairStateErrorKind::QuotePriceParseError(err)) } } @@ -187,8 +194,8 @@ pub mod v1 { pub struct CurrencyPairGenesis { pub currency_pair: CurrencyPair, pub currency_pair_price: QuotePrice, - pub id: u64, - pub nonce: u64, + pub id: CurrencyPairId, + pub nonce: CurrencyPairNonce, } impl TryFrom for CurrencyPairGenesis { @@ -217,17 +224,17 @@ pub mod v1 { } #[must_use] - pub fn id(&self) -> u64 { + pub fn id(&self) -> CurrencyPairId { self.id } #[must_use] - pub fn nonce(&self) -> u64 { + pub fn nonce(&self) -> CurrencyPairNonce { self.nonce } - /// Converts from a raw protobuf `CurrencyPairGenesis` to a native - /// `CurrencyPairGenesis`. + /// Converts from a raw protobuf `raw::CurrencyPairGenesis` to a validated + /// domain type [`CurrencyPairGenesis`]. /// /// # Errors /// @@ -238,19 +245,21 @@ pub mod v1 { pub fn try_from_raw( raw: raw::CurrencyPairGenesis, ) -> Result { - let Some(currency_pair) = raw.currency_pair.map(CurrencyPair::from_raw) else { - return Err(CurrencyPairGenesisError::missing_currency_pair()); - }; - let Some(currency_pair_price) = raw - .currency_pair_price - .map(QuotePrice::try_from_raw) - .transpose() - .map_err(CurrencyPairGenesisError::quote_price_parse_error)? - else { - return Err(CurrencyPairGenesisError::missing_currency_pair_price()); + let currency_pair = raw + .currency_pair + .ok_or_else(|| CurrencyPairGenesisError::field_not_set("currency_pair"))? + .try_into() + .map_err(CurrencyPairGenesisError::currency_pair)?; + let currency_pair_price = { + let wire = raw.currency_pair_price.ok_or_else(|| { + CurrencyPairGenesisError::field_not_set("currency_pair_price") + })?; + QuotePrice::try_from_raw(wire) + .map_err(CurrencyPairGenesisError::currency_pair_price)? }; - let id = raw.id; - let nonce = raw.nonce; + + let id = CurrencyPairId::new(raw.id); + let nonce = CurrencyPairNonce::new(raw.nonce); Ok(Self { currency_pair, currency_pair_price, @@ -264,41 +273,46 @@ pub mod v1 { raw::CurrencyPairGenesis { currency_pair: Some(self.currency_pair.into_raw()), currency_pair_price: Some(self.currency_pair_price.into_raw()), - id: self.id, - nonce: self.nonce, + id: self.id.get(), + nonce: self.nonce.get(), } } } #[derive(Debug, thiserror::Error)] #[error(transparent)] - pub struct CurrencyPairGenesisError(CurrencyPairGenesisErrorKind); + pub struct CurrencyPairGenesisError(#[from] CurrencyPairGenesisErrorKind); impl CurrencyPairGenesisError { #[must_use] - pub fn missing_currency_pair() -> Self { - Self(CurrencyPairGenesisErrorKind::MissingCurrencyPair) + fn field_not_set(name: &'static str) -> Self { + CurrencyPairGenesisErrorKind::FieldNotSet { + name, + } + .into() } - #[must_use] - pub fn missing_currency_pair_price() -> Self { - Self(CurrencyPairGenesisErrorKind::MissingCurrencyPairPrice) + fn currency_pair(source: CurrencyPairError) -> Self { + CurrencyPairGenesisErrorKind::CurrencyPair { + source, + } + .into() } #[must_use] - pub fn quote_price_parse_error(err: QuotePriceError) -> Self { - Self(CurrencyPairGenesisErrorKind::QuotePriceParseError(err)) + fn currency_pair_price(err: QuotePriceError) -> Self { + Self(CurrencyPairGenesisErrorKind::CurrencyPairPrice(err)) } } #[derive(Debug, thiserror::Error)] enum CurrencyPairGenesisErrorKind { - #[error("missing currency pair")] - MissingCurrencyPair, - #[error("missing currency pair price")] - MissingCurrencyPairPrice, - #[error("failed to parse quote price")] - QuotePriceParseError(#[source] QuotePriceError), + #[error("required field not set: .{name}")] + FieldNotSet { name: &'static str }, + #[error("field `.currency_pair` was invalid")] + CurrencyPair { source: CurrencyPairError }, + #[error("field `.currency_pair_price` was invalid")] + CurrencyPairPrice(#[source] QuotePriceError), } #[cfg_attr( @@ -309,7 +323,7 @@ pub mod v1 { #[derive(Debug, Clone)] pub struct GenesisState { pub currency_pair_genesis: Vec, - pub next_id: u64, + pub next_id: CurrencyPairId, } impl TryFrom for GenesisState { @@ -338,7 +352,7 @@ pub mod v1 { .map(CurrencyPairGenesis::try_from_raw) .collect::, _>>() .map_err(GenesisStateError::currency_pair_genesis_parse_error)?; - let next_id = raw.next_id; + let next_id = CurrencyPairId::new(raw.next_id); Ok(Self { currency_pair_genesis, next_id, @@ -357,7 +371,7 @@ pub mod v1 { .map(CurrencyPairGenesis::try_from_raw) .collect::, _>>() .map_err(GenesisStateError::currency_pair_genesis_parse_error)?; - let next_id = raw.next_id; + let next_id = CurrencyPairId::new(raw.next_id); Ok(Self { currency_pair_genesis, next_id, @@ -372,7 +386,7 @@ pub mod v1 { .into_iter() .map(CurrencyPairGenesis::into_raw) .collect(), - next_id: self.next_id, + next_id: self.next_id.get(), } } @@ -384,7 +398,7 @@ pub mod v1 { .into_iter() .map(CurrencyPairGenesis::into_raw) .collect(), - next_id: self.next_id, + next_id: self.next_id.get(), } } } @@ -395,7 +409,7 @@ pub mod v1 { impl GenesisStateError { #[must_use] - pub fn currency_pair_genesis_parse_error(err: CurrencyPairGenesisError) -> Self { + fn currency_pair_genesis_parse_error(err: CurrencyPairGenesisError) -> Self { Self(GenesisStateErrorKind::CurrencyPairGenesisParseError(err)) } } diff --git a/crates/astria-core/src/slinky/service.rs b/crates/astria-core/src/slinky/service.rs new file mode 100644 index 0000000000..0cca9ffe10 --- /dev/null +++ b/crates/astria-core/src/slinky/service.rs @@ -0,0 +1,85 @@ +pub mod v1 { + use indexmap::IndexMap; + + use crate::{ + generated::astria_vendored::slinky::service::v1 as raw, + slinky::types::v1::{ + CurrencyPair, + CurrencyPairParseError, + ParsePriceError, + Price, + }, + }; + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct QueryPriceResponseError(#[from] QueryPriceResponseErrorKind); + + #[derive(Debug, thiserror::Error)] + #[error( + "failed validating wire type {}", + raw::QueryPriceResponseError::full_name() + )] + enum QueryPriceResponseErrorKind { + #[error("failed to parse key `{input}` in `.prices` field as currency pair")] + ParseCurrencyPair { + input: String, + source: CurrencyPairParseError, + }, + #[error("failed to parse value `{input}` in `.prices` field at key `{key}` as price")] + ParsePrice { + input: String, + key: String, + source: ParsePriceError, + }, + } + + pub struct QueryPricesResponse { + pub prices: IndexMap, + pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + } + + impl QueryPricesResponse { + /// Converts the on-wire [`raw::QueryPricesReponse`] to a validated domain type + /// [`QueryPricesResponse`]. + /// + /// # Errors + /// Returns an error if: + /// + A key in the `.prices` map could not be parsed as a [`CurrencyPair`]. + /// + A value in the `.prices` map could not be parsed as [`Price`]. + pub fn try_from_raw( + wire: raw::QueryPricesResponse, + ) -> Result { + let raw::QueryPricesResponse { + prices, + timestamp, + } = wire; + let prices = prices + .into_iter() + .map(|(key, value)| { + let currency_pair = match key.parse() { + Err(source) => { + return Err(QueryPriceResponseErrorKind::ParseCurrencyPair { + input: key, + source, + }); + } + Ok(parsed) => parsed, + }; + let price = value.parse().map_err(move |source| { + QueryPriceResponseErrorKind::ParsePrice { + input: value, + key, + source, + } + })?; + Ok((currency_pair, price)) + }) + .collect::>()?; + Ok(Self { + prices, + timestamp, + }) + } + } +} diff --git a/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/slinky/types.rs index dc62638227..d2891192f5 100644 --- a/crates/astria-core/src/slinky/types.rs +++ b/crates/astria-core/src/slinky/types.rs @@ -1,48 +1,244 @@ pub mod v1 { + use std::{ + fmt::Display, + num::ParseIntError, + str::FromStr, + }; + + use bytes::Bytes; + use crate::generated::astria_vendored::slinky::types::v1 as raw; + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub struct Price(u128); + + impl Price { + #[must_use] + pub fn new(value: u128) -> Self { + Self(value) + } + + #[must_use] + pub fn get(self) -> u128 { + self.0 + } + } + + impl Price { + pub fn checked_add(self, rhs: Self) -> Option { + self.get().checked_add(rhs.get()).map(Self) + } + + pub fn checked_div(self, rhs: u128) -> Option { + self.get().checked_div(rhs).map(Self) + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct ParsePriceError(#[from] ParseIntError); + + impl FromStr for Price { + type Err = ParsePriceError; + + fn from_str(s: &str) -> Result { + s.parse().map(Self::new).map_err(Into::into) + } + } + + impl Display for Price { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + #[derive(Debug, thiserror::Error)] + #[error("failed decoding `{}` as u128 integer", crate::display::base64(.input))] + pub struct DecodePriceError { + input: Bytes, + } + + impl TryFrom for Price { + type Error = DecodePriceError; + + fn try_from(input: Bytes) -> Result { + // throw away the error because it does not contain extra information. + let be_bytes = <[u8; 16]>::try_from(&*input).map_err(|_| Self::Error { + input, + })?; + Ok(Price::new(u128::from_be_bytes(be_bytes))) + } + } + + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Base(String); + + impl Display for Base { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + #[derive(Debug, thiserror::Error)] + #[error( + "failed to parse input `{input}` as base part of currency pair; only ascii alpha \ + characters are permitted" + )] + pub struct ParseBaseError { + input: String, + } + + impl FromStr for Base { + type Err = ParseBaseError; + + fn from_str(s: &str) -> Result { + static REGEX: std::sync::OnceLock = std::sync::OnceLock::new(); + fn get_regex() -> &'static regex::Regex { + REGEX.get_or_init(|| regex::Regex::new(r"^[a-zA-Z]+$").expect("valid regex")) + } + // allocating here because the string will always be allocated on both branches. + // TODO: check if this string can be represented by a stack-optimized alternative + // like ecow, compact_str, or similar. + let input = s.to_string(); + if get_regex().find(s).is_none() { + return Err(Self::Err { + input, + }); + } + Ok(Self(input)) + } + } + + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Quote(String); + + impl Display for Quote { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + #[derive(Debug, thiserror::Error)] + #[error( + "failed to parse input `{input}` as quote part of currency pair; only ascii alpha \ + characters are permitted" + )] + pub struct ParseQuoteError { + input: String, + } + + impl FromStr for Quote { + type Err = ParseQuoteError; + + fn from_str(s: &str) -> Result { + static REGEX: std::sync::OnceLock = std::sync::OnceLock::new(); + fn get_regex() -> &'static regex::Regex { + REGEX.get_or_init(|| regex::Regex::new(r"^[a-zA-Z]+$").expect("valid regex")) + } + // allocating here because the string will always be allocated on both branches. + // TODO: check if this string can be represented by a stack-optimized alternative + // like ecow, compact_str, or similar. + let input = s.to_string(); + if get_regex().find(s).is_none() { + return Err(Self::Err { + input, + }); + } + Ok(Self(input)) + } + } + + #[derive(Debug, thiserror::Error)] + #[error(transparent)] + pub struct CurrencyPairError(#[from] CurrencyPairErrorKind); + + #[derive(Debug, thiserror::Error)] + #[error("failed validating wire type `{}`", CurrencyPair::full_name())] + enum CurrencyPairErrorKind { + #[error("invalid field `.base`")] + ParseBase { source: ParseBaseError }, + #[error("invalid field `.quote`")] + ParseQuote { source: ParseQuoteError }, + } + #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), - serde(from = "raw::CurrencyPair", into = "raw::CurrencyPair") + serde(try_from = "raw::CurrencyPair", into = "raw::CurrencyPair") )] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CurrencyPair { - base: String, - quote: String, + base: Base, + quote: Quote, } impl CurrencyPair { + #[must_use] + pub fn from_parts(base: Base, quote: Quote) -> Self { + Self { + base, + quote, + } + } + + /// Returns the `(base, quote)` pair that makes up this [`CurrencyPair`]. + #[must_use] + pub fn into_parts(self) -> (String, String) { + (self.base.0, self.quote.0) + } + #[must_use] pub fn base(&self) -> &str { - &self.base + &self.base.0 } #[must_use] pub fn quote(&self) -> &str { - &self.quote + &self.quote.0 } - #[must_use] - pub fn from_raw(raw: raw::CurrencyPair) -> Self { - Self { - base: raw.base, - quote: raw.quote, - } + /// Converts a on-wire [`raw::CurrencyPair`] to a validated domain type [`CurrencyPair`]. + /// + /// # Errors + + /// Returns an error if: + /// + The `.base` field could not be parsed as a [`Base`]. + /// + The `.quote` field could not be parsed as [`Quote`]. + // allow reason: symmetry with all other `try_from_raw` methods that take ownership + #[allow(clippy::needless_pass_by_value)] + pub fn try_from_raw(raw: raw::CurrencyPair) -> Result { + let base = raw + .base + .parse() + .map_err(|source| CurrencyPairErrorKind::ParseBase { + source, + })?; + let quote = raw + .quote + .parse() + .map_err(|source| CurrencyPairErrorKind::ParseQuote { + source, + })?; + Ok(Self { + base, + quote, + }) } #[must_use] pub fn into_raw(self) -> raw::CurrencyPair { raw::CurrencyPair { - base: self.base, - quote: self.quote, + base: self.base.0, + quote: self.quote.0, } } } - impl From for CurrencyPair { - fn from(raw: raw::CurrencyPair) -> Self { - Self::from_raw(raw) + impl TryFrom for CurrencyPair { + type Error = CurrencyPairError; + + fn try_from(raw: raw::CurrencyPair) -> Result { + Self::try_from_raw(raw) } } @@ -76,8 +272,8 @@ pub mod v1 { .as_str(); Ok(Self { - base: base.to_string(), - quote: quote.to_string(), + base: Base(base.to_string()), + quote: Quote(quote.to_string()), }) } } @@ -94,12 +290,60 @@ pub mod v1 { impl CurrencyPairParseError { #[must_use] - pub fn invalid_currency_pair_string(s: &str) -> Self { + fn invalid_currency_pair_string(s: &str) -> Self { Self(CurrencyPairParseErrorKind::InvalidCurrencyPairString( s.to_string(), )) } } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct CurrencyPairId(u64); + + impl std::fmt::Display for CurrencyPairId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + impl CurrencyPairId { + #[must_use] + pub fn new(value: u64) -> Self { + Self(value) + } + + #[must_use] + pub fn get(self) -> u64 { + self.0 + } + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct CurrencyPairNonce(u64); + + impl std::fmt::Display for CurrencyPairNonce { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + impl CurrencyPairNonce { + #[must_use] + pub fn new(value: u64) -> Self { + Self(value) + } + + #[must_use] + pub fn get(self) -> u64 { + self.0 + } + + #[must_use] + pub fn increment(self) -> Option { + let new_nonce = self.get().checked_add(1)?; + Some(Self::new(new_nonce)) + } + } } #[cfg(test)] diff --git a/crates/astria-sequencer-utils/Cargo.toml b/crates/astria-sequencer-utils/Cargo.toml index 468c789b87..359bfe2827 100644 --- a/crates/astria-sequencer-utils/Cargo.toml +++ b/crates/astria-sequencer-utils/Cargo.toml @@ -20,7 +20,6 @@ colour = "2.1.0" ethers-core = "2.0.14" hex = { workspace = true } indenter = "0.3.3" -indexmap = { workspace = true } itertools = { workspace = true } pbjson-types = { workspace = true } prost = { workspace = true } @@ -31,6 +30,7 @@ serde_json = { workspace = true } astria-core = { path = "../astria-core", features = ["brotli", "serde"] } astria-eyre = { path = "../astria-eyre" } astria-merkle = { path = "../astria-merkle" } +maplit = "1.0.2" [dev-dependencies] assert_cmd = "2.0.14" diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index c84b94c33b..8d58c64eb1 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -6,7 +6,19 @@ use std::{ use astria_core::{ generated::{ - astria_vendored::slinky::types::v1::CurrencyPair as RawCurrencyPair, + astria_vendored::slinky::{ + marketmap, + marketmap::v1::{ + Market, + MarketMap, + }, + oracle, + oracle::v1::{ + CurrencyPairGenesis, + QuotePrice, + }, + types::v1::CurrencyPair, + }, protocol::genesis::v1alpha1::{ AddressPrefixes, IbcParameters, @@ -18,28 +30,12 @@ use astria_core::{ Fees, GenesisAppState, }, - slinky::{ - market_map::v1::{ - GenesisState as MarketMapGenesisState, - Market, - MarketMap, - Params, - ProviderConfig, - Ticker, - }, - oracle::v1::{ - CurrencyPairGenesis, - GenesisState as OracleGenesisState, - QuotePrice, - }, - }, Protobuf, }; use astria_eyre::eyre::{ Result, WrapErr as _, }; -use indexmap::IndexMap; const ASTRIA_ADDRESS_PREFIX: &str = "astria"; @@ -67,63 +63,64 @@ fn charlie() -> Address { .unwrap() } -fn genesis_state_markets() -> IndexMap { - let mut markets = IndexMap::new(); - markets.insert( - "BTC/USD".to_string(), - Market { - ticker: Ticker { - currency_pair: RawCurrencyPair { +fn genesis_state_markets() -> MarketMap { + use astria_core::generated::astria_vendored::slinky::marketmap::v1::{ + ProviderConfig, + Ticker, + }; + use maplit::{ + btreemap, + convert_args, + }; + let markets = convert_args!(btreemap!( + "BTC/USD" => Market { + ticker: Some(Ticker { + currency_pair: Some(CurrencyPair { base: "BTC".to_string(), quote: "USD".to_string(), - } - .into(), + }), decimals: 8, min_provider_count: 3, enabled: true, metadata_json: String::new(), - }, + }), provider_configs: vec![ProviderConfig { name: "coingecko_api".to_string(), off_chain_ticker: "bitcoin/usd".to_string(), - normalize_by_pair: RawCurrencyPair { + normalize_by_pair: Some(CurrencyPair { base: "USDT".to_string(), quote: "USD".to_string(), - } - .into(), + }), invert: false, metadata_json: String::new(), }], }, - ); - markets.insert( - "ETH/USD".to_string(), - Market { - ticker: Ticker { - currency_pair: RawCurrencyPair { + "ETH/USD" => Market { + ticker: Some(Ticker { + currency_pair: Some(CurrencyPair { base: "ETH".to_string(), quote: "USD".to_string(), - } - .into(), + }), decimals: 8, min_provider_count: 3, enabled: true, metadata_json: String::new(), - }, + }), provider_configs: vec![ProviderConfig { name: "coingecko_api".to_string(), off_chain_ticker: "ethereum/usd".to_string(), - normalize_by_pair: RawCurrencyPair { + normalize_by_pair: Some(CurrencyPair { base: "USDT".to_string(), quote: "USD".to_string(), - } - .into(), + }), invert: false, metadata_json: String::new(), }], }, - ); - markets + )); + MarketMap { + markets, + } } fn accounts() -> Vec { @@ -180,60 +177,52 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1alpha1: slinky: Some( astria_core::generated::protocol::genesis::v1alpha1::SlinkyGenesis { market_map: Some( - MarketMapGenesisState { - market_map: MarketMap { - markets: genesis_state_markets(), - }, + astria_core::generated::astria_vendored::slinky::marketmap::v1::GenesisState { + market_map: Some(genesis_state_markets()), last_updated: 0, - params: Params { - market_authorities: vec![alice(), bob()], - admin: alice(), - }, - } - .into_raw(), - ), - oracle: Some( - OracleGenesisState { - currency_pair_genesis: vec![ - CurrencyPairGenesis { - id: 0, - nonce: 0, - currency_pair_price: QuotePrice { - price: 5_834_065_777, - block_height: 0, - block_timestamp: pbjson_types::Timestamp { - seconds: 1_720_122_395, - nanos: 0, - }, - }, - currency_pair: RawCurrencyPair { - base: "BTC".to_string(), - quote: "USD".to_string(), - } - .into(), - }, - CurrencyPairGenesis { - id: 1, - nonce: 0, - currency_pair_price: QuotePrice { - price: 3_138_872_234, - block_height: 0, - block_timestamp: pbjson_types::Timestamp { - seconds: 1_720_122_395, - nanos: 0, - }, - }, - currency_pair: RawCurrencyPair { - base: "ETH".to_string(), - quote: "USD".to_string(), - } - .into(), - }, - ], - next_id: 2, - } - .into_raw(), + params: Some(marketmap::v1::Params { + market_authorities: vec![alice().to_string(), bob().to_string()], + admin: alice().to_string(), + }), + }, ), + oracle: Some(oracle::v1::GenesisState { + currency_pair_genesis: vec![ + CurrencyPairGenesis { + id: 0, + nonce: 0, + currency_pair_price: Some(QuotePrice { + price: 5_834_065_777_u128.to_string(), + block_height: 0, + block_timestamp: Some(pbjson_types::Timestamp { + seconds: 1_720_122_395, + nanos: 0, + }), + }), + currency_pair: Some(CurrencyPair { + base: "BTC".to_string(), + quote: "USD".to_string(), + }), + }, + CurrencyPairGenesis { + id: 1, + nonce: 0, + currency_pair_price: Some(QuotePrice { + price: 3_138_872_234_u128.to_string(), + block_height: 0, + block_timestamp: Some(pbjson_types::Timestamp { + seconds: 1_720_122_395, + nanos: 0, + }), + }), + currency_pair: Some(CurrencyPair { + base: "ETH".to_string(), + quote: "USD".to_string(), + }), + }, + ], + next_id: 2, + }), }, ), } diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 3170d57a71..2fb56e9c42 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -111,13 +111,9 @@ pub(crate) fn proto_genesis_state() IbcParameters, SlinkyGenesis, }, - slinky::{ - market_map::v1::{ - GenesisState as MarketMapGenesisState, - MarketMap, - Params, - }, - oracle::v1::GenesisState as OracleGenesisState, + slinky::market_map::v1::{ + MarketMap, + Params, }, }; @@ -141,7 +137,7 @@ pub(crate) fn proto_genesis_state() fees: Some(default_fees().to_raw()), slinky: Some(SlinkyGenesis { market_map: Some( - MarketMapGenesisState { + astria_core::slinky::market_map::v1::GenesisState { market_map: MarketMap { markets: IndexMap::new(), }, @@ -154,11 +150,10 @@ pub(crate) fn proto_genesis_state() .into_raw(), ), oracle: Some( - OracleGenesisState { + astria_core::generated::astria_vendored::slinky::oracle::v1::GenesisState { currency_pair_genesis: vec![], next_id: 0, - } - .into_raw(), + }, ), }), } diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index be6ef26089..5d47a78859 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -12,13 +12,16 @@ use astria_core::{ service::v1::{ oracle_client::OracleClient, QueryPricesRequest, - QueryPricesResponse, }, }, slinky::{ abci::v1::OracleVoteExtension, oracle::v1::QuotePrice, - types::v1::CurrencyPair, + service::v1::QueryPricesResponse, + types::v1::{ + CurrencyPair, + Price, + }, }, }; use indexmap::IndexMap; @@ -35,7 +38,9 @@ use tendermint_proto::google::protobuf::Timestamp; use tonic::transport::Channel; use tracing::{ debug, - trace, + info, + instrument, + warn, }; use crate::{ @@ -77,19 +82,17 @@ impl Handler { // if we fail to get prices within the timeout duration, we will return an empty vote // extension to ensure liveness. - let prices = match oracle_client.prices(QueryPricesRequest {}).await { - Ok(prices) => prices.into_inner(), + let rsp = match oracle_client.prices(QueryPricesRequest {}).await { + Ok(rsp) => rsp.into_inner(), Err(e) => { - bail!("failed to get prices from oracle sidecar: {e}",); + bail!("failed to get prices from oracle sidecar: {e:#}",); } }; - tracing::debug!( - prices_count = prices.prices.len(), - "got prices from oracle sidecar; transforming prices" - ); - - let oracle_vote_extension = transform_oracle_service_prices(state, prices) + let query_prices_response = + astria_core::slinky::service::v1::QueryPricesResponse::try_from_raw(rsp) + .context("failed to validate prices server response")?; + let oracle_vote_extension = transform_oracle_service_prices(state, query_prices_response) .await .context("failed to transform oracle service prices")?; @@ -146,35 +149,27 @@ async fn verify_vote_extension( } // see https://github.com/skip-mev/slinky/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 +#[instrument(skip_all)] async fn transform_oracle_service_prices( state: &S, - prices: QueryPricesResponse, + rsp: QueryPricesResponse, ) -> anyhow::Result { let mut strategy_prices = IndexMap::new(); - for (currency_pair_id, price_string) in prices.prices { - let currency_pair = currency_pair_id - .parse() - .context("failed to parse currency pair")?; - - // prices are encoded as just a decimal string in the sidecar response - let price: u128 = price_string - .parse() - .context("failed to parse price string")?; - - let Ok(id) = DefaultCurrencyPairStrategy::id(state, ¤cy_pair).await else { - trace!( - currency_pair = currency_pair.to_string(), - "currency pair not found in state; skipping" - ); - continue; + for (currency_pair, price) in rsp.prices { + let id = match DefaultCurrencyPairStrategy::id(state, ¤cy_pair).await { + Ok(Some(id)) => id, + Ok(None) => { + info!(%currency_pair, "currency pair ID not found in state; skipping"); + continue; + } + Err(err) => { + // FIXME: this event can be removed once all instrumented functions + // can generate an error event. + warn!(%currency_pair, "failed to fetch ID for currency pair; cancelling transformation"); + return Err(err).context("failed to fetch currency pair ID"); + } }; - let encoded_price = DefaultCurrencyPairStrategy::get_encoded_price(state, price); - - debug!( - currency_pair = currency_pair.to_string(), - id, price, "transformed price for inclusion in vote extension" - ); - strategy_prices.insert(id, encoded_price.into()); + strategy_prices.insert(id, price); } Ok(OracleVoteExtension { @@ -423,9 +418,11 @@ pub(crate) async fn apply_prices_from_vote_extensions( .map(|vote| { let raw = RawOracleVoteExtension::decode(vote.vote_extension.clone()) .context("failed to decode oracle vote extension")?; - Ok(OracleVoteExtension::from_raw(raw)) + OracleVoteExtension::try_from_raw(raw) + .context("failed to validate oracle vote extension") }) - .collect::>>()?; + .collect::>>() + .context("failed to extract oracle vote extension from extended commit info")?; let prices = aggregate_oracle_votes(state, votes) .await @@ -441,14 +438,8 @@ pub(crate) async fn apply_prices_from_vote_extensions( block_height: height, }; - tracing::debug!( - currency_pair = currency_pair.to_string(), - price = price.price, - "applied price from vote extension" - ); - state - .put_price_for_currency_pair(¤cy_pair, price) + .put_price_for_currency_pair(currency_pair, price) .await .context("failed to put price")?; } @@ -459,7 +450,7 @@ pub(crate) async fn apply_prices_from_vote_extensions( async fn aggregate_oracle_votes( state: &S, votes: Vec, -) -> anyhow::Result> { +) -> anyhow::Result> { // validators are not weighted right now, so we just take the median price for each currency // pair // @@ -467,48 +458,44 @@ async fn aggregate_oracle_votes( // we can implement this later, when we have stake weighting. let mut currency_pair_to_price_list = HashMap::new(); for vote in votes { - for (id, price_bytes) in vote.prices { - if price_bytes.len() > MAXIMUM_PRICE_BYTE_LEN { - continue; - } - + for (id, price) in vote.prices { let Some(currency_pair) = DefaultCurrencyPairStrategy::from_id(state, id) .await .context("failed to get currency pair from id")? else { continue; }; - - let price = DefaultCurrencyPairStrategy::get_decoded_price(state, &price_bytes) - .context("failed to get decoded price")?; currency_pair_to_price_list .entry(currency_pair) - .and_modify(|prices: &mut Vec| prices.push(price)) + .and_modify(|prices: &mut Vec| prices.push(price)) .or_insert(vec![price]); } } let mut prices = HashMap::new(); for (currency_pair, mut price_list) in currency_pair_to_price_list { - let median_price = if price_list.is_empty() { - // price list should not ever be empty, - // as it was only inserted if it had a price - 0 - } else { - price_list.sort_unstable(); - let mid = price_list.len() / 2; - if price_list.len() % 2 == 0 { - let num_to_skip = mid - .checked_sub(1) - .expect("must subtract as the length of the price list is >0"); - price_list.iter().skip(num_to_skip).take(2).sum::() / 2 - } else { - price_list - .get(mid) - .copied() - .expect("must have element as mid < len") + price_list.sort_unstable(); + let midpoint = price_list + .len() + .checked_div(2) + .expect("has a result because RHS is not 0"); + let median_price = if price_list.len() % 2 == 0 { + 'median_from_even: { + let Some(left) = price_list.get(midpoint) else { + break 'median_from_even None; + }; + let Some(right_idx) = midpoint.checked_add(1) else { + break 'median_from_even None; + }; + let Some(right) = price_list.get(right_idx).copied() else { + break 'median_from_even None; + }; + left.checked_add(right).and_then(|sum| sum.checked_div(2)) } - }; + } else { + price_list.get(midpoint).copied() + } + .unwrap_or_else(|| Price::new(0)); prices.insert(currency_pair, median_price); } diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index e42e2b75d5..34835aabda 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -174,12 +174,20 @@ impl OracleService for SequencerServer { let Some(currency_pair) = request.currency_pair else { return Err(Status::invalid_argument("currency pair is required")); }; - let currency_pair = CurrencyPair::from_raw(currency_pair); + let currency_pair = CurrencyPair::try_from_raw(currency_pair).map_err(|e| { + Status::invalid_argument(format!( + "failed to validate currency pair provided in request: {e:#}" + )) + })?; let snapshot = self.storage.latest_snapshot(); let Some(state) = snapshot .get_currency_pair_state(¤cy_pair) .await - .map_err(|e| Status::internal(format!("failed to get state from storage: {e:#}")))? + .map_err(|e| { + Status::internal(format!( + "failed to get currency pair state from storage: {e:#}" + )) + })? else { return Err(Status::not_found("currency pair state not found")); }; @@ -201,8 +209,8 @@ impl OracleService for SequencerServer { Ok(Response::new(GetPriceResponse { price: Some(state.price.into_raw()), - nonce: state.nonce, - id: state.id, + nonce: state.nonce.get(), + id: state.id.get(), decimals: market.ticker.decimals, })) } @@ -259,8 +267,8 @@ impl OracleService for SequencerServer { prices.push(GetPriceResponse { price: Some(state.price.into_raw()), - nonce: state.nonce, - id: state.id, + nonce: state.nonce.get(), + id: state.id.get(), decimals: market.ticker.decimals, }); } diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/slinky/oracle/component.rs index 19c1fa1f8b..17428fb45c 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/slinky/oracle/component.rs @@ -33,8 +33,8 @@ impl Component for OracleComponent { price: currency_pair.currency_pair_price().clone(), }; state - .put_currency_pair_state(currency_pair.currency_pair(), currency_pair_state) - .context("failed to put currency pair")?; + .put_currency_pair_state(currency_pair.currency_pair().clone(), currency_pair_state) + .context("failed to write currency pair to state")?; } state diff --git a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs index 6d0458d4e8..02b36a26d5 100644 --- a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs +++ b/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs @@ -1,8 +1,8 @@ -use anyhow::{ - ensure, - Context as _, +use anyhow::Context as _; +use astria_core::slinky::types::v1::{ + CurrencyPair, + CurrencyPairId, }; -use astria_core::slinky::types::v1::CurrencyPair; use crate::slinky::oracle::state_ext::StateReadExt; @@ -13,31 +13,17 @@ impl DefaultCurrencyPairStrategy { pub(crate) async fn id( state: &S, currency_pair: &CurrencyPair, - ) -> anyhow::Result { + ) -> anyhow::Result> { state.get_currency_pair_id(currency_pair).await } pub(crate) async fn from_id( state: &S, - id: u64, + id: CurrencyPairId, ) -> anyhow::Result> { state.get_currency_pair(id).await } - pub(crate) fn get_encoded_price(_state: &S, price: u128) -> Vec { - price.to_be_bytes().to_vec() - } - - pub(crate) fn get_decoded_price( - _state: &S, - encoded_price: &[u8], - ) -> anyhow::Result { - ensure!(encoded_price.len() == 16, "invalid encoded price length"); - let mut bytes = [0; 16]; - bytes.copy_from_slice(encoded_price); - Ok(u128::from_be_bytes(bytes)) - } - pub(crate) async fn get_max_num_currency_pairs( state: &S, is_proposal_phase: bool, diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs index 839072560e..fde252095f 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs @@ -8,7 +8,6 @@ use std::{ }; use anyhow::{ - bail, Context as _, Result, }; @@ -17,7 +16,11 @@ use astria_core::slinky::{ CurrencyPairState, QuotePrice, }, - types::v1::CurrencyPair, + types::v1::{ + CurrencyPair, + CurrencyPairId, + CurrencyPairNonce, + }, }; use async_trait::async_trait; use borsh::{ @@ -32,6 +35,176 @@ use futures::Stream; use pin_project_lite::pin_project; use tracing::instrument; +mod in_state { + //! Contains all borsh datatypes that are written to/read from state. + + use anyhow::Context as _; + use borsh::{ + BorshDeserialize, + BorshSerialize, + }; + + #[derive(BorshSerialize, BorshDeserialize, Debug)] + pub(super) struct CurrencyPairId(pub(super) u64); + + impl From for super::CurrencyPairId { + fn from(value: CurrencyPairId) -> Self { + Self::new(value.0) + } + } + + impl From for CurrencyPairId { + fn from(value: super::CurrencyPairId) -> Self { + Self(value.get()) + } + } + + #[derive(BorshSerialize, BorshDeserialize, Debug)] + pub(super) struct CurrencyPairNonce(pub(super) u64); + + impl From for super::CurrencyPairNonce { + fn from(value: CurrencyPairNonce) -> Self { + Self::new(value.0) + } + } + + impl From for CurrencyPairNonce { + fn from(value: super::CurrencyPairNonce) -> Self { + Self(value.get()) + } + } + + #[derive(BorshSerialize, BorshDeserialize, Debug)] + pub(super) struct CurrencyPair { + base: String, + quote: String, + } + + impl TryFrom for super::CurrencyPair { + type Error = anyhow::Error; + + fn try_from(value: CurrencyPair) -> anyhow::Result { + Ok(Self::from_parts( + value.base.parse().with_context(|| { + format!( + "failed to parse state-fetched `{}` as currency pair base", + value.base + ) + })?, + value.quote.parse().with_context(|| { + format!( + "failed to parse state-fetched `{}` as currency pair quote", + value.quote + ) + })?, + )) + } + } + + impl From for CurrencyPair { + fn from(value: super::CurrencyPair) -> Self { + let (base, quote) = value.into_parts(); + Self { + base, + quote, + } + } + } + + #[derive(Debug, BorshSerialize, BorshDeserialize)] + struct Timestamp { + seconds: i64, + nanos: i32, + } + + impl From for Timestamp { + fn from(value: astria_core::primitive::Timestamp) -> Self { + Self { + seconds: value.seconds, + nanos: value.nanos, + } + } + } + + impl From for astria_core::primitive::Timestamp { + fn from(value: Timestamp) -> Self { + Self { + seconds: value.seconds, + nanos: value.nanos, + } + } + } + + #[derive(Debug, BorshSerialize, BorshDeserialize)] + struct Price(u128); + + impl From for Price { + fn from(value: astria_core::slinky::types::v1::Price) -> Self { + Self(value.get()) + } + } + + impl From for astria_core::slinky::types::v1::Price { + fn from(value: Price) -> Self { + Self::new(value.0) + } + } + + #[derive(Debug, BorshSerialize, BorshDeserialize)] + pub(super) struct QuotePrice { + price: Price, + block_timestamp: Timestamp, + block_height: u64, + } + + impl From for QuotePrice { + fn from(value: super::QuotePrice) -> Self { + Self { + price: value.price.into(), + block_timestamp: value.block_timestamp.into(), + block_height: value.block_height, + } + } + } + + impl From for super::QuotePrice { + fn from(value: QuotePrice) -> Self { + Self { + price: value.price.into(), + block_timestamp: value.block_timestamp.into(), + block_height: value.block_height, + } + } + } + + #[derive(Debug, BorshSerialize, BorshDeserialize)] + pub(super) struct CurrencyPairState { + pub(super) price: QuotePrice, + pub(super) nonce: CurrencyPairNonce, + pub(super) id: CurrencyPairId, + } + + impl From for CurrencyPairState { + fn from(value: super::CurrencyPairState) -> Self { + Self { + price: value.price.into(), + nonce: value.nonce.into(), + id: value.id.into(), + } + } + } + + impl From for super::CurrencyPairState { + fn from(value: CurrencyPairState) -> Self { + Self { + price: value.price.into(), + nonce: value.nonce.into(), + id: value.id.into(), + } + } + } +} + const CURRENCY_PAIR_TO_ID_PREFIX: &str = "oraclecpid"; const ID_TO_CURRENCY_PAIR_PREFIX: &str = "oracleidcp"; const CURRENCY_PAIR_STATE_PREFIX: &str = "oraclecpstate"; @@ -44,7 +217,7 @@ fn currency_pair_to_id_storage_key(currency_pair: &CurrencyPair) -> String { format!("{CURRENCY_PAIR_TO_ID_PREFIX}/{currency_pair}",) } -fn id_to_currency_pair_storage_key(id: u64) -> String { +fn id_to_currency_pair_storage_key(id: CurrencyPairId) -> String { format!("{ID_TO_CURRENCY_PAIR_PREFIX}/{id}") } @@ -52,10 +225,6 @@ fn currency_pair_state_storage_key(currency_pair: &CurrencyPair) -> String { format!("{CURRENCY_PAIR_STATE_PREFIX}/{currency_pair}",) } -/// Newtype wrapper to read and write a u64 from rocksdb. -#[derive(BorshSerialize, BorshDeserialize, Debug)] -struct Id(u64); - /// Newtype wrapper to read and write a u64 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Count(u64); @@ -87,9 +256,10 @@ where } None => return Poll::Ready(None), }; - let Id(id) = Id::try_from_slice(&bytes).with_context(|| { - "failed decoding bytes read from state as currency pair ID for key `{key}`" - })?; + let in_state::CurrencyPairId(id) = in_state::CurrencyPairId::try_from_slice(&bytes) + .with_context(|| { + "failed decoding bytes read from state as currency pair ID for key `{key}`" + })?; let currency_pair = match extract_currency_pair_from_key(&key) { Err(err) => { return Poll::Ready(Some(Err(err).with_context(|| { @@ -149,32 +319,36 @@ fn extract_currency_pair_from_key(key: &str) -> anyhow::Result { #[async_trait] pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] - async fn get_currency_pair_id(&self, currency_pair: &CurrencyPair) -> Result { + async fn get_currency_pair_id( + &self, + currency_pair: &CurrencyPair, + ) -> Result> { let Some(bytes) = self .get_raw(¤cy_pair_to_id_storage_key(currency_pair)) .await .context("failed reading currency pair id from state")? else { - bail!("currency pair not found in state") + return Ok(None); }; - let Id(id) = Id::try_from_slice(&bytes).context("invalid currency pair id bytes")?; - Ok(id) + in_state::CurrencyPairId::try_from_slice(&bytes) + .context("invalid currency pair id bytes") + .map(|id| Some(id.into())) } #[instrument(skip_all)] - async fn get_currency_pair(&self, id: u64) -> Result> { - let bytes = self + async fn get_currency_pair(&self, id: CurrencyPairId) -> Result> { + let Some(bytes) = self .get_raw(&id_to_currency_pair_storage_key(id)) .await - .context("failed to get currency pair from state")?; - match bytes { - Some(bytes) => { - let currency_pair = serde_json::from_slice(&bytes) - .context("failed to deserialize currency pair")?; - Ok(Some(currency_pair)) - } - None => Ok(None), - } + .context("failed reading currency pair from state")? + else { + return Ok(None); + }; + let currency_pair = borsh::from_slice::(&bytes) + .context("failed to deserialize bytes read from state as currency pair")? + .try_into() + .context("failed converting in-state currency pair into domain type currency pair")?; + Ok(Some(currency_pair)) } #[instrument(skip_all)] @@ -228,27 +402,27 @@ pub(crate) trait StateReadExt: StateRead { .get_raw(¤cy_pair_state_storage_key(currency_pair)) .await .context("failed to get currency pair state from state")?; - match bytes { - Some(bytes) => { - let currency_pair_state = serde_json::from_slice(&bytes) - .context("failed to deserialize currency pair state")?; - Ok(Some(currency_pair_state)) - } - None => Ok(None), - } + bytes + .map(|bytes| { + borsh::from_slice::(&bytes) + .context("failed to deserialize bytes read from state as currency pair state") + .map(Into::into) + }) + .transpose() } #[instrument(skip_all)] - async fn get_next_currency_pair_id(&self) -> Result { + async fn get_next_currency_pair_id(&self) -> Result { let Some(bytes) = self .get_raw(NEXT_CURRENCY_PAIR_ID_KEY) .await .context("failed reading next currency pair id from state")? else { - return Ok(0); + return Ok(CurrencyPairId::new(0)); }; - let Id(next_currency_pair_id) = - Id::try_from_slice(&bytes).context("invalid next currency pair id bytes")?; + let next_currency_pair_id = in_state::CurrencyPairId::try_from_slice(&bytes) + .context("invalid next currency pair id bytes")? + .into(); Ok(next_currency_pair_id) } } @@ -258,16 +432,21 @@ impl StateReadExt for T {} #[async_trait] pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] - fn put_currency_pair_id(&mut self, currency_pair: &CurrencyPair, id: u64) -> Result<()> { - let bytes = borsh::to_vec(&Id(id)).context("failed to serialize currency pair id")?; + fn put_currency_pair_id( + &mut self, + currency_pair: &CurrencyPair, + id: CurrencyPairId, + ) -> Result<()> { + let bytes = borsh::to_vec(&in_state::CurrencyPairId::from(id)) + .context("failed to serialize currency pair id")?; self.put_raw(currency_pair_to_id_storage_key(currency_pair), bytes); Ok(()) } #[instrument(skip_all)] - fn put_currency_pair(&mut self, id: u64, currency_pair: &CurrencyPair) -> Result<()> { - let bytes = - serde_json::to_vec(¤cy_pair).context("failed to serialize currency pair")?; + fn put_currency_pair(&mut self, id: CurrencyPairId, currency_pair: CurrencyPair) -> Result<()> { + let bytes = borsh::to_vec(&in_state::CurrencyPair::from(currency_pair)) + .context("failed to serialize currency pair")?; self.put_raw(id_to_currency_pair_storage_key(id), bytes); Ok(()) } @@ -291,22 +470,24 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] fn put_currency_pair_state( &mut self, - currency_pair: &CurrencyPair, + currency_pair: CurrencyPair, currency_pair_state: CurrencyPairState, ) -> Result<()> { - let bytes = serde_json::to_vec(¤cy_pair_state) + let currency_pair_id = currency_pair_state.id; + let bytes = borsh::to_vec(&in_state::CurrencyPairState::from(currency_pair_state)) .context("failed to serialize currency pair state")?; - self.put_raw(currency_pair_state_storage_key(currency_pair), bytes); - self.put_currency_pair_id(currency_pair, currency_pair_state.id) + self.put_raw(currency_pair_state_storage_key(¤cy_pair), bytes); + + self.put_currency_pair_id(¤cy_pair, currency_pair_id) .context("failed to put currency pair id")?; - self.put_currency_pair(currency_pair_state.id, currency_pair) + self.put_currency_pair(currency_pair_id, currency_pair) .context("failed to put currency pair")?; Ok(()) } #[instrument(skip_all)] - fn put_next_currency_pair_id(&mut self, next_currency_pair_id: u64) -> Result<()> { - let bytes = borsh::to_vec(&Id(next_currency_pair_id)) + fn put_next_currency_pair_id(&mut self, next_currency_pair_id: CurrencyPairId) -> Result<()> { + let bytes = borsh::to_vec(&in_state::CurrencyPairId::from(next_currency_pair_id)) .context("failed to serialize next currency pair id")?; self.put_raw(NEXT_CURRENCY_PAIR_ID_KEY.to_string(), bytes); Ok(()) @@ -315,22 +496,28 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] async fn put_price_for_currency_pair( &mut self, - currency_pair: &CurrencyPair, + currency_pair: CurrencyPair, price: QuotePrice, ) -> Result<()> { let state = if let Some(mut state) = self - .get_currency_pair_state(currency_pair) + .get_currency_pair_state(¤cy_pair) .await .context("failed to get currency pair state")? { state.price = price; - state.nonce.checked_add(1).context("nonce overflow")?; + state.nonce = state + .nonce + .increment() + .context("increment nonce overflowed")?; state } else { - let id = self.get_next_currency_pair_id().await?; + let id = self + .get_next_currency_pair_id() + .await + .context("failed to read next currency pair ID")?; CurrencyPairState { price, - nonce: 0, + nonce: CurrencyPairNonce::new(0), id, } }; From 21e775aa73efe0f1a50547c40b031966e5fea046 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 19 Sep 2024 17:49:19 -0400 Subject: [PATCH 63/89] fix tests --- .../src/app/tests_app/mempool.rs | 72 +++++++++++++------ .../src/slinky/oracle/state_ext.rs | 8 +-- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index 65d327af80..c592b99be7 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -82,7 +82,10 @@ async fn trigger_cleaning() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 200_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: Height::default(), time: Time::now(), @@ -101,18 +104,32 @@ async fn trigger_cleaning() { // trigger with process_proposal let commitments = generate_rollup_datas_commitment(&[tx_trigger.clone()], HashMap::new()); + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = + tendermint::abci::types::ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let bytes = extended_commit_info.encode_to_vec(); + let mut txs_with_commit_info = vec![bytes.into()]; + txs_with_commit_info + .extend(commitments.into_transactions(vec![tx_trigger.to_raw().encode_to_vec().into()])); + let process_proposal = abci::request::ProcessProposal { hash: Hash::try_from([99u8; 32].to_vec()).unwrap(), height: 1u32.into(), time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: commitments.into_transactions(vec![tx_trigger.to_raw().encode_to_vec().into()]), - proposed_last_commit: None, + txs: txs_with_commit_info.clone(), + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: Round::default(), + }), misbehavior: vec![], }; - app.process_proposal(process_proposal.clone(), storage.clone()) + app.process_proposal(process_proposal, storage.clone()) .await .unwrap(); assert!(app.recost_mempool, "flag should have been set"); @@ -120,14 +137,14 @@ async fn trigger_cleaning() { // trigger with finalize block app.recost_mempool = false; assert!(!app.recost_mempool, "flag should start out false"); - let commitments = generate_rollup_datas_commitment(&[tx_trigger.clone()], HashMap::new()); + let finalize_block = abci::request::FinalizeBlock { hash: Hash::try_from([97u8; 32].to_vec()).unwrap(), height: 1u32.into(), time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: commitments.into_transactions(vec![tx_trigger.to_raw().encode_to_vec().into()]), + txs: txs_with_commit_info, decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -135,7 +152,7 @@ async fn trigger_cleaning() { misbehavior: vec![], }; - app.finalize_block(finalize_block.clone(), storage.clone()) + app.finalize_block(finalize_block, storage.clone()) .await .unwrap(); assert!(app.recost_mempool, "flag should have been set"); @@ -180,7 +197,10 @@ async fn do_not_trigger_cleaning() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 200_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: Height::default(), time: Time::now(), @@ -284,7 +304,10 @@ async fn maintenance_recosting_promotes() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 200_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: Height::default(), time: Time::now(), @@ -298,8 +321,8 @@ async fn maintenance_recosting_promotes() { assert_eq!( res.txs.len(), - 3, - "only one transaction should've been valid (besides 2 generated txs)" + 4, + "only one transaction should've been valid (besides 3 generated txs)" ); // set dummy hash app.executed_proposal_hash = Hash::try_from([97u8; 32].to_vec()).unwrap(); @@ -326,7 +349,10 @@ async fn maintenance_recosting_promotes() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 200_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: 2u8.into(), time: Time::now(), @@ -340,8 +366,8 @@ async fn maintenance_recosting_promotes() { assert_eq!( res.txs.len(), - 3, - "only one transaction should've been valid (besides 2 generated txs)" + 4, + "only one transaction should've been valid (besides 3 generated txs)" ); // set dummy hash app.executed_proposal_hash = Hash::try_from([97u8; 32].to_vec()).unwrap(); @@ -459,7 +485,10 @@ async fn maintenance_funds_added_promotes() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 200_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: Height::default(), time: Time::now(), @@ -473,8 +502,8 @@ async fn maintenance_funds_added_promotes() { assert_eq!( res.txs.len(), - 3, - "only one transactions should've been valid (besides 2 generated txs)" + 4, + "only one transactions should've been valid (besides 3 generated txs)" ); // set dummy hash app.executed_proposal_hash = Hash::try_from([97u8; 32].to_vec()).unwrap(); @@ -501,7 +530,10 @@ async fn maintenance_funds_added_promotes() { let prepare_args = abci::request::PrepareProposal { max_tx_bytes: 200_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: 2u8.into(), time: Time::now(), @@ -515,8 +547,8 @@ async fn maintenance_funds_added_promotes() { assert_eq!( res.txs.len(), - 3, - "only one transactions should've been valid (besides 2 generated txs)" + 4, + "only one transactions should've been valid (besides 3 generated txs)" ); // set dummy hash app.executed_proposal_hash = Hash::try_from([97u8; 32].to_vec()).unwrap(); diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs index 084cb28958..5b3c929da8 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/slinky/oracle/state_ext.rs @@ -260,9 +260,7 @@ where Some(Ok(item)) => item, Some(Err(err)) => { return Poll::Ready(Some( - Err(err) - .map_err(anyhow_to_eyre) - .wrap_err("failed reading from state"), + Err(anyhow_to_eyre(err)).wrap_err("failed reading from state"), )); } None => return Poll::Ready(None), @@ -305,9 +303,7 @@ where Some(Ok(item)) => item, Some(Err(err)) => { return Poll::Ready(Some( - Err(err) - .map_err(anyhow_to_eyre) - .wrap_err("failed reading from state"), + Err(anyhow_to_eyre(err)).wrap_err("failed reading from state"), )); } None => return Poll::Ready(None), From a2c767748d9bec0949a7e98b878174c7cdc1a519 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 24 Sep 2024 14:22:00 -0400 Subject: [PATCH 64/89] bump chart --- charts/sequencer/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/sequencer/Chart.yaml b/charts/sequencer/Chart.yaml index 71f597e184..a2f83c448a 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.23.0 +version: 0.23.1 # 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. From 6306abea1c163abf262cb7ae3b32acbaf45dd873 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 30 Sep 2024 17:27:53 -0400 Subject: [PATCH 65/89] address some comments --- charts/sequencer/templates/configmaps.yaml | 2 +- ...a1__tests__genesis_state_is_unchanged.snap | 47 +++++++- .../src/protocol/genesis/v1alpha1.rs | 103 ++++++++++++++++-- crates/astria-core/src/slinky/types.rs | 12 +- crates/astria-sequencer/local.env.example | 4 +- crates/astria-sequencer/src/app/mod.rs | 21 ++-- .../src/app/vote_extension.rs | 38 +++++-- crates/astria-sequencer/src/config.rs | 4 +- .../src/proposal/commitment.rs | 9 ++ crates/astria-sequencer/src/sequencer.rs | 2 +- 10 files changed, 201 insertions(+), 41 deletions(-) diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index a8d1ba3fc1..3c9ce5a514 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,7 +73,7 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" - ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8081" + ASTRIA_SEQUENCER_SLINKY_GRPC_ADDR: "http://127.0.0.1:8081" ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" {{- if not .Values.global.dev }} {{- else }} diff --git a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 index d62999a024..1925d0977b 100644 --- a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1alpha1__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 @@ -82,11 +82,54 @@ expression: genesis_state() }, "slinky": { "marketMap": { - "marketMap": {}, + "marketMap": { + "markets": { + "ETH/USD": { + "ticker": { + "currencyPair": { + "Base": "ETH", + "Quote": "USD" + }, + "decimals": "8", + "minProviderCount": "3", + "enabled": true + }, + "providerConfigs": [ + { + "name": "coingecko_api", + "offChainTicker": "ethereum/usd", + "normalizeByPair": { + "Base": "USDT", + "Quote": "USD" + } + } + ] + } + } + }, "params": { + "marketAuthorities": [ + "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm", + "astria1xnlvg0rle2u6auane79t4p27g8hxnj36ja960z" + ], "admin": "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" } }, - "oracle": {} + "oracle": { + "currencyPairGenesis": [ + { + "currencyPair": { + "Base": "ETH", + "Quote": "USD" + }, + "currencyPairPrice": { + "price": "3138872234", + "blockTimestamp": "2024-07-04T19:46:35+00:00" + }, + "id": "1" + } + ], + "nextId": "1" + } } } diff --git a/crates/astria-core/src/protocol/genesis/v1alpha1.rs b/crates/astria-core/src/protocol/genesis/v1alpha1.rs index c92414ab5a..f97a1a7ced 100644 --- a/crates/astria-core/src/protocol/genesis/v1alpha1.rs +++ b/crates/astria-core/src/protocol/genesis/v1alpha1.rs @@ -834,7 +834,10 @@ enum FeesErrorKind { #[cfg(test)] mod tests { - use indexmap::IndexMap; + use indexmap::{ + indexmap, + IndexMap, + }; use super::*; use crate::{ @@ -882,7 +885,60 @@ mod tests { .unwrap() } + fn genesis_state_markets() -> MarketMap { + use crate::slinky::{ + market_map::v1::{ + Market, + MarketMap, + ProviderConfig, + Ticker, + }, + types::v1::CurrencyPair, + }; + + let markets = indexmap! { + "ETH/USD".to_string() => Market { + ticker: Ticker { + currency_pair: CurrencyPair::from_parts( + "ETH".parse().unwrap(), + "USD".parse().unwrap(), + ), + decimals: 8, + min_provider_count: 3, + enabled: true, + metadata_json: String::new(), + }, + provider_configs: vec![ProviderConfig { + name: "coingecko_api".to_string(), + off_chain_ticker: "ethereum/usd".to_string(), + normalize_by_pair: CurrencyPair::from_parts( + "USDT".parse().unwrap(), + "USD".parse().unwrap(), + ), + invert: false, + metadata_json: String::new(), + }], + }, + }; + + MarketMap { + markets, + } + } + fn proto_genesis_state() -> raw::GenesisAppState { + use crate::slinky::{ + oracle::v1::{ + CurrencyPairGenesis, + QuotePrice, + }, + types::v1::{ + CurrencyPair, + CurrencyPairNonce, + Price, + }, + }; + raw::GenesisAppState { accounts: vec![ raw::Account { @@ -925,18 +981,31 @@ mod tests { slinky: Some( SlinkyGenesis { market_map: market_map::v1::GenesisState { - market_map: MarketMap { - markets: IndexMap::new(), - }, + market_map: genesis_state_markets(), last_updated: 0, params: Params { - market_authorities: vec![], + market_authorities: vec![alice(), bob()], admin: alice(), }, }, oracle: oracle::v1::GenesisState { - currency_pair_genesis: vec![], - next_id: CurrencyPairId::new(0), + currency_pair_genesis: vec![CurrencyPairGenesis { + id: CurrencyPairId::new(1), + nonce: CurrencyPairNonce::new(0), + currency_pair_price: QuotePrice { + price: Price::new(3_138_872_234_u128), + block_height: 0, + block_timestamp: pbjson_types::Timestamp { + seconds: 1_720_122_395, + nanos: 0, + }, + }, + currency_pair: CurrencyPair::from_parts( + "ETH".parse().unwrap(), + "USD".parse().unwrap(), + ), + }], + next_id: CurrencyPairId::new(1), }, } .into_raw(), @@ -997,6 +1066,26 @@ mod tests { }, ".ibc_relayer_addresses[1]", ); + assert_bad_prefix( + raw::GenesisAppState { + slinky: { + let mut slinky = proto_genesis_state().slinky; + slinky + .as_mut() + .unwrap() + .market_map + .as_mut() + .unwrap() + .params + .as_mut() + .unwrap() + .market_authorities[0] = mallory().to_string(); + slinky + }, + ..proto_genesis_state() + }, + ".market_map.params.market_authorities[0]", + ); assert_bad_prefix( raw::GenesisAppState { accounts: vec![ diff --git a/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/slinky/types.rs index d2891192f5..7ff9d96e47 100644 --- a/crates/astria-core/src/slinky/types.rs +++ b/crates/astria-core/src/slinky/types.rs @@ -258,8 +258,14 @@ pub mod v1 { type Err = CurrencyPairParseError; fn from_str(s: &str) -> Result { - let re = regex::Regex::new(r"^([a-zA-Z]+)/([a-zA-Z]+)$").expect("valid regex"); - let caps = re + static REGEX: std::sync::OnceLock = std::sync::OnceLock::new(); + fn get_regex() -> &'static regex::Regex { + REGEX.get_or_init(|| { + regex::Regex::new(r"^([a-zA-Z]+)/([a-zA-Z]+)$").expect("valid regex") + }) + } + + let caps = get_regex() .captures(s) .ok_or_else(|| CurrencyPairParseError::invalid_currency_pair_string(s))?; let base = caps @@ -359,7 +365,7 @@ mod test { } #[test] - fn test_currency_pair_parse_invalid() { + fn invalid_curreny_pair_is_rejected() { let currency_pair = "ETHUSD".parse::(); assert!(currency_pair.is_err()); } diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index e01bbb48f9..6fd7241e04 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -34,12 +34,12 @@ ASTRIA_SEQUENCER_METRICS_HTTP_LISTENER_ADDR="127.0.0.1:9000" # `ASTRIA_SEQUENCER_FORCE_STDOUT` is set to `true`. ASTRIA_SEQUENCER_PRETTY_PRINT=false -# If the oracle is disabled. If false, the oracle_grpc_addr must be set. +# If the oracle is disabled. If false, the slinky_grpc_addr must be set. # Should be false for validator nodes and true for non-validator nodes. ASTRIA_SEQUENCER_NO_ORACLE=true # The gRPC endpoint for the oracle sidecar. -ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="http://127.0.0.1:8081" +ASTRIA_SEQUENCER_SLINKY_GRPC_ADDR="http://127.0.0.1:8081" # The timeout for the responses from the oracle sidecar in milliseconds. ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS=1000 diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index b2e78b0602..a121e0e429 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -349,7 +349,7 @@ impl App { { Ok(info) => info, Err(e) => { - debug!( + warn!( error = AsRef::::as_ref(&e), "failed to generate extended commit info" ); @@ -360,23 +360,23 @@ impl App { } }; - let mut extended_commit_info_bytes = + let mut encoded_extended_commit_info = ExtendedCommitInfo::from(extended_commit_info).encode_to_vec(); let max_tx_bytes = usize::try_from(prepare_proposal.max_tx_bytes) .wrap_err("failed to convert max_tx_bytes to usize")?; // adjust max block size to account for extended commit info let adjusted_max_tx_bytes = max_tx_bytes - .checked_sub(extended_commit_info_bytes.len()) + .checked_sub(encoded_extended_commit_info.len()) .unwrap_or_else(|| { // zero the commit info if it's too large to fit in the block // for liveness. warn!( - extended_commit_info_bytes_len = extended_commit_info_bytes.len(), + encoded_extended_commit_info_len = encoded_extended_commit_info.len(), max_tx_bytes, "extended commit info is too large to fit in block; not including in block" ); - extended_commit_info_bytes.clear(); + encoded_extended_commit_info.clear(); max_tx_bytes }); let mut block_size_constraints = BlockSizeConstraints::new(adjusted_max_tx_bytes) @@ -410,8 +410,8 @@ impl App { let res = generate_rollup_datas_commitment(&signed_txs_included, deposits); // inject the extended commit info into the start of the block's txs - let txs = std::iter::once(extended_commit_info_bytes.into()) - .chain(res.into_transactions(included_tx_bytes)) + let txs = std::iter::once(encoded_extended_commit_info.into()) + .chain(res.into_iter().chain(included_tx_bytes)) .collect(); Ok(abci::response::PrepareProposal { txs, @@ -915,12 +915,11 @@ impl App { transactions commitment and rollup IDs commitment" ); - let extended_commit_info_bytes = &finalize_block.txs[0]; + let extended_commit_info_bytes = finalize_block.txs.get(0).expect("asserted length above"); let extended_commit_info = ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) - .wrap_err("failed to decode extended commit info")?; - let extended_commit_info = extended_commit_info + .wrap_err("failed to decode extended commit info")? .try_into() - .wrap_err("failed to convert extended commit info from proto to native")?; + .context("failed to validate decoded extended commit info")?; let mut state_tx: StateDelta>> = StateDelta::new(self.state.clone()); crate::app::vote_extension::apply_prices_from_vote_extensions( diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index ffc6da0b17..a702a07baf 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -40,7 +40,6 @@ use tendermint_proto::google::protobuf::Timestamp; use tonic::transport::Channel; use tracing::{ debug, - info, instrument, warn, }; @@ -115,7 +114,7 @@ impl Handler { match verify_vote_extension(state, vote.vote_extension, false).await { Ok(()) => abci::response::VerifyVoteExtension::Accept, Err(e) => { - tracing::error!(error = %e, "failed to verify vote extension"); + tracing::warn!(error = %e, "failed to verify vote extension"); abci::response::VerifyVoteExtension::Reject } } @@ -136,7 +135,7 @@ async fn verify_vote_extension( .wrap_err("failed to get max number of currency pairs")?; ensure!( - oracle_vote_extension.prices.len() as u64 <= max_num_currency_pairs, + u64::try_from(oracle_vote_extension.prices.len()).ok() <= Some(max_num_currency_pairs), "number of oracle vote extension prices exceeds max expected number of currency pairs" ); @@ -156,23 +155,38 @@ async fn transform_oracle_service_prices( state: &S, rsp: QueryPricesResponse, ) -> Result { - let mut strategy_prices = IndexMap::new(); + use astria_core::slinky::types::v1::CurrencyPairId; + use futures::StreamExt as _; + + let futures = futures::stream::FuturesUnordered::new(); for (currency_pair, price) in rsp.prices { - let id = match DefaultCurrencyPairStrategy::id(state, ¤cy_pair).await { + futures.push(async move { + ( + DefaultCurrencyPairStrategy::id(state, ¤cy_pair).await, + currency_pair, + price, + ) + }); + } + + let result: Vec<(Result>, CurrencyPair, Price)> = + futures.collect().await; + let strategy_prices = result.into_iter().filter_map(|(get_id_result, currency_pair, price)| { + let id = match get_id_result { Ok(Some(id)) => id, Ok(None) => { - info!(%currency_pair, "currency pair ID not found in state; skipping"); - continue; + debug!(%currency_pair, "currency pair ID not found in state; skipping"); + return None; } Err(err) => { - // FIXME: this event can be removed once all instrumented functions + // FIXME: this event can be removed once all instrumented functions // can generate an error event. - warn!(%currency_pair, "failed to fetch ID for currency pair; cancelling transformation"); - return Err(err).wrap_err("failed to fetch currency pair ID"); + warn!(error = %err, %currency_pair, "failed to fetch ID for currency pair; cancelling transformation"); + return Some(Err(err).wrap_err("failed to fetch currency pair ID")); } }; - strategy_prices.insert(id, price); - } + Some(Ok((id, price))) + }).collect::>>()?; Ok(OracleVoteExtension { prices: strategy_prices, diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index 5dfdf173eb..62ca58e01e 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -33,11 +33,11 @@ pub struct Config { pub metrics_http_listener_addr: String, /// Writes a human readable format to stdout instead of JSON formatted OTEL trace data. pub pretty_print: bool, - /// If the oracle is disabled. If false, the `oracle_grpc_addr` must be set. + /// If the oracle is disabled. If false, the `slinky_grpc_addr` must be set. /// Should be false for validator nodes and true for non-validator nodes. pub no_oracle: bool, /// The gRPC endpoint for the oracle sidecar. - pub oracle_grpc_addr: String, + pub slinky_grpc_addr: String, /// The timeout for the responses from the oracle sidecar in milliseconds. pub oracle_client_timeout_milliseconds: u64, } diff --git a/crates/astria-sequencer/src/proposal/commitment.rs b/crates/astria-sequencer/src/proposal/commitment.rs index bf902e0bdb..48458a2171 100644 --- a/crates/astria-sequencer/src/proposal/commitment.rs +++ b/crates/astria-sequencer/src/proposal/commitment.rs @@ -33,6 +33,15 @@ impl GeneratedCommitments { txs.append(&mut tx_data); txs } + + #[must_use] + pub(crate) fn into_iter(self) -> impl Iterator { + [ + self.rollup_datas_root.to_vec().into(), + self.rollup_ids_root.to_vec().into(), + ] + .into_iter() + } } /// Called when we receive a `PrepareProposal` or `ProcessProposal` consensus message. diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index 30a4274bcd..00453e16de 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -121,7 +121,7 @@ impl Sequencer { None } else { let uri: Uri = config - .oracle_grpc_addr + .slinky_grpc_addr .parse() .context("failed parsing oracle grpc address as Uri")?; let endpoint = Endpoint::from(uri.clone()).timeout(std::time::Duration::from_millis( From 0f1d47f069c51aa7cb77269e0b76349d636744ff Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 1 Oct 2024 18:36:29 -0400 Subject: [PATCH 66/89] address remaining comments --- crates/astria-sequencer/src/app/mod.rs | 6 +- .../src/app/vote_extension.rs | 109 ++++++++++++------ .../astria-sequencer/src/service/consensus.rs | 4 - .../src/slinky/marketmap/component.rs | 9 +- 4 files changed, 79 insertions(+), 49 deletions(-) diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index a121e0e429..74d5c38043 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -340,14 +340,14 @@ impl App { // the vote extensions empty in this block for liveness. // it's not a critical error if the oracle values are not updated for a block. let round = last_commit.round; - let extended_commit_info = match ProposalHandler::prune_and_validate_extended_commit_info( + let extended_commit_info = match ProposalHandler::prepare_proposal( &self.state, prepare_proposal.height.into(), last_commit, ) .await { - Ok(info) => info, + Ok(info) => info.into_inner(), Err(e) => { warn!( error = AsRef::::as_ref(&e), @@ -465,7 +465,7 @@ impl App { let Some(last_commit) = process_proposal.proposed_last_commit else { bail!("proposed last commit is empty; this should not occur") }; - ProposalHandler::validate_extended_commit_info( + ProposalHandler::validate_proposal( &self.state, process_proposal.height.value(), &last_commit, diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index a702a07baf..2b182af933 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -26,6 +26,7 @@ use astria_eyre::eyre::{ Result, WrapErr as _, }; +use futures::StreamExt; use indexmap::IndexMap; use prost::Message as _; use tendermint::{ @@ -40,6 +41,7 @@ use tendermint_proto::google::protobuf::Timestamp; use tonic::transport::Channel; use tracing::{ debug, + info, instrument, warn, }; @@ -179,9 +181,9 @@ async fn transform_oracle_service_prices( return None; } Err(err) => { - // FIXME: this event can be removed once all instrumented functions - // can generate an error event. - warn!(error = %err, %currency_pair, "failed to fetch ID for currency pair; cancelling transformation"); + // FIXME: this event can be removed once all instrumented functions + // can generate an error event. + warn!(%currency_pair, "failed to fetch ID for currency pair; cancelling transformation"); return Some(Err(err).wrap_err("failed to fetch currency pair ID")); } }; @@ -193,45 +195,72 @@ async fn transform_oracle_service_prices( }) } +pub(crate) struct ValidatedExtendedCommitInfo(ExtendedCommitInfo); + +impl ValidatedExtendedCommitInfo { + pub(crate) fn into_inner(self) -> ExtendedCommitInfo { + self.0 + } +} + pub(crate) struct ProposalHandler; impl ProposalHandler { - // called during prepare_proposal - pub(crate) async fn prune_and_validate_extended_commit_info( + // called during prepare_proposal; prunes and validates the local extended commit info + // received during the previous block's voting period. + // + // the returned extended commit info will be proposed this block. + pub(crate) async fn prepare_proposal( state: &S, height: u64, mut extended_commit_info: ExtendedCommitInfo, - ) -> Result { + ) -> Result { if height == 1 { // we're proposing block 1, so nothing to validate - return Ok(extended_commit_info); + info!( + "skipping vote extension proposal for block 1, as there were no previous vote \ + extensions" + ); + return Ok(ValidatedExtendedCommitInfo(extended_commit_info)); } + let mut futures = futures::stream::FuturesUnordered::new(); for vote in &mut extended_commit_info.votes { - if let Err(e) = verify_vote_extension(state, vote.vote_extension.clone(), true).await { - let address = state - .try_base_prefixed(vote.validator.address.as_slice()) - .await - .wrap_err("failed to construct validator address with base prefix")?; - debug!( - error = AsRef::::as_ref(&e), - validator = address.to_string(), - "failed to verify vote extension; pruning from proposal" - ); - vote.sig_info = Flag(tendermint::block::BlockIdFlag::Absent); - vote.extension_signature = None; - vote.vote_extension.clear(); - } + futures.push(async move { + if let Err(e) = + verify_vote_extension(state, vote.vote_extension.clone(), true).await + { + let address = state + .try_base_prefixed(vote.validator.address.as_slice()) + .await + .wrap_err("failed to construct validator address with base prefix")?; + debug!( + error = AsRef::::as_ref(&e), + validator = address.to_string(), + "failed to verify vote extension; pruning from proposal" + ); + vote.sig_info = Flag(tendermint::block::BlockIdFlag::Absent); + vote.extension_signature = None; + vote.vote_extension.clear(); + } + Ok::<(), astria_eyre::eyre::Report>(()) + }); + } + + while let Some(result) = futures.next().await { + result?; } + drop(futures); + validate_vote_extensions(state, height, &extended_commit_info) .await .wrap_err("failed to validate vote extensions in prepare_proposal")?; - Ok(extended_commit_info) + Ok(ValidatedExtendedCommitInfo(extended_commit_info)) } - // called during process_proposal - pub(crate) async fn validate_extended_commit_info( + // called during process_proposal; validates the proposed extended commit info. + pub(crate) async fn validate_proposal( state: &S, height: u64, last_commit: &CommitInfo, @@ -239,6 +268,10 @@ impl ProposalHandler { ) -> Result<()> { if height == 1 { // we're processing block 1, so nothing to validate (no last commit yet) + info!( + "skipping vote extension validation for block 1, as there were no previous vote \ + extensions" + ); return Ok(()); } @@ -284,22 +317,22 @@ async fn validate_vote_extensions( total_voting_power = total_voting_power.saturating_add(vote.validator.power.value()); - if vote.sig_info == Flag(tendermint::block::BlockIdFlag::Commit) - && vote.extension_signature.is_none() - { - bail!("vote extension signature is missing for validator {address}",); - } - - if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) - && !vote.vote_extension.is_empty() - { - bail!("non-commit vote extension present for validator {address}",); + if vote.sig_info == Flag(tendermint::block::BlockIdFlag::Commit) { + ensure!( + !vote.extension_signature.is_none(), + "vote extension signature is missing for validator {address}", + ); } - if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) - && vote.extension_signature.is_some() - { - bail!("non-commit extension signature present for validator {address}",); + if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) { + ensure!( + vote.vote_extension.is_empty(), + "non-commit vote extension present for validator {address}" + ); + ensure!( + vote.extension_signature.is_none(), + "non-commit extension signature present for validator {address}", + ) } if vote.sig_info != Flag(tendermint::block::BlockIdFlag::Commit) { diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index cef433ec1f..07b50d4726 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -135,10 +135,6 @@ impl Consensus { bail!("database already initialized"); } - println!( - "{}", - String::from_utf8(init_chain.app_state_bytes.to_vec()).unwrap() - ); let genesis_state: GenesisAppState = serde_json::from_slice(&init_chain.app_state_bytes) .wrap_err("failed to parse app_state in genesis file")?; let app_hash = self diff --git a/crates/astria-sequencer/src/slinky/marketmap/component.rs b/crates/astria-sequencer/src/slinky/marketmap/component.rs index cf3c72496d..5e1633a4a9 100644 --- a/crates/astria-sequencer/src/slinky/marketmap/component.rs +++ b/crates/astria-sequencer/src/slinky/marketmap/component.rs @@ -11,7 +11,8 @@ use tendermint::abci::request::{ }; use tracing::instrument; -use super::state_ext::StateWriteExt; +use cnidarium::StateWrite; +use super::state_ext::StateWriteExt as _; use crate::component::Component; #[derive(Default)] @@ -22,7 +23,7 @@ impl Component for MarketMapComponent { type AppState = GenesisAppState; #[instrument(name = "MarketMapComponent::init_chain", skip(state))] - async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { + async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { // TODO: put market map authorites and admin in state; // only required for related actions however @@ -36,7 +37,7 @@ impl Component for MarketMapComponent { } #[instrument(name = "MarketMapComponent::begin_block", skip(_state))] - async fn begin_block( + async fn begin_block( _state: &mut Arc, _begin_block: &BeginBlock, ) -> Result<()> { @@ -44,7 +45,7 @@ impl Component for MarketMapComponent { } #[instrument(name = "MarketMapComponent::end_block", skip(_state))] - async fn end_block( + async fn end_block( _state: &mut Arc, _end_block: &EndBlock, ) -> Result<()> { From c877303f86d87831649a565940cac3f8522a22b2 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 28 Oct 2024 17:21:31 -0400 Subject: [PATCH 67/89] fix and cleanup unit tests --- crates/astria-sequencer/src/app/test_utils.rs | 33 ++++++++ .../src/app/tests_app/mempool.rs | 33 +++----- .../astria-sequencer/src/app/tests_app/mod.rs | 79 ++++--------------- .../src/app/tests_block_ordering.rs | 79 ++++++++++--------- .../src/app/tests_breaking_changes.rs | 28 ++----- 5 files changed, 106 insertions(+), 146 deletions(-) diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index b03ec60d67..94f86d5394 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -48,6 +48,7 @@ use astria_core::{ TransactionBody, }, }, + sequencerblock::v1::block::Deposit, Protobuf, }; use astria_eyre::eyre::WrapErr as _; @@ -723,3 +724,35 @@ pub(crate) async fn mock_state_getter() -> StateDelta { state } + +#[expect( + clippy::allow_attributes, + clippy::allow_attributes_without_reason, + reason = "allow is only necessary when benchmark isn't enabled" +)] +#[cfg_attr(feature = "benchmark", allow(dead_code))] +pub(crate) fn transactions_with_extended_commit_info_and_commitments( + txs: &[Transaction], + deposits: Option>>, +) -> Vec { + use prost::Message as _; + use tendermint::abci::types::ExtendedCommitInfo; + + use crate::proposal::commitment::generate_rollup_datas_commitment; + + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let commitments = generate_rollup_datas_commitment(txs, deposits.unwrap_or_default()); + let txs_with_commit_info: Vec = + std::iter::once(extended_commit_info.encode_to_vec().into()) + .chain( + commitments + .into_iter() + .chain(txs.iter().map(|tx| tx.to_raw().encode_to_vec().into())), + ) + .collect(); + txs_with_commit_info +} diff --git a/crates/astria-sequencer/src/app/tests_app/mempool.rs b/crates/astria-sequencer/src/app/tests_app/mempool.rs index 8edd3333e5..1954c8e26d 100644 --- a/crates/astria-sequencer/src/app/tests_app/mempool.rs +++ b/crates/astria-sequencer/src/app/tests_app/mempool.rs @@ -14,7 +14,6 @@ use astria_core::{ }, Protobuf, }; -use prost::Message as _; use tendermint::{ abci::{ self, @@ -33,7 +32,6 @@ use super::*; use crate::{ accounts::StateReadExt as _, app::test_utils::*, - proposal::commitment::generate_rollup_datas_commitment, test_utils::{ astria_address_from_hex_string, nria, @@ -98,29 +96,14 @@ async fn trigger_cleaning() { assert!(!app.recost_mempool, "flag should start out false"); // trigger with process_proposal - let commitments = generate_rollup_datas_commitment(&[tx_trigger.clone()], HashMap::new()); - let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = - tendermint::abci::types::ExtendedCommitInfo { - round: 0u16.into(), - votes: vec![], - } - .into(); - let txs_with_commit_info: Vec = - std::iter::once(extended_commit_info.encode_to_vec().into()) - .chain( - commitments - .into_iter() - .chain(vec![tx_trigger.to_raw().encode_to_vec().into()]), - ) - .collect(); - + let txs = transactions_with_extended_commit_info_and_commitments(&vec![tx_trigger], None); let process_proposal = abci::request::ProcessProposal { hash: Hash::try_from([99u8; 32].to_vec()).unwrap(), height: 1u32.into(), time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: txs_with_commit_info.clone(), + txs: txs.clone(), proposed_last_commit: Some(CommitInfo { votes: vec![], round: Round::default(), @@ -143,7 +126,7 @@ async fn trigger_cleaning() { time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: txs_with_commit_info, + txs, decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -332,7 +315,10 @@ async fn maintenance_recosting_promotes() { next_validators_hash: Hash::default(), proposer_address: [1u8; 20].to_vec().try_into().unwrap(), txs: res.txs.clone(), - proposed_last_commit: None, + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; app.process_proposal(process_proposal, storage.clone()) @@ -512,7 +498,10 @@ async fn maintenance_funds_added_promotes() { next_validators_hash: Hash::default(), proposer_address: [1u8; 20].to_vec().try_into().unwrap(), txs: res.txs.clone(), - proposed_last_commit: None, + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; app.process_proposal(process_proposal, storage.clone()) diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 56a17d3e43..ab6132994b 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -63,7 +63,6 @@ use crate::{ }, bridge::StateWriteExt as _, fees::StateReadExt as _, - proposal::commitment::generate_rollup_datas_commitment, test_utils::{ astria_address, astria_address_from_hex_string, @@ -255,30 +254,13 @@ async fn app_transfer_block_fees_to_sudo() { let signed_tx = tx.sign(&alice); let proposer_address: tendermint::account::Id = [99u8; 20].to_vec().try_into().unwrap(); - - let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], HashMap::new()); - let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { - round: 0u16.into(), - votes: vec![], - } - .into(); - - let txs_with_commit_info: Vec = - std::iter::once(extended_commit_info.encode_to_vec().into()) - .chain( - commitments - .into_iter() - .chain(vec![signed_tx.to_raw().encode_to_vec().into()]), - ) - .collect(); - let finalize_block = abci::request::FinalizeBlock { hash: Hash::try_from([0u8; 32].to_vec()).unwrap(), height: 1u32.into(), time: Time::now(), next_validators_hash: Hash::default(), proposer_address, - txs: txs_with_commit_info, + txs: transactions_with_extended_commit_info_and_commitments(&vec![signed_tx], None), decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -385,20 +367,6 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { source_action_index: starting_index_of_action, }; let deposits = HashMap::from_iter(vec![(rollup_id, vec![expected_deposit.clone()])]); - let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], deposits.clone()); - let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { - round: 0u16.into(), - votes: vec![], - } - .into(); - let txs_with_commit_info: Vec = - std::iter::once(extended_commit_info.encode_to_vec().into()) - .chain( - commitments - .into_iter() - .chain(vec![signed_tx.to_raw().encode_to_vec().into()]), - ) - .collect(); let finalize_block = abci::request::FinalizeBlock { hash: Hash::try_from([0u8; 32].to_vec()).unwrap(), @@ -406,7 +374,7 @@ async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: txs_with_commit_info, + txs: transactions_with_extended_commit_info_and_commitments(&[signed_tx], Some(deposits)), decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -490,20 +458,6 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { source_action_index: starting_index_of_action, }; let deposits = HashMap::from_iter(vec![(rollup_id, vec![expected_deposit.clone()])]); - let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], deposits.clone()); - let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { - round: 0u16.into(), - votes: vec![], - } - .into(); - let txs_with_commit_info: Vec = - std::iter::once(extended_commit_info.encode_to_vec().into()) - .chain( - commitments - .into_iter() - .chain(vec![signed_tx.to_raw().encode_to_vec().into()]), - ) - .collect(); let timestamp = Time::now(); let block_hash = Hash::try_from([99u8; 32].to_vec()).unwrap(); @@ -513,7 +467,10 @@ async fn app_execution_results_match_proposal_vs_after_proposal() { time: timestamp, next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: txs_with_commit_info, + txs: transactions_with_extended_commit_info_and_commitments( + &[signed_tx.clone()], + Some(deposits), + ), decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), @@ -844,11 +801,6 @@ async fn app_process_proposal_sequencer_max_bytes_overflow_fail() { .sign(&alice); let txs: Vec = vec![tx_pass, tx_overflow]; - let generated_commitments = generate_rollup_datas_commitment(&txs, HashMap::new()); - let txs = generated_commitments - .into_iter() - .chain(txs.into_iter().map(|tx| tx.to_raw().encode_to_vec().into())) - .collect(); let process_proposal = ProcessProposal { hash: Hash::default(), @@ -856,8 +808,11 @@ async fn app_process_proposal_sequencer_max_bytes_overflow_fail() { time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs, - proposed_last_commit: None, + txs: transactions_with_extended_commit_info_and_commitments(&txs, None), + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; @@ -893,11 +848,6 @@ async fn app_process_proposal_transaction_fails_to_execute_fails() { .sign(&alice); let txs: Vec = vec![tx_fail]; - let generated_commitments = generate_rollup_datas_commitment(&txs, HashMap::new()); - let txs = generated_commitments - .into_iter() - .chain(txs.into_iter().map(|tx| tx.to_raw().encode_to_vec().into())) - .collect(); let process_proposal = ProcessProposal { hash: Hash::default(), @@ -905,8 +855,11 @@ async fn app_process_proposal_transaction_fails_to_execute_fails() { time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs, - proposed_last_commit: None, + txs: transactions_with_extended_commit_info_and_commitments(&txs, None), + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; diff --git a/crates/astria-sequencer/src/app/tests_block_ordering.rs b/crates/astria-sequencer/src/app/tests_block_ordering.rs index b78b411fb2..d71f2594fb 100644 --- a/crates/astria-sequencer/src/app/tests_block_ordering.rs +++ b/crates/astria-sequencer/src/app/tests_block_ordering.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashMap, - ops::Deref, -}; +use std::ops::Deref; use astria_core::{ protocol::transaction::v1::{ @@ -13,9 +10,15 @@ use astria_core::{ use bytes::Bytes; use prost::Message; use tendermint::{ - abci::request::{ - PrepareProposal, - ProcessProposal, + abci::{ + request::{ + PrepareProposal, + ProcessProposal, + }, + types::{ + CommitInfo, + ExtendedCommitInfo, + }, }, block::Height, Hash, @@ -23,16 +26,14 @@ use tendermint::{ }; use super::test_utils::get_alice_signing_key; -use crate::{ - app::test_utils::{ - get_bob_signing_key, - get_judy_signing_key, - initialize_app_with_storage, - mock_balances, - mock_tx_cost, - MockTxBuilder, - }, - proposal::commitment::generate_rollup_datas_commitment, +use crate::app::test_utils::{ + get_bob_signing_key, + get_judy_signing_key, + initialize_app_with_storage, + mock_balances, + mock_tx_cost, + transactions_with_extended_commit_info_and_commitments, + MockTxBuilder, }; #[tokio::test] @@ -68,20 +69,17 @@ async fn app_process_proposal_ordering_ok() { .clone(), ]; - let generated_commitments = generate_rollup_datas_commitment(&txs, HashMap::new()); - let txs = generated_commitments - .into_iter() - .chain(txs.into_iter().map(|tx| tx.to_raw().encode_to_vec().into())) - .collect(); - let process_proposal = ProcessProposal { hash: Hash::Sha256([1; 32]), height: 1u32.into(), time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs, - proposed_last_commit: None, + txs: transactions_with_extended_commit_info_and_commitments(&txs, None), + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; @@ -115,20 +113,17 @@ async fn app_process_proposal_ordering_fail() { .clone(), ]; - let generated_commitments = generate_rollup_datas_commitment(&txs, HashMap::new()); - let txs = generated_commitments - .into_iter() - .chain(txs.into_iter().map(|tx| tx.to_raw().encode_to_vec().into())) - .collect(); - let process_proposal = ProcessProposal { hash: Hash::default(), height: 1u32.into(), time: Time::now(), next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs, - proposed_last_commit: None, + txs: transactions_with_extended_commit_info_and_commitments(&txs, None), + proposed_last_commit: Some(CommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], }; @@ -184,7 +179,10 @@ async fn app_prepare_proposal_account_block_misordering_ok() { let prepare_args = PrepareProposal { max_tx_bytes: 600_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: Height::default(), time: Time::now(), @@ -198,8 +196,8 @@ async fn app_prepare_proposal_account_block_misordering_ok() { .expect("incorrect account ordering shouldn't cause blocks to fail"); assert_eq!( - prepare_proposal_result.txs[2], - Into::::into(tx_0.to_raw().encode_to_vec()), + prepare_proposal_result.txs.last().unwrap(), + &Into::::into(tx_0.to_raw().encode_to_vec()), "expected to contain first transaction" ); @@ -217,7 +215,10 @@ async fn app_prepare_proposal_account_block_misordering_ok() { let prepare_args = PrepareProposal { max_tx_bytes: 600_000, txs: vec![], - local_last_commit: None, + local_last_commit: Some(ExtendedCommitInfo { + votes: vec![], + round: 0u16.into(), + }), misbehavior: vec![], height: 1u32.into(), time: Time::now(), @@ -230,8 +231,8 @@ async fn app_prepare_proposal_account_block_misordering_ok() { .expect("incorrect account ordering shouldn't cause blocks to fail"); assert_eq!( - prepare_proposal_result.txs[2], - Into::::into(tx_1.to_raw().encode_to_vec()), + prepare_proposal_result.txs.last().unwrap(), + &Into::::into(tx_1.to_raw().encode_to_vec()), "expected to contain second transaction" ); diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 04682379e0..95dba7f314 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -37,10 +37,7 @@ use astria_core::{ Protobuf, }; use cnidarium::StateDelta; -use prost::{ - bytes::Bytes, - Message as _, -}; +use prost::bytes::Bytes; use tendermint::{ abci, abci::types::CommitInfo, @@ -57,12 +54,12 @@ use crate::{ initialize_app, initialize_app_with_storage, proto_genesis_state, + transactions_with_extended_commit_info_and_commitments, BOB_ADDRESS, CAROL_ADDRESS, }, authority::StateReadExt as _, bridge::StateWriteExt as _, - proposal::commitment::generate_rollup_datas_commitment, test_utils::{ astria_address, astria_address_from_hex_string, @@ -132,22 +129,6 @@ async fn app_finalize_block_snapshot() { source_action_index: starting_index_of_action, }; let deposits = HashMap::from_iter(vec![(rollup_id, vec![expected_deposit.clone()])]); - let commitments = generate_rollup_datas_commitment(&[signed_tx.clone()], deposits.clone()); - - let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = - tendermint::abci::types::ExtendedCommitInfo { - round: 0u16.into(), - votes: vec![], - } - .into(); - let txs_with_commit_info: Vec = - std::iter::once(extended_commit_info.encode_to_vec().into()) - .chain( - commitments - .into_iter() - .chain(vec![signed_tx.to_raw().encode_to_vec().into()]), - ) - .collect(); let timestamp = Time::unix_epoch(); let block_hash = Hash::try_from([99u8; 32].to_vec()).unwrap(); @@ -157,7 +138,10 @@ async fn app_finalize_block_snapshot() { time: timestamp, next_validators_hash: Hash::default(), proposer_address: [0u8; 20].to_vec().try_into().unwrap(), - txs: txs_with_commit_info, + txs: transactions_with_extended_commit_info_and_commitments( + &vec![signed_tx], + Some(deposits), + ), decided_last_commit: CommitInfo { votes: vec![], round: Round::default(), From 3edcdf0110c46ac6d01c6cdec02321a9a63f9f38 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 28 Oct 2024 17:25:11 -0400 Subject: [PATCH 68/89] chart and clippy --- charts/sequencer/Chart.yaml | 2 +- crates/astria-core/src/slinky/types.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/sequencer/Chart.yaml b/charts/sequencer/Chart.yaml index cd718ae1dc..54b04be575 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: 1.0.0 +version: 1.0.1 # 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/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/slinky/types.rs index 7ff9d96e47..5440575c5a 100644 --- a/crates/astria-core/src/slinky/types.rs +++ b/crates/astria-core/src/slinky/types.rs @@ -205,7 +205,7 @@ pub mod v1 { /// + The `.base` field could not be parsed as a [`Base`]. /// + The `.quote` field could not be parsed as [`Quote`]. // allow reason: symmetry with all other `try_from_raw` methods that take ownership - #[allow(clippy::needless_pass_by_value)] + #[expect(clippy::needless_pass_by_value, reason = "symmetry with other types")] pub fn try_from_raw(raw: raw::CurrencyPair) -> Result { let base = raw .base From 2f76b7823e8f9227e494fe77a0bf6956b2d369be Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 28 Oct 2024 19:54:32 -0400 Subject: [PATCH 69/89] fix smoke tests --- charts/sequencer/values.yaml | 1 - crates/astria-sequencer/src/app/mod.rs | 21 +++++++++++-------- .../astria-sequencer/src/app/tests_app/mod.rs | 1 - crates/astria-sequencer/src/grpc/slinky.rs | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/charts/sequencer/values.yaml b/charts/sequencer/values.yaml index bab24c6afb..552bd98200 100644 --- a/charts/sequencer/values.yaml +++ b/charts/sequencer/values.yaml @@ -35,7 +35,6 @@ genesis: ibcCompat: "astriacompat" authoritySudoAddress: "" marketAdminAddress: "" - # nativeAssetBaseDenomination: nria allowedFeeAssets: [] # - nria ibc: diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index f945881c8d..d7dd40727a 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -146,6 +146,14 @@ const EXECUTION_RESULTS_KEY: &str = "execution_results"; // cleared at the end of the block. const POST_TRANSACTION_EXECUTION_RESULT_KEY: &str = "post_transaction_execution_result"; +// the number of non-external transactions expected at the start of the block. +// +// currently consists of: +// 1. encoded `ExtendedCommitInfo` for the previous block +// 2. rollup data root +// 3. rollup IDs root +const INHERENT_TRANSACTIONS_COUNT: usize = 3; + /// The inter-block state being written to by the application. type InterBlockState = Arc>; @@ -1031,9 +1039,9 @@ impl App { // tx result for the commitments, even though they're not actually user txs. // // the tx_results passed to this function only contain results for every user - // transaction, not the commitment, so its length is len(txs) - 2. + // transaction, not the commitment, so its length is len(txs) - 3. let mut finalize_block_tx_results: Vec = Vec::with_capacity(txs.len()); - finalize_block_tx_results.extend(std::iter::repeat(ExecTxResult::default()).take(2)); + finalize_block_tx_results.extend(std::iter::repeat(ExecTxResult::default()).take(INHERENT_TRANSACTIONS_COUNT)); finalize_block_tx_results.extend(tx_results); let sequencer_block = SequencerBlock::try_from_block_info_and_data( @@ -1085,7 +1093,7 @@ impl App { } ensure!( - finalize_block.txs.len() >= 3, + finalize_block.txs.len() >= INHERENT_TRANSACTIONS_COUNT, "block must contain at least three transactions: the extended commit info, the rollup \ transactions commitment and rollup IDs commitment" ); @@ -1107,11 +1115,6 @@ impl App { .wrap_err("failed to apply prices from vote extensions")?; let _ = self.apply(state_tx); - // cometbft expects a result for every tx in the block, so we need to return a - // tx result for the commitments, even though they're not actually user txs. - let mut tx_results: Vec = Vec::with_capacity(finalize_block.txs.len()); - tx_results.extend(std::iter::repeat(ExecTxResult::default()).take(3)); - // When the hash is not empty, we have already executed and cached the results if self.executed_proposal_hash.is_empty() { // convert tendermint id to astria address; this assumes they are @@ -1135,7 +1138,7 @@ impl App { let mut tx_results = Vec::with_capacity(finalize_block.txs.len()); // skip the first three transactions, as they are injected transactions - for tx in finalize_block.txs.iter().skip(3) { + for tx in finalize_block.txs.iter().skip(INHERENT_TRANSACTIONS_COUNT) { let signed_tx = signed_transaction_from_bytes(tx) .wrap_err("protocol error; only valid txs should be finalized")?; diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index ab6132994b..6fd85316fe 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -290,7 +290,6 @@ async fn app_transfer_block_fees_to_sudo() { assert_eq!(app.state.get_block_fees().len(), 0); } -#[allow(clippy::too_many_lines)] #[tokio::test] async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { use astria_core::{ diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/slinky.rs index 21fbe565d0..8931ff91b6 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/slinky.rs @@ -94,7 +94,7 @@ impl MarketMapQueryService for SequencerServer { Ok(Response::new(MarketMapResponse { market_map: market_map.map(astria_core::slinky::market_map::v1::MarketMap::into_raw), last_updated, - chain_id: chain_id.to_string(), // TODO: is this the right chain id? + chain_id: chain_id.to_string(), })) } From 2bce11721126b930fe11458f04b9ed8024b33ffd Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 28 Oct 2024 19:55:22 -0400 Subject: [PATCH 70/89] fmt --- crates/astria-sequencer/src/app/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index d7dd40727a..d9b97e6ff1 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -1041,7 +1041,8 @@ impl App { // the tx_results passed to this function only contain results for every user // transaction, not the commitment, so its length is len(txs) - 3. let mut finalize_block_tx_results: Vec = Vec::with_capacity(txs.len()); - finalize_block_tx_results.extend(std::iter::repeat(ExecTxResult::default()).take(INHERENT_TRANSACTIONS_COUNT)); + finalize_block_tx_results + .extend(std::iter::repeat(ExecTxResult::default()).take(INHERENT_TRANSACTIONS_COUNT)); finalize_block_tx_results.extend(tx_results); let sequencer_block = SequencerBlock::try_from_block_info_and_data( From bf67de67223701ddbf9cf24340462d428521992a Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 28 Oct 2024 20:10:55 -0400 Subject: [PATCH 71/89] update dev values --- dev/values/validators/all-without-native.yml | 1 + .../protocol/genesis/v1alpha1/types.proto | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto diff --git a/dev/values/validators/all-without-native.yml b/dev/values/validators/all-without-native.yml index 8452e71d2c..c1e1f7683d 100644 --- a/dev/values/validators/all-without-native.yml +++ b/dev/values/validators/all-without-native.yml @@ -7,6 +7,7 @@ genesis: addressPrefixes: base: "astria" authoritySudoAddress: astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm + marketAdminAddress: astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm ibc: enabled: true inboundEnabled: true 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..7ba82446cc --- /dev/null +++ b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; + +package astria.protocol.genesis.v1alpha1; + +import "astria/primitive/v1/types.proto"; +import "astria/protocol/fees/v1alpha1/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; + GenesisFees fees = 10; +} + +message Account { + astria.primitive.v1.Address address = 1; + astria.primitive.v1.Uint128 balance = 2; +} + +message AddressPrefixes { + // The base prefix used for most Astria Sequencer addresses. + string base = 1; + // The prefix used for sending ics20 transfers to IBC chains + // that enforce a bech32 format of the packet sender. + string ibc_compat = 2; +} + +// 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 GenesisFees { + astria.protocol.fees.v1alpha1.BridgeLockFeeComponents bridge_lock = 1; + astria.protocol.fees.v1alpha1.BridgeSudoChangeFeeComponents bridge_sudo_change = 2; + astria.protocol.fees.v1alpha1.BridgeUnlockFeeComponents bridge_unlock = 3; + astria.protocol.fees.v1alpha1.FeeAssetChangeFeeComponents fee_asset_change = 4; + astria.protocol.fees.v1alpha1.FeeChangeFeeComponents fee_change = 5; + astria.protocol.fees.v1alpha1.IbcRelayFeeComponents ibc_relay = 7; + astria.protocol.fees.v1alpha1.IbcRelayerChangeFeeComponents ibc_relayer_change = 6; + astria.protocol.fees.v1alpha1.IbcSudoChangeFeeComponents ibc_sudo_change = 8; + astria.protocol.fees.v1alpha1.Ics20WithdrawalFeeComponents ics20_withdrawal = 9; + astria.protocol.fees.v1alpha1.InitBridgeAccountFeeComponents init_bridge_account = 10; + astria.protocol.fees.v1alpha1.RollupDataSubmissionFeeComponents rollup_data_submission = 11; + astria.protocol.fees.v1alpha1.SudoAddressChangeFeeComponents sudo_address_change = 12; + astria.protocol.fees.v1alpha1.TransferFeeComponents transfer = 13; + astria.protocol.fees.v1alpha1.ValidatorUpdateFeeComponents validator_update = 14; +} From d2f87186d65b5c7c174d8bad21bae3db54097ea5 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 28 Oct 2024 20:18:14 -0400 Subject: [PATCH 72/89] protos --- .../astria.protocol.genesis.v1alpha1.rs | 21 --- .../astria.protocol.genesis.v1alpha1.serde.rs | 126 ------------------ 2 files changed, 147 deletions(-) diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs index 1f6b358612..fc4cf3cf9f 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.rs @@ -27,8 +27,6 @@ pub struct GenesisAppState { pub allowed_fee_assets: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(message, optional, tag = "10")] pub fees: ::core::option::Option, - #[prost(message, optional, tag = "11")] - pub slinky: ::core::option::Option, } impl ::prost::Name for GenesisAppState { const NAME: &'static str = "GenesisAppState"; @@ -158,22 +156,3 @@ impl ::prost::Name for GenesisFees { ::prost::alloc::format!("astria.protocol.genesis.v1alpha1.{}", Self::NAME) } } -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SlinkyGenesis { - #[prost(message, optional, tag = "1")] - pub market_map: ::core::option::Option< - super::super::super::super::astria_vendored::slinky::marketmap::v1::GenesisState, - >, - #[prost(message, optional, tag = "2")] - pub oracle: ::core::option::Option< - super::super::super::super::astria_vendored::slinky::oracle::v1::GenesisState, - >, -} -impl ::prost::Name for SlinkyGenesis { - const NAME: &'static str = "SlinkyGenesis"; - 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 index 1e9b281239..dba51d7b9b 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1alpha1.serde.rs @@ -253,9 +253,6 @@ impl serde::Serialize for GenesisAppState { if self.fees.is_some() { len += 1; } - if self.slinky.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)?; @@ -287,9 +284,6 @@ impl serde::Serialize for GenesisAppState { if let Some(v) = self.fees.as_ref() { struct_ser.serialize_field("fees", v)?; } - if let Some(v) = self.slinky.as_ref() { - struct_ser.serialize_field("slinky", v)?; - } struct_ser.end() } } @@ -318,7 +312,6 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "allowed_fee_assets", "allowedFeeAssets", "fees", - "slinky", ]; #[allow(clippy::enum_variant_names)] @@ -333,7 +326,6 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { IbcParameters, AllowedFeeAssets, Fees, - Slinky, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -365,7 +357,6 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "ibcParameters" | "ibc_parameters" => Ok(GeneratedField::IbcParameters), "allowedFeeAssets" | "allowed_fee_assets" => Ok(GeneratedField::AllowedFeeAssets), "fees" => Ok(GeneratedField::Fees), - "slinky" => Ok(GeneratedField::Slinky), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -395,7 +386,6 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { let mut ibc_parameters__ = None; let mut allowed_fee_assets__ = None; let mut fees__ = None; - let mut slinky__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::ChainId => { @@ -458,12 +448,6 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { } fees__ = map_.next_value()?; } - GeneratedField::Slinky => { - if slinky__.is_some() { - return Err(serde::de::Error::duplicate_field("slinky")); - } - slinky__ = map_.next_value()?; - } } } Ok(GenesisAppState { @@ -477,7 +461,6 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { ibc_parameters: ibc_parameters__, allowed_fee_assets: allowed_fee_assets__.unwrap_or_default(), fees: fees__, - slinky: slinky__, }) } } @@ -937,112 +920,3 @@ impl<'de> serde::Deserialize<'de> for IbcParameters { deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.IbcParameters", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for SlinkyGenesis { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.market_map.is_some() { - len += 1; - } - if self.oracle.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1alpha1.SlinkyGenesis", len)?; - if let Some(v) = self.market_map.as_ref() { - struct_ser.serialize_field("marketMap", v)?; - } - if let Some(v) = self.oracle.as_ref() { - struct_ser.serialize_field("oracle", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for SlinkyGenesis { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "market_map", - "marketMap", - "oracle", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - MarketMap, - Oracle, - } - 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 { - "marketMap" | "market_map" => Ok(GeneratedField::MarketMap), - "oracle" => Ok(GeneratedField::Oracle), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = SlinkyGenesis; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.protocol.genesis.v1alpha1.SlinkyGenesis") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut market_map__ = None; - let mut oracle__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::MarketMap => { - if market_map__.is_some() { - return Err(serde::de::Error::duplicate_field("marketMap")); - } - market_map__ = map_.next_value()?; - } - GeneratedField::Oracle => { - if oracle__.is_some() { - return Err(serde::de::Error::duplicate_field("oracle")); - } - oracle__ = map_.next_value()?; - } - } - } - Ok(SlinkyGenesis { - market_map: market_map__, - oracle: oracle__, - }) - } - } - deserializer.deserialize_struct("astria.protocol.genesis.v1alpha1.SlinkyGenesis", FIELDS, GeneratedVisitor) - } -} From 477d0ecb79689597da8ce73a7ddd18297e2d7182 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 29 Oct 2024 11:00:58 -0400 Subject: [PATCH 73/89] verify vote extensions in validate_proposal --- crates/astria-sequencer/justfile | 14 +++++++++++--- crates/astria-sequencer/src/app/vote_extension.rs | 12 ++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/astria-sequencer/justfile b/crates/astria-sequencer/justfile index 5251162ed9..f1df7c8131 100644 --- a/crates/astria-sequencer/justfile +++ b/crates/astria-sequencer/justfile @@ -12,14 +12,22 @@ run: cargo run run-cometbft: + #!/usr/bin/env bash + set -e + app_state_genesis="$(mktemp)" + cometbft init + + # uncomment this line if you want to inspect `app_state_genesis` + trap "rm -f ${app_state_genesis@Q}" EXIT cargo run -p astria-sequencer-utils -- \ - generate-genesis-state -o ../../target/app-genesis-state.json + generate-genesis-state -o "${app_state_genesis}" --force cargo run -p astria-sequencer-utils -- \ copy-genesis-state \ - --genesis-app-state-file=../../target/app-genesis-state.json \ - --destination-genesis-file=$HOME/.cometbft/config/genesis.json \ + --genesis-app-state-file="${app_state_genesis}" \ + --destination-genesis-file="$HOME/.cometbft/config/genesis.json" \ --chain-id=astria + sed -i'.bak' 's/timeout_commit = "1s"/timeout_commit = "2s"/g' ~/.cometbft/config/config.toml cometbft node diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 4334e37dd3..092d1be9bc 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -282,6 +282,18 @@ impl ProposalHandler { validate_vote_extensions(state, height, extended_commit_info) .await .wrap_err("failed to validate vote extensions in validate_extended_commit_info")?; + + let mut futures = futures::stream::FuturesUnordered::new(); + for vote in &extended_commit_info.votes { + futures.push(async move { + verify_vote_extension(state, vote.vote_extension.clone(), true).await + }); + } + + while let Some(result) = futures.next().await { + result.wrap_err("failed to verify vote extension in validate_proposal")?; + } + Ok(()) } } From 43be7e87f14472d2a8b8cc3fadccaf74a6f22065 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 29 Oct 2024 11:08:44 -0400 Subject: [PATCH 74/89] update verify_vote_extension to not read from state --- crates/astria-sequencer/src/app/mod.rs | 2 +- .../src/app/vote_extension.rs | 65 +++++++++---------- .../astria-sequencer/src/service/consensus.rs | 6 +- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index d9b97e6ff1..d2bb401d22 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -986,7 +986,7 @@ impl App { pub(crate) async fn verify_vote_extension( &mut self, vote_extension: abci::request::VerifyVoteExtension, - ) -> abci::response::VerifyVoteExtension { + ) -> Result { self.vote_extension_handler .verify_vote_extension(&self.state, vote_extension) .await diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 092d1be9bc..935ec621c5 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -108,33 +108,35 @@ impl Handler { &self, state: &S, vote: abci::request::VerifyVoteExtension, - ) -> abci::response::VerifyVoteExtension { + ) -> Result { if vote.vote_extension.is_empty() { - return abci::response::VerifyVoteExtension::Accept; + return Ok(abci::response::VerifyVoteExtension::Accept); } - match verify_vote_extension(state, vote.vote_extension, false).await { - Ok(()) => abci::response::VerifyVoteExtension::Accept, - Err(e) => { - tracing::warn!(error = %e, "failed to verify vote extension"); - abci::response::VerifyVoteExtension::Reject - } - } + let max_num_currency_pairs = + DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, false) + .await + .wrap_err("failed to get max number of currency pairs")?; + + let response = + match verify_vote_extension(vote.vote_extension, max_num_currency_pairs).await { + Ok(()) => abci::response::VerifyVoteExtension::Accept, + Err(e) => { + tracing::warn!(error = %e, "failed to verify vote extension"); + abci::response::VerifyVoteExtension::Reject + } + }; + Ok(response) } } // see https://github.com/skip-mev/slinky/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L24 -async fn verify_vote_extension( - state: &S, +async fn verify_vote_extension( oracle_vote_extension_bytes: bytes::Bytes, - is_proposal_phase: bool, + max_num_currency_pairs: u64, ) -> Result<()> { let oracle_vote_extension = RawOracleVoteExtension::decode(oracle_vote_extension_bytes) .wrap_err("failed to decode oracle vote extension")?; - let max_num_currency_pairs = - DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, is_proposal_phase) - .await - .wrap_err("failed to get max number of currency pairs")?; ensure!( u64::try_from(oracle_vote_extension.prices.len()).ok() <= Some(max_num_currency_pairs), @@ -224,11 +226,16 @@ impl ProposalHandler { return Ok(ValidatedExtendedCommitInfo(extended_commit_info)); } + let max_num_currency_pairs = + DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, true) + .await + .wrap_err("failed to get max number of currency pairs")?; + let mut futures = futures::stream::FuturesUnordered::new(); for vote in &mut extended_commit_info.votes { futures.push(async move { if let Err(e) = - verify_vote_extension(state, vote.vote_extension.clone(), true).await + verify_vote_extension(vote.vote_extension.clone(), max_num_currency_pairs).await { let address = state .try_base_prefixed(vote.validator.address.as_slice()) @@ -283,10 +290,15 @@ impl ProposalHandler { .await .wrap_err("failed to validate vote extensions in validate_extended_commit_info")?; + let max_num_currency_pairs = + DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state, true) + .await + .wrap_err("failed to get max number of currency pairs")?; + let mut futures = futures::stream::FuturesUnordered::new(); for vote in &extended_commit_info.votes { futures.push(async move { - verify_vote_extension(state, vote.vote_extension.clone(), true).await + verify_vote_extension(vote.vote_extension.clone(), max_num_currency_pairs).await }); } @@ -587,21 +599,8 @@ mod test { }; #[tokio::test] - async fn verify_vote_extension_proposal_phase_ok() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - verify_vote_extension(&snapshot, vec![].into(), true) - .await - .unwrap(); - } - - #[tokio::test] - async fn verify_vote_extension_not_proposal_phase_ok() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - verify_vote_extension(&snapshot, vec![].into(), true) - .await - .unwrap(); + async fn verify_vote_extension_empty_ok() { + verify_vote_extension(vec![].into(), 100).await.unwrap(); } #[tokio::test] diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index c11b667843..1fc4fa6ed0 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -114,7 +114,9 @@ impl Consensus { } ConsensusRequest::VerifyVoteExtension(vote_extension) => { ConsensusResponse::VerifyVoteExtension( - self.handle_verify_vote_extension(vote_extension).await, + self.handle_verify_vote_extension(vote_extension) + .await + .wrap_err("failed to verify vote extension")?, ) } ConsensusRequest::FinalizeBlock(finalize_block) => ConsensusResponse::FinalizeBlock( @@ -199,7 +201,7 @@ impl Consensus { async fn handle_verify_vote_extension( &mut self, vote_extension: request::VerifyVoteExtension, - ) -> response::VerifyVoteExtension { + ) -> Result { self.app.verify_vote_extension(vote_extension).await } From 3510b0bd4e9bf80f0a08539f2742462469141cc7 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 29 Oct 2024 11:45:33 -0400 Subject: [PATCH 75/89] modify consensus_params in justfile, not genesis parser --- .../src/genesis_parser.rs | 25 ------------------- crates/astria-sequencer/justfile | 2 ++ 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/crates/astria-sequencer-utils/src/genesis_parser.rs b/crates/astria-sequencer-utils/src/genesis_parser.rs index f79c18b3bd..a26770f490 100644 --- a/crates/astria-sequencer-utils/src/genesis_parser.rs +++ b/crates/astria-sequencer-utils/src/genesis_parser.rs @@ -80,14 +80,6 @@ fn insert_app_state_and_chain_id(dst: &mut Value, app_state: &Value, chain_id: S let Value::Object(dst) = dst else { panic!("dst is not an object"); }; - dst.get_mut("consensus_params") - .expect("consensus_params field exists in cometbft genesis") - .as_object_mut() - .expect("consensus_params field is a JSON object") - .insert( - "abci".to_string(), - serde_json::json!({ "vote_extensions_enable_height": "1" }), - ); dst.insert("app_state".to_string(), app_state.clone()); dst.insert("chain_id".to_string(), chain_id.into()); } @@ -103,13 +95,6 @@ mod tests { let mut a = json!({ "genesis_time": "2023-06-21T15:58:36.741257Z", "initial_height": "0", - "consensus_params": { - "validator": { - "pub_key_types": [ - "ed25519" - ] - } - } }); let b = json!({ @@ -132,16 +117,6 @@ mod tests { let output = json!({ "genesis_time": "2023-06-21T15:58:36.741257Z", "initial_height": "0", - "consensus_params": { - "abci": { - "vote_extensions_enable_height": "1" - }, - "validator": { - "pub_key_types": [ - "ed25519" - ] - } - }, "app_state": { "accounts": [ { diff --git a/crates/astria-sequencer/justfile b/crates/astria-sequencer/justfile index f1df7c8131..bd2c96ce5d 100644 --- a/crates/astria-sequencer/justfile +++ b/crates/astria-sequencer/justfile @@ -15,6 +15,7 @@ run-cometbft: #!/usr/bin/env bash set -e app_state_genesis="$(mktemp)" + genesis="$(mktemp)" cometbft init @@ -27,6 +28,7 @@ run-cometbft: --genesis-app-state-file="${app_state_genesis}" \ --destination-genesis-file="$HOME/.cometbft/config/genesis.json" \ --chain-id=astria + jq ".consensus_params.abci.vote_extensions_enable_height = \"1\"" $HOME/.cometbft/config/genesis.json > "$genesis" && mv "$genesis" $HOME/.cometbft/config/genesis.json sed -i'.bak' 's/timeout_commit = "1s"/timeout_commit = "2s"/g' ~/.cometbft/config/config.toml cometbft node From 5d99f4d4dcd37b6cf00e3b3a324d108ad4ee84b4 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 29 Oct 2024 17:34:18 -0400 Subject: [PATCH 76/89] bump slinky protos to v2 and rename to connect --- buf.yaml | 6 +- .../files/cometbft/config/genesis.json | 2 +- charts/sequencer/templates/configmaps.yaml | 2 +- .../src/{slinky => connect}/abci.rs | 10 +- .../src/{slinky => connect}/market_map.rs | 12 +- .../src/{slinky => connect}/mod.rs | 0 .../src/{slinky => connect}/oracle.rs | 6 +- .../src/{slinky => connect}/service.rs | 9 +- .../src/{slinky => connect}/types.rs | 6 +- .../generated/astria.protocol.genesis.v1.rs | 12 +- .../astria.protocol.genesis.v1.serde.rs | 242 +++++++++--------- ....rs => astria_vendored.connect.abci.v2.rs} | 4 +- ... astria_vendored.connect.abci.v2.serde.rs} | 6 +- ...> astria_vendored.connect.marketmap.v2.rs} | 88 +++---- ...ia_vendored.connect.marketmap.v2.serde.rs} | 84 +++--- ...s => astria_vendored.connect.oracle.v2.rs} | 92 +++---- ...stria_vendored.connect.oracle.v2.serde.rs} | 82 +++--- ... => astria_vendored.connect.service.v2.rs} | 156 +++++++++-- ...tria_vendored.connect.service.v2.serde.rs} | 203 ++++++++++++++- ...rs => astria_vendored.connect.types.v2.rs} | 4 +- ...astria_vendored.connect.types.v2.serde.rs} | 6 +- crates/astria-core/src/generated/mod.rs | 32 +-- crates/astria-core/src/lib.rs | 2 +- ...v1__tests__genesis_state_is_unchanged.snap | 2 +- crates/astria-core/src/protocol/genesis/v1.rs | 136 +++++----- .../src/genesis_example.rs | 20 +- crates/astria-sequencer/local.env.example | 4 +- .../src/app/benchmark_and_test_utils.rs | 16 +- crates/astria-sequencer/src/app/mod.rs | 8 +- ...ransaction_with_every_action_snapshot.snap | 58 ++--- ..._changes__app_finalize_block_snapshot.snap | 58 ++--- ...reaking_changes__app_genesis_snapshot.snap | 60 ++--- .../src/app/vote_extension.rs | 42 +-- crates/astria-sequencer/src/config.rs | 4 +- .../marketmap/component.rs | 4 +- .../src/{slinky => connect}/marketmap/mod.rs | 0 .../marketmap/state_ext.rs | 8 +- .../src/{slinky => connect}/mod.rs | 0 .../{slinky => connect}/oracle/component.rs | 8 +- .../oracle/currency_pair_strategy.rs | 6 +- .../src/{slinky => connect}/oracle/mod.rs | 0 .../{slinky => connect}/oracle/state_ext.rs | 12 +- .../src/grpc/{slinky.rs => connect.rs} | 24 +- crates/astria-sequencer/src/grpc/mod.rs | 2 +- crates/astria-sequencer/src/lib.rs | 2 +- crates/astria-sequencer/src/sequencer.rs | 21 +- .../astria/protocol/genesis/v1/types.proto | 12 +- .../protocol/genesis/v1alpha1/types.proto | 59 ----- .../abci/v2}/vote_extensions.proto | 5 +- .../v1 => connect/marketmap/v2}/genesis.proto | 9 +- .../v1 => connect/marketmap/v2}/market.proto | 11 +- .../v1 => connect/marketmap/v2}/params.proto | 5 +- .../v1 => connect/marketmap/v2}/query.proto | 37 +-- .../v1 => connect/oracle/v2}/genesis.proto | 13 +- .../v1 => connect/oracle/v2}/query.proto | 52 ++-- .../connect/service/v2/oracle.proto | 67 +++++ .../connect/types/v2/currency_pair.proto | 11 + .../slinky/service/v1/oracle.proto | 42 --- .../slinky/types/v1/currency_pair.proto | 12 - tools/protobuf-compiler/src/main.rs | 6 +- 60 files changed, 1089 insertions(+), 813 deletions(-) rename crates/astria-core/src/{slinky => connect}/abci.rs (89%) rename crates/astria-core/src/{slinky => connect}/market_map.rs (99%) rename crates/astria-core/src/{slinky => connect}/mod.rs (100%) rename crates/astria-core/src/{slinky => connect}/oracle.rs (99%) rename crates/astria-core/src/{slinky => connect}/service.rs (93%) rename crates/astria-core/src/{slinky => connect}/types.rs (98%) rename crates/astria-core/src/generated/{astria_vendored.slinky.abci.v1.rs => astria_vendored.connect.abci.v2.rs} (82%) rename crates/astria-core/src/generated/{astria_vendored.slinky.abci.v1.serde.rs => astria_vendored.connect.abci.v2.serde.rs} (92%) rename crates/astria-core/src/generated/{astria_vendored.slinky.marketmap.v1.rs => astria_vendored.connect.marketmap.v2.rs} (90%) rename crates/astria-core/src/generated/{astria_vendored.slinky.marketmap.v1.serde.rs => astria_vendored.connect.marketmap.v2.serde.rs} (93%) rename crates/astria-core/src/generated/{astria_vendored.slinky.oracle.v1.rs => astria_vendored.connect.oracle.v2.rs} (90%) rename crates/astria-core/src/generated/{astria_vendored.slinky.oracle.v1.serde.rs => astria_vendored.connect.oracle.v2.serde.rs} (92%) rename crates/astria-core/src/generated/{astria_vendored.slinky.service.v1.rs => astria_vendored.connect.service.v2.rs} (72%) rename crates/astria-core/src/generated/{astria_vendored.slinky.service.v1.serde.rs => astria_vendored.connect.service.v2.serde.rs} (60%) rename crates/astria-core/src/generated/{astria_vendored.slinky.types.v1.rs => astria_vendored.connect.types.v2.rs} (78%) rename crates/astria-core/src/generated/{astria_vendored.slinky.types.v1.serde.rs => astria_vendored.connect.types.v2.serde.rs} (93%) rename crates/astria-sequencer/src/{slinky => connect}/marketmap/component.rs (90%) rename crates/astria-sequencer/src/{slinky => connect}/marketmap/mod.rs (100%) rename crates/astria-sequencer/src/{slinky => connect}/marketmap/state_ext.rs (93%) rename crates/astria-sequencer/src/{slinky => connect}/mod.rs (100%) rename crates/astria-sequencer/src/{slinky => connect}/oracle/component.rs (85%) rename crates/astria-sequencer/src/{slinky => connect}/oracle/currency_pair_strategy.rs (84%) rename crates/astria-sequencer/src/{slinky => connect}/oracle/mod.rs (100%) rename crates/astria-sequencer/src/{slinky => connect}/oracle/state_ext.rs (98%) rename crates/astria-sequencer/src/grpc/{slinky.rs => connect.rs} (93%) delete mode 100644 proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto rename proto/protocolapis/astria_vendored/{slinky/abci/v1 => connect/abci/v2}/vote_extensions.proto (60%) rename proto/protocolapis/astria_vendored/{slinky/marketmap/v1 => connect/marketmap/v2}/genesis.proto (59%) rename proto/protocolapis/astria_vendored/{slinky/marketmap/v1 => connect/marketmap/v2}/market.proto (85%) rename proto/protocolapis/astria_vendored/{slinky/marketmap/v1 => connect/marketmap/v2}/params.proto (63%) rename proto/protocolapis/astria_vendored/{slinky/marketmap/v1 => connect/marketmap/v2}/query.proto (71%) rename proto/protocolapis/astria_vendored/{slinky/oracle/v1 => connect/oracle/v2}/genesis.proto (85%) rename proto/protocolapis/astria_vendored/{slinky/oracle/v1 => connect/oracle/v2}/query.proto (62%) create mode 100644 proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto create mode 100644 proto/protocolapis/astria_vendored/connect/types/v2/currency_pair.proto delete mode 100644 proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto delete mode 100644 proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto diff --git a/buf.yaml b/buf.yaml index a462d0bc07..5c62be926b 100644 --- a/buf.yaml +++ b/buf.yaml @@ -63,11 +63,11 @@ modules: PACKAGE_VERSION_SUFFIX: - proto/protocolapis/astria_vendored/tendermint FIELD_LOWER_SNAKE_CASE: - - proto/protocolapis/astria_vendored/slinky + - proto/protocolapis/astria_vendored/connect SERVICE_SUFFIX: - - proto/protocolapis/astria_vendored/slinky + - proto/protocolapis/astria_vendored/connect RPC_REQUEST_STANDARD_NAME: - - proto/protocolapis/astria_vendored/slinky + - proto/protocolapis/astria_vendored/connect disallow_comment_ignores: true breaking: use: diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index f276850613..df4e9a72c4 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -120,7 +120,7 @@ {{ include "sequencer.address" $value }} {{- end }} ], - "slinky": { + "connect": { "marketMap": { "marketMap": { "markets": {} diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index ff1c3acc73..036ba3e273 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,7 +73,7 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" - ASTRIA_SEQUENCER_SLINKY_GRPC_ADDR: "http://127.0.0.1:8081" + ASTRIA_SEQUENCER_CONNECT_GRPC_ADDR: "http://127.0.0.1:8081" ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" {{- if not .Values.global.dev }} {{- else }} diff --git a/crates/astria-core/src/slinky/abci.rs b/crates/astria-core/src/connect/abci.rs similarity index 89% rename from crates/astria-core/src/slinky/abci.rs rename to crates/astria-core/src/connect/abci.rs index cb5aa77686..27fb6ac9ca 100644 --- a/crates/astria-core/src/slinky/abci.rs +++ b/crates/astria-core/src/connect/abci.rs @@ -1,13 +1,13 @@ -pub mod v1 { +pub mod v2 { use bytes::Bytes; use indexmap::IndexMap; use crate::{ - generated::astria_vendored::slinky::abci::v1 as raw, - slinky::types::v1::{ + connect::types::v2::{ CurrencyPairId, Price, }, + generated::astria_vendored::connect::abci::v2 as raw, }; #[derive(Debug, thiserror::Error)] @@ -15,12 +15,12 @@ pub mod v1 { pub struct OracleVoteExtensionError(#[from] OracleVoteExtensionErrorKind); #[derive(Debug, thiserror::Error)] - #[error("failed to validate astria_vendored.slinky.abci.v1.OracleVoteExtension")] + #[error("failed to validate astria_vendored.connect.abci.v1.OracleVoteExtension")] enum OracleVoteExtensionErrorKind { #[error("failed decoding price value in .prices field for key `{id}`")] DecodePrice { id: u64, - source: crate::slinky::types::v1::DecodePriceError, + source: crate::connect::types::v2::DecodePriceError, }, } diff --git a/crates/astria-core/src/slinky/market_map.rs b/crates/astria-core/src/connect/market_map.rs similarity index 99% rename from crates/astria-core/src/slinky/market_map.rs rename to crates/astria-core/src/connect/market_map.rs index 3dd9f85511..eea7654e86 100644 --- a/crates/astria-core/src/slinky/market_map.rs +++ b/crates/astria-core/src/connect/market_map.rs @@ -1,18 +1,18 @@ -pub mod v1 { +pub mod v2 { use std::str::FromStr; use indexmap::IndexMap; use crate::{ - generated::astria_vendored::slinky::marketmap::v1 as raw, + connect::types::v2::{ + CurrencyPair, + CurrencyPairError, + }, + generated::astria_vendored::connect::marketmap::v2 as raw, primitive::v1::{ Address, AddressError, }, - slinky::types::v1::{ - CurrencyPair, - CurrencyPairError, - }, Protobuf, }; diff --git a/crates/astria-core/src/slinky/mod.rs b/crates/astria-core/src/connect/mod.rs similarity index 100% rename from crates/astria-core/src/slinky/mod.rs rename to crates/astria-core/src/connect/mod.rs diff --git a/crates/astria-core/src/slinky/oracle.rs b/crates/astria-core/src/connect/oracle.rs similarity index 99% rename from crates/astria-core/src/slinky/oracle.rs rename to crates/astria-core/src/connect/oracle.rs index 50ca26e9d5..3397fc5e6d 100644 --- a/crates/astria-core/src/slinky/oracle.rs +++ b/crates/astria-core/src/connect/oracle.rs @@ -1,9 +1,8 @@ -pub mod v1 { +pub mod v2 { use pbjson_types::Timestamp; use crate::{ - generated::astria_vendored::slinky::oracle::v1 as raw, - slinky::types::v1::{ + connect::types::v2::{ CurrencyPair, CurrencyPairError, CurrencyPairId, @@ -11,6 +10,7 @@ pub mod v1 { ParsePriceError, Price, }, + generated::astria_vendored::connect::oracle::v2 as raw, Protobuf, }; diff --git a/crates/astria-core/src/slinky/service.rs b/crates/astria-core/src/connect/service.rs similarity index 93% rename from crates/astria-core/src/slinky/service.rs rename to crates/astria-core/src/connect/service.rs index 0cca9ffe10..36f7da5120 100644 --- a/crates/astria-core/src/slinky/service.rs +++ b/crates/astria-core/src/connect/service.rs @@ -1,14 +1,14 @@ -pub mod v1 { +pub mod v2 { use indexmap::IndexMap; use crate::{ - generated::astria_vendored::slinky::service::v1 as raw, - slinky::types::v1::{ + connect::types::v2::{ CurrencyPair, CurrencyPairParseError, ParsePriceError, Price, }, + generated::astria_vendored::connect::service::v2 as raw, }; #[derive(Debug, thiserror::Error)] @@ -37,6 +37,7 @@ pub mod v1 { pub struct QueryPricesResponse { pub prices: IndexMap, pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + pub version: String, } impl QueryPricesResponse { @@ -53,6 +54,7 @@ pub mod v1 { let raw::QueryPricesResponse { prices, timestamp, + version, } = wire; let prices = prices .into_iter() @@ -79,6 +81,7 @@ pub mod v1 { Ok(Self { prices, timestamp, + version, }) } } diff --git a/crates/astria-core/src/slinky/types.rs b/crates/astria-core/src/connect/types.rs similarity index 98% rename from crates/astria-core/src/slinky/types.rs rename to crates/astria-core/src/connect/types.rs index 5440575c5a..633b2c9981 100644 --- a/crates/astria-core/src/slinky/types.rs +++ b/crates/astria-core/src/connect/types.rs @@ -1,4 +1,4 @@ -pub mod v1 { +pub mod v2 { use std::{ fmt::Display, num::ParseIntError, @@ -7,7 +7,7 @@ pub mod v1 { use bytes::Bytes; - use crate::generated::astria_vendored::slinky::types::v1 as raw; + use crate::generated::astria_vendored::connect::types::v2 as raw; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Price(u128); @@ -354,7 +354,7 @@ pub mod v1 { #[cfg(test)] mod test { - use super::v1::CurrencyPair; + use super::v2::CurrencyPair; #[test] fn currency_pair_parse() { diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs index c5ecec32c7..20b5ec8320 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs @@ -28,7 +28,7 @@ pub struct GenesisAppState { #[prost(message, optional, tag = "10")] pub fees: ::core::option::Option, #[prost(message, optional, tag = "11")] - pub slinky: ::core::option::Option, + pub connect: ::core::option::Option, } impl ::prost::Name for GenesisAppState { const NAME: &'static str = "GenesisAppState"; @@ -157,18 +157,18 @@ impl ::prost::Name for GenesisFees { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct SlinkyGenesis { +pub struct ConnectGenesis { #[prost(message, optional, tag = "1")] pub market_map: ::core::option::Option< - super::super::super::super::astria_vendored::slinky::marketmap::v1::GenesisState, + super::super::super::super::astria_vendored::connect::marketmap::v2::GenesisState, >, #[prost(message, optional, tag = "2")] pub oracle: ::core::option::Option< - super::super::super::super::astria_vendored::slinky::oracle::v1::GenesisState, + super::super::super::super::astria_vendored::connect::oracle::v2::GenesisState, >, } -impl ::prost::Name for SlinkyGenesis { - const NAME: &'static str = "SlinkyGenesis"; +impl ::prost::Name for ConnectGenesis { + const NAME: &'static str = "ConnectGenesis"; const PACKAGE: &'static str = "astria.protocol.genesis.v1"; fn full_name() -> ::prost::alloc::string::String { ::prost::alloc::format!("astria.protocol.genesis.v1.{}", Self::NAME) diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1.serde.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1.serde.rs index 5e0b162de1..9ca59c33ca 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1.serde.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1.serde.rs @@ -215,6 +215,115 @@ impl<'de> serde::Deserialize<'de> for AddressPrefixes { deserializer.deserialize_struct("astria.protocol.genesis.v1.AddressPrefixes", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for ConnectGenesis { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.market_map.is_some() { + len += 1; + } + if self.oracle.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1.ConnectGenesis", len)?; + if let Some(v) = self.market_map.as_ref() { + struct_ser.serialize_field("marketMap", v)?; + } + if let Some(v) = self.oracle.as_ref() { + struct_ser.serialize_field("oracle", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ConnectGenesis { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "market_map", + "marketMap", + "oracle", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MarketMap, + Oracle, + } + 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 { + "marketMap" | "market_map" => Ok(GeneratedField::MarketMap), + "oracle" => Ok(GeneratedField::Oracle), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ConnectGenesis; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.protocol.genesis.v1.ConnectGenesis") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut market_map__ = None; + let mut oracle__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MarketMap => { + if market_map__.is_some() { + return Err(serde::de::Error::duplicate_field("marketMap")); + } + market_map__ = map_.next_value()?; + } + GeneratedField::Oracle => { + if oracle__.is_some() { + return Err(serde::de::Error::duplicate_field("oracle")); + } + oracle__ = map_.next_value()?; + } + } + } + Ok(ConnectGenesis { + market_map: market_map__, + oracle: oracle__, + }) + } + } + deserializer.deserialize_struct("astria.protocol.genesis.v1.ConnectGenesis", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for GenesisAppState { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -253,7 +362,7 @@ impl serde::Serialize for GenesisAppState { if self.fees.is_some() { len += 1; } - if self.slinky.is_some() { + if self.connect.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1.GenesisAppState", len)?; @@ -287,8 +396,8 @@ impl serde::Serialize for GenesisAppState { if let Some(v) = self.fees.as_ref() { struct_ser.serialize_field("fees", v)?; } - if let Some(v) = self.slinky.as_ref() { - struct_ser.serialize_field("slinky", v)?; + if let Some(v) = self.connect.as_ref() { + struct_ser.serialize_field("connect", v)?; } struct_ser.end() } @@ -318,7 +427,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "allowed_fee_assets", "allowedFeeAssets", "fees", - "slinky", + "connect", ]; #[allow(clippy::enum_variant_names)] @@ -333,7 +442,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { IbcParameters, AllowedFeeAssets, Fees, - Slinky, + Connect, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -365,7 +474,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { "ibcParameters" | "ibc_parameters" => Ok(GeneratedField::IbcParameters), "allowedFeeAssets" | "allowed_fee_assets" => Ok(GeneratedField::AllowedFeeAssets), "fees" => Ok(GeneratedField::Fees), - "slinky" => Ok(GeneratedField::Slinky), + "connect" => Ok(GeneratedField::Connect), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -395,7 +504,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { let mut ibc_parameters__ = None; let mut allowed_fee_assets__ = None; let mut fees__ = None; - let mut slinky__ = None; + let mut connect__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::ChainId => { @@ -458,11 +567,11 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { } fees__ = map_.next_value()?; } - GeneratedField::Slinky => { - if slinky__.is_some() { - return Err(serde::de::Error::duplicate_field("slinky")); + GeneratedField::Connect => { + if connect__.is_some() { + return Err(serde::de::Error::duplicate_field("connect")); } - slinky__ = map_.next_value()?; + connect__ = map_.next_value()?; } } } @@ -477,7 +586,7 @@ impl<'de> serde::Deserialize<'de> for GenesisAppState { ibc_parameters: ibc_parameters__, allowed_fee_assets: allowed_fee_assets__.unwrap_or_default(), fees: fees__, - slinky: slinky__, + connect: connect__, }) } } @@ -937,112 +1046,3 @@ impl<'de> serde::Deserialize<'de> for IbcParameters { deserializer.deserialize_struct("astria.protocol.genesis.v1.IbcParameters", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for SlinkyGenesis { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.market_map.is_some() { - len += 1; - } - if self.oracle.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.protocol.genesis.v1.SlinkyGenesis", len)?; - if let Some(v) = self.market_map.as_ref() { - struct_ser.serialize_field("marketMap", v)?; - } - if let Some(v) = self.oracle.as_ref() { - struct_ser.serialize_field("oracle", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for SlinkyGenesis { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "market_map", - "marketMap", - "oracle", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - MarketMap, - Oracle, - } - 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 { - "marketMap" | "market_map" => Ok(GeneratedField::MarketMap), - "oracle" => Ok(GeneratedField::Oracle), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = SlinkyGenesis; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.protocol.genesis.v1.SlinkyGenesis") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut market_map__ = None; - let mut oracle__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::MarketMap => { - if market_map__.is_some() { - return Err(serde::de::Error::duplicate_field("marketMap")); - } - market_map__ = map_.next_value()?; - } - GeneratedField::Oracle => { - if oracle__.is_some() { - return Err(serde::de::Error::duplicate_field("oracle")); - } - oracle__ = map_.next_value()?; - } - } - } - Ok(SlinkyGenesis { - market_map: market_map__, - oracle: oracle__, - }) - } - } - deserializer.deserialize_struct("astria.protocol.genesis.v1.SlinkyGenesis", FIELDS, GeneratedVisitor) - } -} diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.rs b/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.rs similarity index 82% rename from crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.rs rename to crates/astria-core/src/generated/astria_vendored.connect.abci.v2.rs index 404700652f..287d08a45f 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.rs @@ -10,8 +10,8 @@ pub struct OracleVoteExtension { } impl ::prost::Name for OracleVoteExtension { const NAME: &'static str = "OracleVoteExtension"; - const PACKAGE: &'static str = "astria_vendored.slinky.abci.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.abci.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.abci.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.abci.v2.{}", Self::NAME) } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.serde.rs similarity index 92% rename from crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.serde.rs rename to crates/astria-core/src/generated/astria_vendored.connect.abci.v2.serde.rs index d491536570..be62d28c24 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.abci.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.serde.rs @@ -9,7 +9,7 @@ impl serde::Serialize for OracleVoteExtension { if !self.prices.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.abci.v1.OracleVoteExtension", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.abci.v2.OracleVoteExtension", len)?; if !self.prices.is_empty() { let v: std::collections::HashMap<_, _> = self.prices.iter() .map(|(k, v)| (k, pbjson::private::base64::encode(v))).collect(); @@ -65,7 +65,7 @@ impl<'de> serde::Deserialize<'de> for OracleVoteExtension { type Value = OracleVoteExtension; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.abci.v1.OracleVoteExtension") + formatter.write_str("struct astria_vendored.connect.abci.v2.OracleVoteExtension") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -91,6 +91,6 @@ impl<'de> serde::Deserialize<'de> for OracleVoteExtension { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.abci.v1.OracleVoteExtension", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.abci.v2.OracleVoteExtension", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.rs b/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.rs similarity index 90% rename from crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.rs rename to crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.rs index 1104ea451b..f632496531 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.rs @@ -13,9 +13,9 @@ pub struct Market { } impl ::prost::Name for Market { const NAME: &'static str = "Market"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// Ticker represents a price feed for a given asset pair i.e. BTC/USD. The price @@ -26,7 +26,7 @@ impl ::prost::Name for Market { pub struct Ticker { /// CurrencyPair is the currency pair for this ticker. #[prost(message, optional, tag = "1")] - pub currency_pair: ::core::option::Option, + pub currency_pair: ::core::option::Option, /// Decimals is the number of decimal places for the ticker. The number of /// decimal places is used to convert the price to a human-readable format. #[prost(uint64, tag = "2")] @@ -46,9 +46,9 @@ pub struct Ticker { } impl ::prost::Name for Ticker { const NAME: &'static str = "Ticker"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -68,7 +68,7 @@ pub struct ProviderConfig { /// using: OffChainTicker = BTC/USDT NormalizeByPair = USDT/USD This field is /// optional and nullable. #[prost(message, optional, tag = "3")] - pub normalize_by_pair: ::core::option::Option, + pub normalize_by_pair: ::core::option::Option, /// Invert is a boolean indicating if the BASE and QUOTE of the market should /// be inverted. i.e. BASE -> QUOTE, QUOTE -> BASE #[prost(bool, tag = "4")] @@ -80,9 +80,9 @@ pub struct ProviderConfig { } impl ::prost::Name for ProviderConfig { const NAME: &'static str = "ProviderConfig"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// MarketMap maps ticker strings to their Markets. @@ -99,9 +99,9 @@ pub struct MarketMap { } impl ::prost::Name for MarketMap { const NAME: &'static str = "MarketMap"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// Params defines the parameters for the x/marketmap module. @@ -119,9 +119,9 @@ pub struct Params { } impl ::prost::Name for Params { const NAME: &'static str = "Params"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// GenesisState defines the x/marketmap module's genesis state. @@ -143,9 +143,9 @@ pub struct GenesisState { } impl ::prost::Name for GenesisState { const NAME: &'static str = "GenesisState"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// MarketMapRequest is the query request for the MarketMap query. @@ -155,9 +155,9 @@ impl ::prost::Name for GenesisState { pub struct MarketMapRequest {} impl ::prost::Name for MarketMapRequest { const NAME: &'static str = "MarketMapRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// MarketMapResponse is the query response for the MarketMap query. @@ -179,9 +179,9 @@ pub struct MarketMapResponse { } impl ::prost::Name for MarketMapResponse { const NAME: &'static str = "MarketMapResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// MarketRequest is the query request for the Market query. @@ -192,13 +192,13 @@ pub struct MarketRequest { /// CurrencyPair is the currency pair associated with the market being /// requested. #[prost(message, optional, tag = "1")] - pub currency_pair: ::core::option::Option, + pub currency_pair: ::core::option::Option, } impl ::prost::Name for MarketRequest { const NAME: &'static str = "MarketRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// MarketResponse is the query response for the Market query. @@ -211,9 +211,9 @@ pub struct MarketResponse { } impl ::prost::Name for MarketResponse { const NAME: &'static str = "MarketResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// ParamsRequest is the request type for the Query/Params RPC method. @@ -222,9 +222,9 @@ impl ::prost::Name for MarketResponse { pub struct ParamsRequest {} impl ::prost::Name for ParamsRequest { const NAME: &'static str = "ParamsRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// ParamsResponse is the response type for the Query/Params RPC method. @@ -236,9 +236,9 @@ pub struct ParamsResponse { } impl ::prost::Name for ParamsResponse { const NAME: &'static str = "ParamsResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// LastUpdatedRequest is the request type for the Query/LastUpdated RPC @@ -248,9 +248,9 @@ impl ::prost::Name for ParamsResponse { pub struct LastUpdatedRequest {} impl ::prost::Name for LastUpdatedRequest { const NAME: &'static str = "LastUpdatedRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// LastUpdatedResponse is the response type for the Query/LastUpdated RPC @@ -263,9 +263,9 @@ pub struct LastUpdatedResponse { } impl ::prost::Name for LastUpdatedResponse { const NAME: &'static str = "LastUpdatedResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.marketmap.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.marketmap.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) } } /// Generated client implementations. @@ -375,13 +375,13 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.marketmap.v1.Query/MarketMap", + "/astria_vendored.connect.marketmap.v2.Query/MarketMap", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.marketmap.v1.Query", + "astria_vendored.connect.marketmap.v2.Query", "MarketMap", ), ); @@ -404,13 +404,13 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.marketmap.v1.Query/Market", + "/astria_vendored.connect.marketmap.v2.Query/Market", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.marketmap.v1.Query", + "astria_vendored.connect.marketmap.v2.Query", "Market", ), ); @@ -435,13 +435,13 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.marketmap.v1.Query/LastUpdated", + "/astria_vendored.connect.marketmap.v2.Query/LastUpdated", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.marketmap.v1.Query", + "astria_vendored.connect.marketmap.v2.Query", "LastUpdated", ), ); @@ -463,13 +463,13 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.marketmap.v1.Query/Params", + "/astria_vendored.connect.marketmap.v2.Query/Params", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.marketmap.v1.Query", + "astria_vendored.connect.marketmap.v2.Query", "Params", ), ); @@ -594,7 +594,7 @@ pub mod query_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { - "/astria_vendored.slinky.marketmap.v1.Query/MarketMap" => { + "/astria_vendored.connect.marketmap.v2.Query/MarketMap" => { #[allow(non_camel_case_types)] struct MarketMapSvc(pub Arc); impl tonic::server::UnaryService @@ -638,7 +638,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.slinky.marketmap.v1.Query/Market" => { + "/astria_vendored.connect.marketmap.v2.Query/Market" => { #[allow(non_camel_case_types)] struct MarketSvc(pub Arc); impl tonic::server::UnaryService @@ -682,7 +682,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.slinky.marketmap.v1.Query/LastUpdated" => { + "/astria_vendored.connect.marketmap.v2.Query/LastUpdated" => { #[allow(non_camel_case_types)] struct LastUpdatedSvc(pub Arc); impl tonic::server::UnaryService @@ -726,7 +726,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.slinky.marketmap.v1.Query/Params" => { + "/astria_vendored.connect.marketmap.v2.Query/Params" => { #[allow(non_camel_case_types)] struct ParamsSvc(pub Arc); impl tonic::server::UnaryService @@ -808,6 +808,6 @@ pub mod query_server { } } impl tonic::server::NamedService for QueryServer { - const NAME: &'static str = "astria_vendored.slinky.marketmap.v1.Query"; + const NAME: &'static str = "astria_vendored.connect.marketmap.v2.Query"; } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.serde.rs similarity index 93% rename from crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.serde.rs rename to crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.serde.rs index a0effe4302..f3e7392f13 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.marketmap.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.serde.rs @@ -15,7 +15,7 @@ impl serde::Serialize for GenesisState { if self.params.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.GenesisState", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.GenesisState", len)?; if let Some(v) = self.market_map.as_ref() { struct_ser.serialize_field("marketMap", v)?; } @@ -84,7 +84,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { type Value = GenesisState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.GenesisState") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.GenesisState") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -125,7 +125,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.GenesisState", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.GenesisState", FIELDS, GeneratedVisitor) } } impl serde::Serialize for LastUpdatedRequest { @@ -136,7 +136,7 @@ impl serde::Serialize for LastUpdatedRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.LastUpdatedRequest", len)?; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedRequest", len)?; struct_ser.end() } } @@ -182,7 +182,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedRequest { type Value = LastUpdatedRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.LastUpdatedRequest") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.LastUpdatedRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -196,7 +196,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.LastUpdatedRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for LastUpdatedResponse { @@ -210,7 +210,7 @@ impl serde::Serialize for LastUpdatedResponse { if self.last_updated != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.LastUpdatedResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedResponse", len)?; if self.last_updated != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("lastUpdated", ToString::to_string(&self.last_updated).as_str())?; @@ -266,7 +266,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedResponse { type Value = LastUpdatedResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.LastUpdatedResponse") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.LastUpdatedResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -291,7 +291,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.LastUpdatedResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for Market { @@ -308,7 +308,7 @@ impl serde::Serialize for Market { if !self.provider_configs.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.Market", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.Market", len)?; if let Some(v) = self.ticker.as_ref() { struct_ser.serialize_field("ticker", v)?; } @@ -369,7 +369,7 @@ impl<'de> serde::Deserialize<'de> for Market { type Value = Market; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.Market") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.Market") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -400,7 +400,7 @@ impl<'de> serde::Deserialize<'de> for Market { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.Market", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.Market", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketMap { @@ -414,7 +414,7 @@ impl serde::Serialize for MarketMap { if !self.markets.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.MarketMap", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketMap", len)?; if !self.markets.is_empty() { struct_ser.serialize_field("markets", &self.markets)?; } @@ -468,7 +468,7 @@ impl<'de> serde::Deserialize<'de> for MarketMap { type Value = MarketMap; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.MarketMap") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketMap") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -493,7 +493,7 @@ impl<'de> serde::Deserialize<'de> for MarketMap { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.MarketMap", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketMap", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketMapRequest { @@ -504,7 +504,7 @@ impl serde::Serialize for MarketMapRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.MarketMapRequest", len)?; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketMapRequest", len)?; struct_ser.end() } } @@ -550,7 +550,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapRequest { type Value = MarketMapRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.MarketMapRequest") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketMapRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -564,7 +564,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.MarketMapRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketMapRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketMapResponse { @@ -584,7 +584,7 @@ impl serde::Serialize for MarketMapResponse { if !self.chain_id.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.MarketMapResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketMapResponse", len)?; if let Some(v) = self.market_map.as_ref() { struct_ser.serialize_field("marketMap", v)?; } @@ -654,7 +654,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapResponse { type Value = MarketMapResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.MarketMapResponse") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketMapResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -695,7 +695,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.MarketMapResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketMapResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketRequest { @@ -709,7 +709,7 @@ impl serde::Serialize for MarketRequest { if self.currency_pair.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.MarketRequest", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketRequest", len)?; if let Some(v) = self.currency_pair.as_ref() { struct_ser.serialize_field("currencyPair", v)?; } @@ -764,7 +764,7 @@ impl<'de> serde::Deserialize<'de> for MarketRequest { type Value = MarketRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.MarketRequest") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -787,7 +787,7 @@ impl<'de> serde::Deserialize<'de> for MarketRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.MarketRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketResponse { @@ -801,7 +801,7 @@ impl serde::Serialize for MarketResponse { if self.market.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.MarketResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketResponse", len)?; if let Some(v) = self.market.as_ref() { struct_ser.serialize_field("market", v)?; } @@ -855,7 +855,7 @@ impl<'de> serde::Deserialize<'de> for MarketResponse { type Value = MarketResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.MarketResponse") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -878,7 +878,7 @@ impl<'de> serde::Deserialize<'de> for MarketResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.MarketResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for Params { @@ -895,7 +895,7 @@ impl serde::Serialize for Params { if !self.admin.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.Params", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.Params", len)?; if !self.market_authorities.is_empty() { struct_ser.serialize_field("marketAuthorities", &self.market_authorities)?; } @@ -956,7 +956,7 @@ impl<'de> serde::Deserialize<'de> for Params { type Value = Params; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.Params") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.Params") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -987,7 +987,7 @@ impl<'de> serde::Deserialize<'de> for Params { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.Params", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.Params", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ParamsRequest { @@ -998,7 +998,7 @@ impl serde::Serialize for ParamsRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.ParamsRequest", len)?; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.ParamsRequest", len)?; struct_ser.end() } } @@ -1044,7 +1044,7 @@ impl<'de> serde::Deserialize<'de> for ParamsRequest { type Value = ParamsRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.ParamsRequest") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.ParamsRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1058,7 +1058,7 @@ impl<'de> serde::Deserialize<'de> for ParamsRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.ParamsRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.ParamsRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ParamsResponse { @@ -1072,7 +1072,7 @@ impl serde::Serialize for ParamsResponse { if self.params.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.ParamsResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.ParamsResponse", len)?; if let Some(v) = self.params.as_ref() { struct_ser.serialize_field("params", v)?; } @@ -1126,7 +1126,7 @@ impl<'de> serde::Deserialize<'de> for ParamsResponse { type Value = ParamsResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.ParamsResponse") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.ParamsResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1149,7 +1149,7 @@ impl<'de> serde::Deserialize<'de> for ParamsResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.ParamsResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.ParamsResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ProviderConfig { @@ -1175,7 +1175,7 @@ impl serde::Serialize for ProviderConfig { if !self.metadata_json.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.ProviderConfig", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.ProviderConfig", len)?; if !self.name.is_empty() { struct_ser.serialize_field("name", &self.name)?; } @@ -1256,7 +1256,7 @@ impl<'de> serde::Deserialize<'de> for ProviderConfig { type Value = ProviderConfig; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.ProviderConfig") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.ProviderConfig") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1311,7 +1311,7 @@ impl<'de> serde::Deserialize<'de> for ProviderConfig { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.ProviderConfig", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.ProviderConfig", FIELDS, GeneratedVisitor) } } impl serde::Serialize for Ticker { @@ -1337,7 +1337,7 @@ impl serde::Serialize for Ticker { if !self.metadata_json.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.marketmap.v1.Ticker", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.Ticker", len)?; if let Some(v) = self.currency_pair.as_ref() { struct_ser.serialize_field("currencyPair", v)?; } @@ -1420,7 +1420,7 @@ impl<'de> serde::Deserialize<'de> for Ticker { type Value = Ticker; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.marketmap.v1.Ticker") + formatter.write_str("struct astria_vendored.connect.marketmap.v2.Ticker") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1479,6 +1479,6 @@ impl<'de> serde::Deserialize<'de> for Ticker { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.marketmap.v1.Ticker", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.Ticker", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.rs b/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.rs similarity index 90% rename from crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.rs rename to crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.rs index 56a5da8790..a851fa6405 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.rs @@ -16,9 +16,9 @@ pub struct QuotePrice { } impl ::prost::Name for QuotePrice { const NAME: &'static str = "QuotePrice"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// CurrencyPairState represents the stateful information tracked by the x/oracle @@ -39,9 +39,9 @@ pub struct CurrencyPairState { } impl ::prost::Name for CurrencyPairState { const NAME: &'static str = "CurrencyPairState"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// CurrencyPairGenesis is the information necessary for initialization of a @@ -51,7 +51,7 @@ impl ::prost::Name for CurrencyPairState { pub struct CurrencyPairGenesis { /// The CurrencyPair to be added to module state #[prost(message, optional, tag = "1")] - pub currency_pair: ::core::option::Option, + pub currency_pair: ::core::option::Option, /// A genesis price if one exists (note this will be empty, unless it results /// from forking the state of this module) #[prost(message, optional, tag = "2")] @@ -66,9 +66,9 @@ pub struct CurrencyPairGenesis { } impl ::prost::Name for CurrencyPairGenesis { const NAME: &'static str = "CurrencyPairGenesis"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// GenesisState is the genesis-state for the x/oracle module, it takes a set of @@ -87,9 +87,9 @@ pub struct GenesisState { } impl ::prost::Name for GenesisState { const NAME: &'static str = "GenesisState"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -97,9 +97,9 @@ impl ::prost::Name for GenesisState { pub struct GetAllCurrencyPairsRequest {} impl ::prost::Name for GetAllCurrencyPairsRequest { const NAME: &'static str = "GetAllCurrencyPairsRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// GetAllCurrencyPairsResponse returns all CurrencyPairs that the module is @@ -108,29 +108,29 @@ impl ::prost::Name for GetAllCurrencyPairsRequest { #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetAllCurrencyPairsResponse { #[prost(message, repeated, tag = "1")] - pub currency_pairs: ::prost::alloc::vec::Vec, + pub currency_pairs: ::prost::alloc::vec::Vec, } impl ::prost::Name for GetAllCurrencyPairsResponse { const NAME: &'static str = "GetAllCurrencyPairsResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } -/// GetPriceRequest either takes a CurrencyPair, or an identifier for the +/// GetPriceRequest takes an identifier for the /// CurrencyPair in the format base/quote. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetPriceRequest { /// CurrencyPair represents the pair that the user wishes to query. - #[prost(message, optional, tag = "1")] - pub currency_pair: ::core::option::Option, + #[prost(string, tag = "1")] + pub currency_pair: ::prost::alloc::string::String, } impl ::prost::Name for GetPriceRequest { const NAME: &'static str = "GetPriceRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// GetPriceResponse is the response from the GetPrice grpc method exposed from @@ -146,8 +146,7 @@ pub struct GetPriceResponse { #[prost(uint64, tag = "2")] pub nonce: u64, /// decimals represents the number of decimals that the quote-price is - /// represented in. For Pairs where ETHEREUM is the quote this will be 18, - /// otherwise it will be 8. + /// represented in. It is used to scale the QuotePrice to its proper value. #[prost(uint64, tag = "3")] pub decimals: u64, /// ID represents the identifier for the CurrencyPair. @@ -156,9 +155,9 @@ pub struct GetPriceResponse { } impl ::prost::Name for GetPriceResponse { const NAME: &'static str = "GetPriceResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// GetPricesRequest takes an identifier for the CurrencyPair @@ -171,9 +170,9 @@ pub struct GetPricesRequest { } impl ::prost::Name for GetPricesRequest { const NAME: &'static str = "GetPricesRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// GetPricesResponse is the response from the GetPrices grpc method exposed from @@ -186,9 +185,9 @@ pub struct GetPricesResponse { } impl ::prost::Name for GetPricesResponse { const NAME: &'static str = "GetPricesResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// GetCurrencyPairMappingRequest is the GetCurrencyPairMapping request type. @@ -197,9 +196,9 @@ impl ::prost::Name for GetPricesResponse { pub struct GetCurrencyPairMappingRequest {} impl ::prost::Name for GetCurrencyPairMappingRequest { const NAME: &'static str = "GetCurrencyPairMappingRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// GetCurrencyPairMappingResponse is the GetCurrencyPairMapping response type. @@ -211,14 +210,14 @@ pub struct GetCurrencyPairMappingResponse { #[prost(btree_map = "uint64, message", tag = "1")] pub currency_pair_mapping: ::prost::alloc::collections::BTreeMap< u64, - super::super::types::v1::CurrencyPair, + super::super::types::v2::CurrencyPair, >, } impl ::prost::Name for GetCurrencyPairMappingResponse { const NAME: &'static str = "GetCurrencyPairMappingResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.oracle.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.oracle.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) } } /// Generated client implementations. @@ -327,13 +326,13 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.oracle.v1.Query/GetAllCurrencyPairs", + "/astria_vendored.connect.oracle.v2.Query/GetAllCurrencyPairs", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.oracle.v1.Query", + "astria_vendored.connect.oracle.v2.Query", "GetAllCurrencyPairs", ), ); @@ -359,12 +358,15 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.oracle.v1.Query/GetPrice", + "/astria_vendored.connect.oracle.v2.Query/GetPrice", ); let mut req = request.into_request(); req.extensions_mut() .insert( - GrpcMethod::new("astria_vendored.slinky.oracle.v1.Query", "GetPrice"), + GrpcMethod::new( + "astria_vendored.connect.oracle.v2.Query", + "GetPrice", + ), ); self.inner.unary(req, path, codec).await } @@ -386,13 +388,13 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.oracle.v1.Query/GetPrices", + "/astria_vendored.connect.oracle.v2.Query/GetPrices", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.oracle.v1.Query", + "astria_vendored.connect.oracle.v2.Query", "GetPrices", ), ); @@ -419,13 +421,13 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.oracle.v1.Query/GetCurrencyPairMapping", + "/astria_vendored.connect.oracle.v2.Query/GetCurrencyPairMapping", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.oracle.v1.Query", + "astria_vendored.connect.oracle.v2.Query", "GetCurrencyPairMapping", ), ); @@ -556,7 +558,7 @@ pub mod query_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { - "/astria_vendored.slinky.oracle.v1.Query/GetAllCurrencyPairs" => { + "/astria_vendored.connect.oracle.v2.Query/GetAllCurrencyPairs" => { #[allow(non_camel_case_types)] struct GetAllCurrencyPairsSvc(pub Arc); impl< @@ -602,7 +604,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.slinky.oracle.v1.Query/GetPrice" => { + "/astria_vendored.connect.oracle.v2.Query/GetPrice" => { #[allow(non_camel_case_types)] struct GetPriceSvc(pub Arc); impl tonic::server::UnaryService @@ -646,7 +648,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.slinky.oracle.v1.Query/GetPrices" => { + "/astria_vendored.connect.oracle.v2.Query/GetPrices" => { #[allow(non_camel_case_types)] struct GetPricesSvc(pub Arc); impl tonic::server::UnaryService @@ -690,7 +692,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.slinky.oracle.v1.Query/GetCurrencyPairMapping" => { + "/astria_vendored.connect.oracle.v2.Query/GetCurrencyPairMapping" => { #[allow(non_camel_case_types)] struct GetCurrencyPairMappingSvc(pub Arc); impl< @@ -775,6 +777,6 @@ pub mod query_server { } } impl tonic::server::NamedService for QueryServer { - const NAME: &'static str = "astria_vendored.slinky.oracle.v1.Query"; + const NAME: &'static str = "astria_vendored.connect.oracle.v2.Query"; } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.serde.rs similarity index 92% rename from crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.serde.rs rename to crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.serde.rs index 50e9193673..efb0030d28 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.oracle.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.serde.rs @@ -18,7 +18,7 @@ impl serde::Serialize for CurrencyPairGenesis { if self.id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.CurrencyPairGenesis", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairGenesis", len)?; if let Some(v) = self.currency_pair.as_ref() { struct_ser.serialize_field("currencyPair", v)?; } @@ -94,7 +94,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairGenesis { type Value = CurrencyPairGenesis; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.CurrencyPairGenesis") + formatter.write_str("struct astria_vendored.connect.oracle.v2.CurrencyPairGenesis") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -145,7 +145,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairGenesis { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.CurrencyPairGenesis", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairGenesis", FIELDS, GeneratedVisitor) } } impl serde::Serialize for CurrencyPairState { @@ -165,7 +165,7 @@ impl serde::Serialize for CurrencyPairState { if self.id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.CurrencyPairState", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairState", len)?; if let Some(v) = self.price.as_ref() { struct_ser.serialize_field("price", v)?; } @@ -233,7 +233,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairState { type Value = CurrencyPairState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.CurrencyPairState") + formatter.write_str("struct astria_vendored.connect.oracle.v2.CurrencyPairState") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -276,7 +276,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairState { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.CurrencyPairState", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairState", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GenesisState { @@ -293,7 +293,7 @@ impl serde::Serialize for GenesisState { if self.next_id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GenesisState", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GenesisState", len)?; if !self.currency_pair_genesis.is_empty() { struct_ser.serialize_field("currencyPairGenesis", &self.currency_pair_genesis)?; } @@ -356,7 +356,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { type Value = GenesisState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GenesisState") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GenesisState") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -389,7 +389,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GenesisState", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GenesisState", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetAllCurrencyPairsRequest { @@ -400,7 +400,7 @@ impl serde::Serialize for GetAllCurrencyPairsRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetAllCurrencyPairsRequest", len)?; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsRequest", len)?; struct_ser.end() } } @@ -446,7 +446,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsRequest { type Value = GetAllCurrencyPairsRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetAllCurrencyPairsRequest") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetAllCurrencyPairsRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -460,7 +460,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetAllCurrencyPairsRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetAllCurrencyPairsResponse { @@ -474,7 +474,7 @@ impl serde::Serialize for GetAllCurrencyPairsResponse { if !self.currency_pairs.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetAllCurrencyPairsResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsResponse", len)?; if !self.currency_pairs.is_empty() { struct_ser.serialize_field("currencyPairs", &self.currency_pairs)?; } @@ -529,7 +529,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsResponse { type Value = GetAllCurrencyPairsResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetAllCurrencyPairsResponse") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetAllCurrencyPairsResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -552,7 +552,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetAllCurrencyPairsResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetCurrencyPairMappingRequest { @@ -563,7 +563,7 @@ impl serde::Serialize for GetCurrencyPairMappingRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetCurrencyPairMappingRequest", len)?; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingRequest", len)?; struct_ser.end() } } @@ -609,7 +609,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingRequest { type Value = GetCurrencyPairMappingRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetCurrencyPairMappingRequest") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetCurrencyPairMappingRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -623,7 +623,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetCurrencyPairMappingRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetCurrencyPairMappingResponse { @@ -637,7 +637,7 @@ impl serde::Serialize for GetCurrencyPairMappingResponse { if !self.currency_pair_mapping.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetCurrencyPairMappingResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingResponse", len)?; if !self.currency_pair_mapping.is_empty() { struct_ser.serialize_field("currencyPairMapping", &self.currency_pair_mapping)?; } @@ -692,7 +692,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingResponse { type Value = GetCurrencyPairMappingResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetCurrencyPairMappingResponse") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetCurrencyPairMappingResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -718,7 +718,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetCurrencyPairMappingResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPriceRequest { @@ -729,12 +729,12 @@ impl serde::Serialize for GetPriceRequest { { use serde::ser::SerializeStruct; let mut len = 0; - if self.currency_pair.is_some() { + if !self.currency_pair.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetPriceRequest", len)?; - if let Some(v) = self.currency_pair.as_ref() { - struct_ser.serialize_field("currencyPair", v)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPriceRequest", len)?; + if !self.currency_pair.is_empty() { + struct_ser.serialize_field("currencyPair", &self.currency_pair)?; } struct_ser.end() } @@ -787,7 +787,7 @@ impl<'de> serde::Deserialize<'de> for GetPriceRequest { type Value = GetPriceRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetPriceRequest") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPriceRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -801,16 +801,16 @@ impl<'de> serde::Deserialize<'de> for GetPriceRequest { if currency_pair__.is_some() { return Err(serde::de::Error::duplicate_field("currencyPair")); } - currency_pair__ = map_.next_value()?; + currency_pair__ = Some(map_.next_value()?); } } } Ok(GetPriceRequest { - currency_pair: currency_pair__, + currency_pair: currency_pair__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetPriceRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPriceRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPriceResponse { @@ -833,7 +833,7 @@ impl serde::Serialize for GetPriceResponse { if self.id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetPriceResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPriceResponse", len)?; if let Some(v) = self.price.as_ref() { struct_ser.serialize_field("price", v)?; } @@ -908,7 +908,7 @@ impl<'de> serde::Deserialize<'de> for GetPriceResponse { type Value = GetPriceResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetPriceResponse") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPriceResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -961,7 +961,7 @@ impl<'de> serde::Deserialize<'de> for GetPriceResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetPriceResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPriceResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPricesRequest { @@ -975,7 +975,7 @@ impl serde::Serialize for GetPricesRequest { if !self.currency_pair_ids.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetPricesRequest", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPricesRequest", len)?; if !self.currency_pair_ids.is_empty() { struct_ser.serialize_field("currencyPairIds", &self.currency_pair_ids)?; } @@ -1030,7 +1030,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesRequest { type Value = GetPricesRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetPricesRequest") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPricesRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1053,7 +1053,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetPricesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPricesRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPricesResponse { @@ -1067,7 +1067,7 @@ impl serde::Serialize for GetPricesResponse { if !self.prices.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.GetPricesResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPricesResponse", len)?; if !self.prices.is_empty() { struct_ser.serialize_field("prices", &self.prices)?; } @@ -1121,7 +1121,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesResponse { type Value = GetPricesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.GetPricesResponse") + formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPricesResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1144,7 +1144,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.GetPricesResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPricesResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QuotePrice { @@ -1164,7 +1164,7 @@ impl serde::Serialize for QuotePrice { if self.block_height != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.oracle.v1.QuotePrice", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.QuotePrice", len)?; if !self.price.is_empty() { struct_ser.serialize_field("price", &self.price)?; } @@ -1233,7 +1233,7 @@ impl<'de> serde::Deserialize<'de> for QuotePrice { type Value = QuotePrice; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.oracle.v1.QuotePrice") + formatter.write_str("struct astria_vendored.connect.oracle.v2.QuotePrice") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1274,6 +1274,6 @@ impl<'de> serde::Deserialize<'de> for QuotePrice { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.oracle.v1.QuotePrice", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.QuotePrice", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.rs b/crates/astria-core/src/generated/astria_vendored.connect.service.v2.rs similarity index 72% rename from crates/astria-core/src/generated/astria_vendored.slinky.service.v1.rs rename to crates/astria-core/src/generated/astria_vendored.connect.service.v2.rs index 4e4477d3ae..ab3e31e083 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.service.v2.rs @@ -4,29 +4,33 @@ pub struct QueryPricesRequest {} impl ::prost::Name for QueryPricesRequest { const NAME: &'static str = "QueryPricesRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.service.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.service.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) } } /// QueryPricesResponse defines the response type for the Prices method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryPricesResponse { - /// prices defines the list of prices. + /// Prices defines the list of prices. #[prost(btree_map = "string, string", tag = "1")] pub prices: ::prost::alloc::collections::BTreeMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, + /// Timestamp defines the timestamp of the prices. #[prost(message, optional, tag = "2")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + /// Version defines the version of the oracle service that provided the prices. + #[prost(string, tag = "3")] + pub version: ::prost::alloc::string::String, } impl ::prost::Name for QueryPricesResponse { const NAME: &'static str = "QueryPricesResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.service.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.service.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) } } /// QueryMarketMapRequest defines the request type for the MarketMap method. @@ -35,24 +39,50 @@ impl ::prost::Name for QueryPricesResponse { pub struct QueryMarketMapRequest {} impl ::prost::Name for QueryMarketMapRequest { const NAME: &'static str = "QueryMarketMapRequest"; - const PACKAGE: &'static str = "astria_vendored.slinky.service.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.service.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) } } /// QueryMarketMapResponse defines the response type for the MarketMap method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryMarketMapResponse { - /// market_map defines the current market map configuration. + /// MarketMap defines the current market map configuration. #[prost(message, optional, tag = "1")] - pub market_map: ::core::option::Option, + pub market_map: ::core::option::Option, } impl ::prost::Name for QueryMarketMapResponse { const NAME: &'static str = "QueryMarketMapResponse"; - const PACKAGE: &'static str = "astria_vendored.slinky.service.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.service.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + } +} +/// QueryVersionRequest defines the request type for the Version method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVersionRequest {} +impl ::prost::Name for QueryVersionRequest { + const NAME: &'static str = "QueryVersionRequest"; + const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + } +} +/// QueryVersionResponse defines the response type for the Version method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVersionResponse { + /// Version defines the current version of the oracle service. + #[prost(string, tag = "1")] + pub version: ::prost::alloc::string::String, +} +impl ::prost::Name for QueryVersionResponse { + const NAME: &'static str = "QueryVersionResponse"; + const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) } } /// Generated client implementations. @@ -161,12 +191,15 @@ pub mod oracle_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.service.v1.Oracle/Prices", + "/astria_vendored.connect.service.v2.Oracle/Prices", ); let mut req = request.into_request(); req.extensions_mut() .insert( - GrpcMethod::new("astria_vendored.slinky.service.v1.Oracle", "Prices"), + GrpcMethod::new( + "astria_vendored.connect.service.v2.Oracle", + "Prices", + ), ); self.inner.unary(req, path, codec).await } @@ -190,18 +223,50 @@ pub mod oracle_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.slinky.service.v1.Oracle/MarketMap", + "/astria_vendored.connect.service.v2.Oracle/MarketMap", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "astria_vendored.slinky.service.v1.Oracle", + "astria_vendored.connect.service.v2.Oracle", "MarketMap", ), ); self.inner.unary(req, path, codec).await } + /// Version defines a method for fetching the current version of the oracle + /// service. + pub async fn version( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria_vendored.connect.service.v2.Oracle/Version", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria_vendored.connect.service.v2.Oracle", + "Version", + ), + ); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -229,6 +294,15 @@ pub mod oracle_server { tonic::Response, tonic::Status, >; + /// Version defines a method for fetching the current version of the oracle + /// service. + async fn version( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } /// Oracle defines the gRPC oracle service. #[derive(Debug)] @@ -310,7 +384,7 @@ pub mod oracle_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { - "/astria_vendored.slinky.service.v1.Oracle/Prices" => { + "/astria_vendored.connect.service.v2.Oracle/Prices" => { #[allow(non_camel_case_types)] struct PricesSvc(pub Arc); impl< @@ -356,7 +430,7 @@ pub mod oracle_server { }; Box::pin(fut) } - "/astria_vendored.slinky.service.v1.Oracle/MarketMap" => { + "/astria_vendored.connect.service.v2.Oracle/MarketMap" => { #[allow(non_camel_case_types)] struct MarketMapSvc(pub Arc); impl< @@ -402,6 +476,52 @@ pub mod oracle_server { }; Box::pin(fut) } + "/astria_vendored.connect.service.v2.Oracle/Version" => { + #[allow(non_camel_case_types)] + struct VersionSvc(pub Arc); + impl< + T: Oracle, + > tonic::server::UnaryService + for VersionSvc { + type Response = super::QueryVersionResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::version(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = VersionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { Ok( @@ -440,6 +560,6 @@ pub mod oracle_server { } } impl tonic::server::NamedService for OracleServer { - const NAME: &'static str = "astria_vendored.slinky.service.v1.Oracle"; + const NAME: &'static str = "astria_vendored.connect.service.v2.Oracle"; } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.connect.service.v2.serde.rs similarity index 60% rename from crates/astria-core/src/generated/astria_vendored.slinky.service.v1.serde.rs rename to crates/astria-core/src/generated/astria_vendored.connect.service.v2.serde.rs index 188b44bfa2..2760b1501e 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.service.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.service.v2.serde.rs @@ -6,7 +6,7 @@ impl serde::Serialize for QueryMarketMapRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.slinky.service.v1.QueryMarketMapRequest", len)?; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryMarketMapRequest", len)?; struct_ser.end() } } @@ -52,7 +52,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapRequest { type Value = QueryMarketMapRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.service.v1.QueryMarketMapRequest") + formatter.write_str("struct astria_vendored.connect.service.v2.QueryMarketMapRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -66,7 +66,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.service.v1.QueryMarketMapRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryMarketMapRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryMarketMapResponse { @@ -80,7 +80,7 @@ impl serde::Serialize for QueryMarketMapResponse { if self.market_map.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.service.v1.QueryMarketMapResponse", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryMarketMapResponse", len)?; if let Some(v) = self.market_map.as_ref() { struct_ser.serialize_field("marketMap", v)?; } @@ -135,7 +135,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapResponse { type Value = QueryMarketMapResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.service.v1.QueryMarketMapResponse") + formatter.write_str("struct astria_vendored.connect.service.v2.QueryMarketMapResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -158,7 +158,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapResponse { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.service.v1.QueryMarketMapResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryMarketMapResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryPricesRequest { @@ -169,7 +169,7 @@ impl serde::Serialize for QueryPricesRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.slinky.service.v1.QueryPricesRequest", len)?; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryPricesRequest", len)?; struct_ser.end() } } @@ -215,7 +215,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesRequest { type Value = QueryPricesRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.service.v1.QueryPricesRequest") + formatter.write_str("struct astria_vendored.connect.service.v2.QueryPricesRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -229,7 +229,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesRequest { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.service.v1.QueryPricesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryPricesRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryPricesResponse { @@ -246,13 +246,19 @@ impl serde::Serialize for QueryPricesResponse { if self.timestamp.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.service.v1.QueryPricesResponse", len)?; + if !self.version.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryPricesResponse", len)?; if !self.prices.is_empty() { struct_ser.serialize_field("prices", &self.prices)?; } if let Some(v) = self.timestamp.as_ref() { struct_ser.serialize_field("timestamp", v)?; } + if !self.version.is_empty() { + struct_ser.serialize_field("version", &self.version)?; + } struct_ser.end() } } @@ -265,12 +271,14 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { const FIELDS: &[&str] = &[ "prices", "timestamp", + "version", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Prices, Timestamp, + Version, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -294,6 +302,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { match value { "prices" => Ok(GeneratedField::Prices), "timestamp" => Ok(GeneratedField::Timestamp), + "version" => Ok(GeneratedField::Version), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -306,7 +315,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { type Value = QueryPricesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.service.v1.QueryPricesResponse") + formatter.write_str("struct astria_vendored.connect.service.v2.QueryPricesResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -315,6 +324,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { { let mut prices__ = None; let mut timestamp__ = None; + let mut version__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Prices => { @@ -331,14 +341,183 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { } timestamp__ = map_.next_value()?; } + GeneratedField::Version => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("version")); + } + version__ = Some(map_.next_value()?); + } } } Ok(QueryPricesResponse { prices: prices__.unwrap_or_default(), timestamp: timestamp__, + version: version__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryPricesResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QueryVersionRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryVersionRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryVersionRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + 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, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryVersionRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria_vendored.connect.service.v2.QueryVersionRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(QueryVersionRequest { + }) + } + } + deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryVersionRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QueryVersionResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.version.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryVersionResponse", len)?; + if !self.version.is_empty() { + struct_ser.serialize_field("version", &self.version)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryVersionResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "version", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Version, + } + 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 { + "version" => Ok(GeneratedField::Version), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryVersionResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria_vendored.connect.service.v2.QueryVersionResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Version => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("version")); + } + version__ = Some(map_.next_value()?); + } + } + } + Ok(QueryVersionResponse { + version: version__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("astria_vendored.slinky.service.v1.QueryPricesResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryVersionResponse", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.types.v1.rs b/crates/astria-core/src/generated/astria_vendored.connect.types.v2.rs similarity index 78% rename from crates/astria-core/src/generated/astria_vendored.slinky.types.v1.rs rename to crates/astria-core/src/generated/astria_vendored.connect.types.v2.rs index 57c3e72b86..ee4429a051 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.types.v1.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.types.v2.rs @@ -10,8 +10,8 @@ pub struct CurrencyPair { } impl ::prost::Name for CurrencyPair { const NAME: &'static str = "CurrencyPair"; - const PACKAGE: &'static str = "astria_vendored.slinky.types.v1"; + const PACKAGE: &'static str = "astria_vendored.connect.types.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.slinky.types.v1.{}", Self::NAME) + ::prost::alloc::format!("astria_vendored.connect.types.v2.{}", Self::NAME) } } diff --git a/crates/astria-core/src/generated/astria_vendored.slinky.types.v1.serde.rs b/crates/astria-core/src/generated/astria_vendored.connect.types.v2.serde.rs similarity index 93% rename from crates/astria-core/src/generated/astria_vendored.slinky.types.v1.serde.rs rename to crates/astria-core/src/generated/astria_vendored.connect.types.v2.serde.rs index 8a79280911..2ea87a7889 100644 --- a/crates/astria-core/src/generated/astria_vendored.slinky.types.v1.serde.rs +++ b/crates/astria-core/src/generated/astria_vendored.connect.types.v2.serde.rs @@ -12,7 +12,7 @@ impl serde::Serialize for CurrencyPair { if !self.quote.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.slinky.types.v1.CurrencyPair", len)?; + let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.types.v2.CurrencyPair", len)?; if !self.base.is_empty() { struct_ser.serialize_field("Base", &self.base)?; } @@ -72,7 +72,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPair { type Value = CurrencyPair; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.slinky.types.v1.CurrencyPair") + formatter.write_str("struct astria_vendored.connect.types.v2.CurrencyPair") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -103,6 +103,6 @@ impl<'de> serde::Deserialize<'de> for CurrencyPair { }) } } - deserializer.deserialize_struct("astria_vendored.slinky.types.v1.CurrencyPair", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria_vendored.connect.types.v2.CurrencyPair", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index 8812b9695d..7adccab543 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -39,63 +39,63 @@ pub mod astria_vendored { } #[path = ""] - pub mod slinky { + pub mod connect { pub mod abci { - pub mod v1 { - include!("astria_vendored.slinky.abci.v1.rs"); + pub mod v2 { + include!("astria_vendored.connect.abci.v2.rs"); #[cfg(feature = "serde")] mod _serde_impl { use super::*; - include!("astria_vendored.slinky.abci.v1.serde.rs"); + include!("astria_vendored.connect.abci.v2.serde.rs"); } } } pub mod marketmap { - pub mod v1 { - include!("astria_vendored.slinky.marketmap.v1.rs"); + pub mod v2 { + include!("astria_vendored.connect.marketmap.v2.rs"); #[cfg(feature = "serde")] mod _serde_impl { use super::*; - include!("astria_vendored.slinky.marketmap.v1.serde.rs"); + include!("astria_vendored.connect.marketmap.v2.serde.rs"); } } } pub mod oracle { - pub mod v1 { - include!("astria_vendored.slinky.oracle.v1.rs"); + pub mod v2 { + include!("astria_vendored.connect.oracle.v2.rs"); #[cfg(feature = "serde")] mod _serde_impl { use super::*; - include!("astria_vendored.slinky.oracle.v1.serde.rs"); + include!("astria_vendored.connect.oracle.v2.serde.rs"); } } } pub mod service { - pub mod v1 { - include!("astria_vendored.slinky.service.v1.rs"); + pub mod v2 { + include!("astria_vendored.connect.service.v2.rs"); #[cfg(feature = "serde")] mod _serde_impl { use super::*; - include!("astria_vendored.slinky.service.v1.serde.rs"); + include!("astria_vendored.connect.service.v2.serde.rs"); } } } pub mod types { - pub mod v1 { - include!("astria_vendored.slinky.types.v1.rs"); + pub mod v2 { + include!("astria_vendored.connect.types.v2.rs"); #[cfg(feature = "serde")] mod _serde_impl { use super::*; - include!("astria_vendored.slinky.types.v1.serde.rs"); + include!("astria_vendored.connect.types.v2.serde.rs"); } } } diff --git a/crates/astria-core/src/lib.rs b/crates/astria-core/src/lib.rs index c9263e9d9d..51e2f8477b 100644 --- a/crates/astria-core/src/lib.rs +++ b/crates/astria-core/src/lib.rs @@ -14,13 +14,13 @@ compile_error!( )] pub mod generated; +pub mod connect; pub mod crypto; pub mod display; pub mod execution; pub mod primitive; pub mod protocol; pub mod sequencerblock; -pub mod slinky; #[cfg(feature = "brotli")] pub mod brotli; diff --git a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state_is_unchanged.snap b/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state_is_unchanged.snap index cb9e4ae7d3..c782292789 100644 --- a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state_is_unchanged.snap +++ b/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state_is_unchanged.snap @@ -133,7 +133,7 @@ expression: genesis_state() "multiplier": {} } }, - "slinky": { + "connect": { "marketMap": { "marketMap": { "markets": { diff --git a/crates/astria-core/src/protocol/genesis/v1.rs b/crates/astria-core/src/protocol/genesis/v1.rs index 7e737e290b..6e4ae301f2 100644 --- a/crates/astria-core/src/protocol/genesis/v1.rs +++ b/crates/astria-core/src/protocol/genesis/v1.rs @@ -3,6 +3,10 @@ use std::convert::Infallible; pub use penumbra_ibc::params::IBCParameters; use crate::{ + connect::{ + market_map, + oracle, + }, generated::protocol::genesis::v1 as raw, primitive::v1::{ asset::{ @@ -33,10 +37,6 @@ use crate::{ TransferFeeComponents, ValidatorUpdateFeeComponents, }, - slinky::{ - market_map, - oracle, - }, Protobuf, }; @@ -44,28 +44,28 @@ use crate::{ #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), - serde(try_from = "raw::SlinkyGenesis", into = "raw::SlinkyGenesis") + serde(try_from = "raw::ConnectGenesis", into = "raw::ConnectGenesis") )] -pub struct SlinkyGenesis { - market_map: market_map::v1::GenesisState, - oracle: oracle::v1::GenesisState, +pub struct ConnectGenesis { + market_map: market_map::v2::GenesisState, + oracle: oracle::v2::GenesisState, } -impl SlinkyGenesis { +impl ConnectGenesis { #[must_use] - pub fn market_map(&self) -> &market_map::v1::GenesisState { + pub fn market_map(&self) -> &market_map::v2::GenesisState { &self.market_map } #[must_use] - pub fn oracle(&self) -> &oracle::v1::GenesisState { + pub fn oracle(&self) -> &oracle::v2::GenesisState { &self.oracle } } -impl Protobuf for SlinkyGenesis { - type Error = SlinkyGenesisError; - type Raw = raw::SlinkyGenesis; +impl Protobuf for ConnectGenesis { + type Error = ConnectGenesisError; + type Raw = raw::ConnectGenesis; fn try_from_raw_ref(raw: &Self::Raw) -> Result { let Self::Raw { @@ -76,14 +76,14 @@ impl Protobuf for SlinkyGenesis { .as_ref() .ok_or_else(|| Self::Error::field_not_set("market_map")) .and_then(|market_map| { - market_map::v1::GenesisState::try_from_raw_ref(market_map) + market_map::v2::GenesisState::try_from_raw_ref(market_map) .map_err(Self::Error::market_map) })?; let oracle = oracle .as_ref() .ok_or_else(|| Self::Error::field_not_set("oracle")) .and_then(|oracle| { - oracle::v1::GenesisState::try_from_raw_ref(oracle).map_err(Self::Error::oracle) + oracle::v2::GenesisState::try_from_raw_ref(oracle).map_err(Self::Error::oracle) })?; Ok(Self { market_map, @@ -103,56 +103,56 @@ impl Protobuf for SlinkyGenesis { } } -impl TryFrom for SlinkyGenesis { +impl TryFrom for ConnectGenesis { type Error = ::Error; - fn try_from(value: raw::SlinkyGenesis) -> Result { + fn try_from(value: raw::ConnectGenesis) -> Result { Self::try_from_raw(value) } } -impl From for raw::SlinkyGenesis { - fn from(value: SlinkyGenesis) -> Self { +impl From for raw::ConnectGenesis { + fn from(value: ConnectGenesis) -> Self { value.into_raw() } } #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct SlinkyGenesisError(SlinkyGenesisErrorKind); +pub struct ConnectGenesisError(ConnectGenesisErrorKind); -impl SlinkyGenesisError { +impl ConnectGenesisError { fn field_not_set(name: &'static str) -> Self { - Self(SlinkyGenesisErrorKind::FieldNotSet { + Self(ConnectGenesisErrorKind::FieldNotSet { name, }) } - fn market_map(source: market_map::v1::GenesisStateError) -> Self { - Self(SlinkyGenesisErrorKind::MarketMap { + fn market_map(source: market_map::v2::GenesisStateError) -> Self { + Self(ConnectGenesisErrorKind::MarketMap { source, }) } - fn oracle(source: oracle::v1::GenesisStateError) -> Self { - Self(SlinkyGenesisErrorKind::Oracle { + fn oracle(source: oracle::v2::GenesisStateError) -> Self { + Self(ConnectGenesisErrorKind::Oracle { source, }) } } #[derive(Debug, thiserror::Error)] -#[error("failed ensuring invariants of {}", SlinkyGenesis::full_name())] -enum SlinkyGenesisErrorKind { +#[error("failed ensuring invariants of {}", ConnectGenesis::full_name())] +enum ConnectGenesisErrorKind { #[error("field was not set: `{name}`")] FieldNotSet { name: &'static str }, #[error("`market_map` field was invalid")] MarketMap { - source: market_map::v1::GenesisStateError, + source: market_map::v2::GenesisStateError, }, #[error("`oracle` field was invalid")] Oracle { - source: oracle::v1::GenesisStateError, + source: oracle::v2::GenesisStateError, }, } @@ -177,7 +177,7 @@ pub struct GenesisAppState { ibc_parameters: IBCParameters, allowed_fee_assets: Vec, fees: GenesisFees, - slinky: SlinkyGenesis, + connect: ConnectGenesis, } impl GenesisAppState { @@ -232,8 +232,8 @@ impl GenesisAppState { } #[must_use] - pub fn slinky(&self) -> &SlinkyGenesis { - &self.slinky + pub fn connect(&self) -> &ConnectGenesis { + &self.connect } fn ensure_address_has_base_prefix( @@ -268,7 +268,7 @@ impl GenesisAppState { } for (i, address) in self - .slinky + .connect .market_map .params .market_authorities @@ -281,7 +281,7 @@ impl GenesisAppState { )?; } self.ensure_address_has_base_prefix( - &self.slinky.market_map.params.admin, + &self.connect.market_map.params.admin, ".market_map.params.admin", )?; @@ -312,7 +312,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - slinky, + connect, } = raw; let address_prefixes = address_prefixes .as_ref() @@ -372,25 +372,25 @@ impl Protobuf for GenesisAppState { .ok_or_else(|| Self::Error::field_not_set("fees")) .and_then(|fees| GenesisFees::try_from_raw_ref(fees).map_err(Self::Error::fees))?; - let slinky = slinky + let connect = connect .as_ref() - .ok_or_else(|| Self::Error::field_not_set("slinky"))?; + .ok_or_else(|| Self::Error::field_not_set("connect"))?; - let market_map = slinky + let market_map = connect .market_map .as_ref() .ok_or_else(|| Self::Error::field_not_set("market_map")) .and_then(|market_map| { - market_map::v1::GenesisState::try_from_raw(market_map.clone()) + market_map::v2::GenesisState::try_from_raw(market_map.clone()) .map_err(Self::Error::market_map) })?; - let oracle = slinky + let oracle = connect .oracle .as_ref() .ok_or_else(|| Self::Error::field_not_set("oracle")) .and_then(|oracle| { - oracle::v1::GenesisState::try_from_raw(oracle.clone()).map_err(Self::Error::oracle) + oracle::v2::GenesisState::try_from_raw(oracle.clone()).map_err(Self::Error::oracle) })?; let this = Self { @@ -404,7 +404,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - slinky: SlinkyGenesis { + connect: ConnectGenesis { market_map, oracle, }, @@ -426,7 +426,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - slinky, + connect, } = self; Self::Raw { address_prefixes: Some(address_prefixes.to_raw()), @@ -441,7 +441,7 @@ impl Protobuf for GenesisAppState { ibc_parameters: Some(ibc_parameters.to_raw()), allowed_fee_assets: allowed_fee_assets.iter().map(ToString::to_string).collect(), fees: Some(fees.to_raw()), - slinky: Some(slinky.to_raw()), + connect: Some(connect.to_raw()), } } } @@ -525,13 +525,13 @@ impl GenesisAppStateError { }) } - fn market_map(source: market_map::v1::GenesisStateError) -> Self { + fn market_map(source: market_map::v2::GenesisStateError) -> Self { Self(GenesisAppStateErrorKind::MarketMap { source, }) } - fn oracle(source: oracle::v1::GenesisStateError) -> Self { + fn oracle(source: oracle::v2::GenesisStateError) -> Self { Self(GenesisAppStateErrorKind::Oracle { source, }) @@ -565,11 +565,11 @@ enum GenesisAppStateErrorKind { NativeAssetBaseDenomination { source: ParseTracePrefixedError }, #[error("`market_map` field was invalid")] MarketMap { - source: market_map::v1::GenesisStateError, + source: market_map::v2::GenesisStateError, }, #[error("`oracle` field was invalid")] Oracle { - source: oracle::v1::GenesisStateError, + source: oracle::v2::GenesisStateError, }, } @@ -987,14 +987,14 @@ mod tests { use super::*; use crate::{ - primitive::v1::Address, - slinky::{ - market_map::v1::{ + connect::{ + market_map::v2::{ MarketMap, Params, }, - types::v1::CurrencyPairId, + types::v2::CurrencyPairId, }, + primitive::v1::Address, }; const ASTRIA_ADDRESS_PREFIX: &str = "astria"; @@ -1032,14 +1032,14 @@ mod tests { } fn genesis_state_markets() -> MarketMap { - use crate::slinky::{ - market_map::v1::{ + use crate::connect::{ + market_map::v2::{ Market, MarketMap, ProviderConfig, Ticker, }, - types::v1::CurrencyPair, + types::v2::CurrencyPair, }; let markets = indexmap! { @@ -1074,12 +1074,12 @@ mod tests { #[expect(clippy::too_many_lines, reason = "for testing purposes")] fn proto_genesis_state() -> raw::GenesisAppState { - use crate::slinky::{ - oracle::v1::{ + use crate::connect::{ + oracle::v2::{ CurrencyPairGenesis, QuotePrice, }, - types::v1::{ + types::v2::{ CurrencyPair, CurrencyPairNonce, Price, @@ -1216,9 +1216,9 @@ mod tests { .to_raw(), ), }), - slinky: Some( - SlinkyGenesis { - market_map: market_map::v1::GenesisState { + connect: Some( + ConnectGenesis { + market_map: market_map::v2::GenesisState { market_map: genesis_state_markets(), last_updated: 0, params: Params { @@ -1226,7 +1226,7 @@ mod tests { admin: alice(), }, }, - oracle: oracle::v1::GenesisState { + oracle: oracle::v2::GenesisState { currency_pair_genesis: vec![CurrencyPairGenesis { id: CurrencyPairId::new(1), nonce: CurrencyPairNonce::new(0), @@ -1306,9 +1306,9 @@ mod tests { ); assert_bad_prefix( raw::GenesisAppState { - slinky: { - let mut slinky = proto_genesis_state().slinky; - slinky + connect: { + let mut connect = proto_genesis_state().connect; + connect .as_mut() .unwrap() .market_map @@ -1318,7 +1318,7 @@ mod tests { .as_mut() .unwrap() .market_authorities[0] = mallory().to_string(); - slinky + connect }, ..proto_genesis_state() }, diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index a7a6bc40c8..3bd402f93e 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -6,18 +6,18 @@ use std::{ use astria_core::{ generated::{ - astria_vendored::slinky::{ + astria_vendored::connect::{ marketmap, - marketmap::v1::{ + marketmap::v2::{ Market, MarketMap, }, oracle, - oracle::v1::{ + oracle::v2::{ CurrencyPairGenesis, QuotePrice, }, - types::v1::CurrencyPair, + types::v2::CurrencyPair, }, protocol::genesis::v1::{ AddressPrefixes, @@ -82,7 +82,7 @@ fn charlie() -> Address { } fn genesis_state_markets() -> MarketMap { - use astria_core::generated::astria_vendored::slinky::marketmap::v1::{ + use astria_core::generated::astria_vendored::connect::marketmap::v2::{ ProviderConfig, Ticker, }; @@ -181,19 +181,19 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::Genes outbound_ics20_transfers_enabled: true, }), allowed_fee_assets: vec!["nria".parse().unwrap()], - slinky: Some( - astria_core::generated::protocol::genesis::v1::SlinkyGenesis { + connect: Some( + astria_core::generated::protocol::genesis::v1::ConnectGenesis { market_map: Some( - astria_core::generated::astria_vendored::slinky::marketmap::v1::GenesisState { + astria_core::generated::astria_vendored::connect::marketmap::v2::GenesisState { market_map: Some(genesis_state_markets()), last_updated: 0, - params: Some(marketmap::v1::Params { + params: Some(marketmap::v2::Params { market_authorities: vec![alice().to_string(), bob().to_string()], admin: alice().to_string(), }), }, ), - oracle: Some(oracle::v1::GenesisState { + oracle: Some(oracle::v2::GenesisState { currency_pair_genesis: vec![ CurrencyPairGenesis { id: 0, diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index 1831149c4c..b219d1e3f2 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -33,12 +33,12 @@ ASTRIA_SEQUENCER_METRICS_HTTP_LISTENER_ADDR="127.0.0.1:9000" # `ASTRIA_SEQUENCER_FORCE_STDOUT` is set to `true`. ASTRIA_SEQUENCER_PRETTY_PRINT=false -# If the oracle is disabled. If false, the slinky_grpc_addr must be set. +# If the oracle is disabled. If false, the connect_grpc_addr must be set. # Should be false for validator nodes and true for non-validator nodes. ASTRIA_SEQUENCER_NO_ORACLE=true # The gRPC endpoint for the oracle sidecar. -ASTRIA_SEQUENCER_SLINKY_GRPC_ADDR="http://127.0.0.1:8081" +ASTRIA_SEQUENCER_CONNECT_GRPC_ADDR="http://127.0.0.1:8081" # The timeout for the responses from the oracle sidecar in milliseconds. ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS=1000 diff --git a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs index ee0b4c1e8f..f86903f052 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -1,7 +1,11 @@ use std::collections::HashMap; use astria_core::{ - generated::protocol::genesis::v1::SlinkyGenesis, + connect::market_map::v2::{ + MarketMap, + Params, + }, + generated::protocol::genesis::v1::ConnectGenesis, primitive::v1::asset::{ Denom, IbcPrefixed, @@ -30,10 +34,6 @@ use astria_core::{ }, transaction::v1::action::ValidatorUpdate, }, - slinky::market_map::v1::{ - MarketMap, - Params, - }, Protobuf, }; use astria_eyre::eyre::WrapErr as _; @@ -158,9 +158,9 @@ pub(crate) fn proto_genesis_state() -> astria_core::generated::protocol::genesis }), allowed_fee_assets: vec![nria().to_string()], fees: Some(default_fees().to_raw()), - slinky: Some(SlinkyGenesis { + connect: Some(ConnectGenesis { market_map: Some( - astria_core::slinky::market_map::v1::GenesisState { + astria_core::connect::market_map::v2::GenesisState { market_map: MarketMap { markets: indexmap::IndexMap::new(), }, @@ -173,7 +173,7 @@ pub(crate) fn proto_genesis_state() -> astria_core::generated::protocol::genesis .into_raw(), ), oracle: Some( - astria_core::generated::astria_vendored::slinky::oracle::v1::GenesisState { + astria_core::generated::astria_vendored::connect::oracle::v2::GenesisState { currency_pair_genesis: vec![], next_id: 0, }, diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 25065b4c7b..6671cd8cfc 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -115,6 +115,10 @@ use crate::{ StateWriteExt as _, }, component::Component as _, + connect::{ + marketmap::component::MarketMapComponent, + oracle::component::OracleComponent, + }, fees::{ component::FeesComponent, StateReadExt as _, @@ -133,10 +137,6 @@ use crate::{ GeneratedCommitments, }, }, - slinky::{ - marketmap::component::MarketMapComponent, - oracle::component::OracleComponent, - }, transaction::InvalidNonce, }; diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap index baf0abc44c..9cdee0c76c 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_execute_transaction_with_every_action_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 58, - 142, - 54, - 69, + 7, + 73, 159, - 45, - 152, - 139, - 201, - 168, - 143, - 64, - 3, + 33, + 203, + 171, + 43, + 107, 191, - 165, + 198, + 89, + 66, + 213, + 54, + 135, + 108, + 97, + 126, + 73, + 192, + 102, + 156, + 128, 81, - 142, - 181, - 159, - 62, - 2, - 222, - 70, - 24, - 193, - 20, - 70, - 177, - 51, - 250, - 253, - 168 + 249, + 111, + 203, + 53, + 36, + 53, + 188, + 61 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap index 3b16576e90..685595551e 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_finalize_block_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 15, - 251, - 175, - 211, - 230, - 230, - 232, - 169, - 60, - 103, - 140, - 177, - 67, - 214, - 197, - 15, - 121, + 75, + 218, 136, - 56, - 181, + 254, + 179, 193, - 43, - 135, - 52, - 49, - 143, - 245, - 11, - 36, + 82, + 27, + 253, + 94, + 227, + 174, + 229, + 51, + 196, + 185, + 210, + 71, + 247, + 132, + 229, + 90, 138, - 147, - 33 + 4, + 228, + 145, + 229, + 136, + 191, + 219, + 150, + 190 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap index 52931c1c76..a90e229cdf 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_genesis_snapshot.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 42, - 90, - 219, - 79, - 134, - 238, - 135, - 74, - 68, - 160, - 196, - 72, - 17, - 64, - 2, - 87, + 231, + 14, + 30, + 46, + 75, + 144, + 96, + 107, 201, - 220, - 122, - 244, - 61, - 40, - 11, - 226, + 50, + 247, + 214, + 18, + 146, + 108, + 66, + 67, + 117, 152, - 106, - 49, - 5, - 88, - 177, - 143, - 28 + 244, + 68, + 26, + 169, + 236, + 241, + 120, + 252, + 87, + 16, + 209, + 145, + 236 ] diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index a77fc59ce5..d21e3b1765 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -1,23 +1,23 @@ use std::collections::HashMap; use astria_core::{ + connect::{ + abci::v2::OracleVoteExtension, + oracle::v2::QuotePrice, + service::v2::QueryPricesResponse, + types::v2::{ + CurrencyPair, + Price, + }, + }, crypto::Signature, - generated::astria_vendored::slinky::{ - abci::v1::OracleVoteExtension as RawOracleVoteExtension, - service::v1::{ + generated::astria_vendored::connect::{ + abci::v2::OracleVoteExtension as RawOracleVoteExtension, + service::v2::{ oracle_client::OracleClient, QueryPricesRequest, }, }, - slinky::{ - abci::v1::OracleVoteExtension, - oracle::v1::QuotePrice, - service::v1::QueryPricesResponse, - types::v1::{ - CurrencyPair, - Price, - }, - }, }; use astria_eyre::eyre::{ bail, @@ -49,17 +49,17 @@ use crate::{ address::StateReadExt as _, app::state_ext::StateReadExt, authority::StateReadExt as _, - slinky::oracle::{ + connect::oracle::{ currency_pair_strategy::DefaultCurrencyPairStrategy, state_ext::StateWriteExt, }, }; -// https://github.com/skip-mev/slinky/blob/793b2e874d6e720bd288e82e782502e41cf06f8c/abci/types/constants.go#L6 +// https://github.com/skip-mev/connect/blob/793b2e874d6e720bd288e82e782502e41cf06f8c/abci/types/constants.go#L6 const MAXIMUM_PRICE_BYTE_LEN: usize = 33; pub(crate) struct Handler { - // gRPC client for the slinky oracle sidecar. + // gRPC client for the connect oracle sidecar. oracle_client: Option>, } @@ -92,7 +92,7 @@ impl Handler { }; let query_prices_response = - astria_core::slinky::service::v1::QueryPricesResponse::try_from_raw(rsp) + astria_core::connect::service::v2::QueryPricesResponse::try_from_raw(rsp) .wrap_err("failed to validate prices server response")?; let oracle_vote_extension = transform_oracle_service_prices(state, query_prices_response) .await @@ -128,7 +128,7 @@ impl Handler { } } -// see https://github.com/skip-mev/slinky/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L24 +// see https://github.com/skip-mev/connect/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L24 fn verify_vote_extension( oracle_vote_extension_bytes: bytes::Bytes, max_num_currency_pairs: u64, @@ -151,13 +151,13 @@ fn verify_vote_extension( Ok(()) } -// see https://github.com/skip-mev/slinky/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 +// see https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/abci/ve/vote_extension.go#L290 #[instrument(skip_all)] async fn transform_oracle_service_prices( state: &S, rsp: QueryPricesResponse, ) -> Result { - use astria_core::slinky::types::v1::CurrencyPairId; + use astria_core::connect::types::v2::CurrencyPairId; use futures::StreamExt as _; let futures = futures::stream::FuturesUnordered::new(); @@ -293,7 +293,7 @@ impl ProposalHandler { } } -// see https://github.com/skip-mev/slinky/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L111 +// see https://github.com/skip-mev/connect/blob/5b07f91d6c0110e617efda3f298f147a31da0f25/abci/ve/utils.go#L111 async fn validate_vote_extensions( state: &S, height: u64, @@ -510,7 +510,7 @@ async fn aggregate_oracle_votes( // validators are not weighted right now, so we just take the median price for each currency // pair // - // skip uses a stake-weighted median: https://github.com/skip-mev/slinky/blob/19a916122110cfd0e98d93978107d7ada1586918/pkg/math/voteweighted/voteweighted.go#L59 + // skip uses a stake-weighted median: https://github.com/skip-mev/connect/blob/19a916122110cfd0e98d93978107d7ada1586918/pkg/math/voteweighted/voteweighted.go#L59 // we can implement this later, when we have stake weighting. let mut currency_pair_to_price_list = HashMap::new(); for vote in votes { diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index 3aa768681b..5c8b48efe3 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -30,11 +30,11 @@ pub struct Config { pub metrics_http_listener_addr: String, /// Writes a human readable format to stdout instead of JSON formatted OTEL trace data. pub pretty_print: bool, - /// If the oracle is disabled. If false, the `slinky_grpc_addr` must be set. + /// If the oracle is disabled. If false, the `connect_grpc_addr` must be set. /// Should be false for validator nodes and true for non-validator nodes. pub no_oracle: bool, /// The gRPC endpoint for the oracle sidecar. - pub slinky_grpc_addr: String, + pub connect_grpc_addr: String, /// The timeout for the responses from the oracle sidecar in milliseconds. pub oracle_client_timeout_milliseconds: u64, /// The maximum number of transactions that can be parked in the mempool. diff --git a/crates/astria-sequencer/src/slinky/marketmap/component.rs b/crates/astria-sequencer/src/connect/marketmap/component.rs similarity index 90% rename from crates/astria-sequencer/src/slinky/marketmap/component.rs rename to crates/astria-sequencer/src/connect/marketmap/component.rs index 503c593494..a164bdd523 100644 --- a/crates/astria-sequencer/src/slinky/marketmap/component.rs +++ b/crates/astria-sequencer/src/connect/marketmap/component.rs @@ -28,10 +28,10 @@ impl Component for MarketMapComponent { // only required for related actions however state - .put_market_map(app_state.slinky().market_map().market_map.clone()) + .put_market_map(app_state.connect().market_map().market_map.clone()) .wrap_err("failed to put market map")?; state - .put_params(app_state.slinky().market_map().params.clone()) + .put_params(app_state.connect().market_map().params.clone()) .wrap_err("failed to put params")?; Ok(()) } diff --git a/crates/astria-sequencer/src/slinky/marketmap/mod.rs b/crates/astria-sequencer/src/connect/marketmap/mod.rs similarity index 100% rename from crates/astria-sequencer/src/slinky/marketmap/mod.rs rename to crates/astria-sequencer/src/connect/marketmap/mod.rs diff --git a/crates/astria-sequencer/src/slinky/marketmap/state_ext.rs b/crates/astria-sequencer/src/connect/marketmap/state_ext.rs similarity index 93% rename from crates/astria-sequencer/src/slinky/marketmap/state_ext.rs rename to crates/astria-sequencer/src/connect/marketmap/state_ext.rs index 249d9ae3e1..5c09935068 100644 --- a/crates/astria-sequencer/src/slinky/marketmap/state_ext.rs +++ b/crates/astria-sequencer/src/connect/marketmap/state_ext.rs @@ -1,4 +1,4 @@ -use astria_core::slinky::market_map::v1::{ +use astria_core::connect::market_map::v2::{ MarketMap, Params, }; @@ -20,9 +20,9 @@ use cnidarium::{ }; use tracing::instrument; -const MARKET_MAP_KEY: &str = "slinkymarketmap"; -const PARAMS_KEY: &str = "slinkyparams"; -const MARKET_MAP_LAST_UPDATED_KEY: &str = "slinkymarketmaplastupdated"; +const MARKET_MAP_KEY: &str = "connectmarketmap"; +const PARAMS_KEY: &str = "connectparams"; +const MARKET_MAP_LAST_UPDATED_KEY: &str = "connectmarketmaplastupdated"; /// Newtype wrapper to read and write a u64 from rocksdb. #[derive(BorshSerialize, BorshDeserialize, Debug)] diff --git a/crates/astria-sequencer/src/slinky/mod.rs b/crates/astria-sequencer/src/connect/mod.rs similarity index 100% rename from crates/astria-sequencer/src/slinky/mod.rs rename to crates/astria-sequencer/src/connect/mod.rs diff --git a/crates/astria-sequencer/src/slinky/oracle/component.rs b/crates/astria-sequencer/src/connect/oracle/component.rs similarity index 85% rename from crates/astria-sequencer/src/slinky/oracle/component.rs rename to crates/astria-sequencer/src/connect/oracle/component.rs index 01fa6a66ea..e5db437d63 100644 --- a/crates/astria-sequencer/src/slinky/oracle/component.rs +++ b/crates/astria-sequencer/src/connect/oracle/component.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use astria_core::{ + connect::oracle::v2::CurrencyPairState, protocol::genesis::v1::GenesisAppState, - slinky::oracle::v1::CurrencyPairState, }; use astria_eyre::eyre::{ Result, @@ -26,7 +26,7 @@ impl Component for OracleComponent { #[instrument(name = "OracleComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { - for currency_pair in &app_state.slinky().oracle().currency_pair_genesis { + for currency_pair in &app_state.connect().oracle().currency_pair_genesis { let currency_pair_state = CurrencyPairState { id: currency_pair.id(), nonce: currency_pair.nonce(), @@ -38,10 +38,10 @@ impl Component for OracleComponent { } state - .put_next_currency_pair_id(app_state.slinky().oracle().next_id) + .put_next_currency_pair_id(app_state.connect().oracle().next_id) .wrap_err("failed to put next currency pair id")?; state - .put_num_currency_pairs(app_state.slinky().oracle().currency_pair_genesis.len() as u64) + .put_num_currency_pairs(app_state.connect().oracle().currency_pair_genesis.len() as u64) .wrap_err("failed to put number of currency pairs")?; state .put_num_removed_currency_pairs(0) diff --git a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/connect/oracle/currency_pair_strategy.rs similarity index 84% rename from crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs rename to crates/astria-sequencer/src/connect/oracle/currency_pair_strategy.rs index a3311e027a..3e21daddc8 100644 --- a/crates/astria-sequencer/src/slinky/oracle/currency_pair_strategy.rs +++ b/crates/astria-sequencer/src/connect/oracle/currency_pair_strategy.rs @@ -1,4 +1,4 @@ -use astria_core::slinky::types::v1::{ +use astria_core::connect::types::v2::{ CurrencyPair, CurrencyPairId, }; @@ -7,9 +7,9 @@ use astria_eyre::eyre::{ WrapErr as _, }; -use crate::slinky::oracle::state_ext::StateReadExt; +use crate::connect::oracle::state_ext::StateReadExt; -/// see +/// see pub(crate) struct DefaultCurrencyPairStrategy; impl DefaultCurrencyPairStrategy { diff --git a/crates/astria-sequencer/src/slinky/oracle/mod.rs b/crates/astria-sequencer/src/connect/oracle/mod.rs similarity index 100% rename from crates/astria-sequencer/src/slinky/oracle/mod.rs rename to crates/astria-sequencer/src/connect/oracle/mod.rs diff --git a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs b/crates/astria-sequencer/src/connect/oracle/state_ext.rs similarity index 98% rename from crates/astria-sequencer/src/slinky/oracle/state_ext.rs rename to crates/astria-sequencer/src/connect/oracle/state_ext.rs index 5b3c929da8..21d98fe827 100644 --- a/crates/astria-sequencer/src/slinky/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/connect/oracle/state_ext.rs @@ -7,12 +7,12 @@ use std::{ }, }; -use astria_core::slinky::{ - oracle::v1::{ +use astria_core::connect::{ + oracle::v2::{ CurrencyPairState, QuotePrice, }, - types::v1::{ + types::v2::{ CurrencyPair, CurrencyPairId, CurrencyPairNonce, @@ -145,13 +145,13 @@ mod in_state { #[derive(Debug, BorshSerialize, BorshDeserialize)] struct Price(u128); - impl From for Price { - fn from(value: astria_core::slinky::types::v1::Price) -> Self { + impl From for Price { + fn from(value: astria_core::connect::types::v2::Price) -> Self { Self(value.get()) } } - impl From for astria_core::slinky::types::v1::Price { + impl From for astria_core::connect::types::v2::Price { fn from(value: Price) -> Self { Self::new(value.0) } diff --git a/crates/astria-sequencer/src/grpc/slinky.rs b/crates/astria-sequencer/src/grpc/connect.rs similarity index 93% rename from crates/astria-sequencer/src/grpc/slinky.rs rename to crates/astria-sequencer/src/grpc/connect.rs index 8931ff91b6..2658e6275d 100644 --- a/crates/astria-sequencer/src/grpc/slinky.rs +++ b/crates/astria-sequencer/src/grpc/connect.rs @@ -4,8 +4,9 @@ use std::{ }; use astria_core::{ - generated::astria_vendored::slinky::{ - marketmap::v1::{ + connect::types::v2::CurrencyPair, + generated::astria_vendored::connect::{ + marketmap::v2::{ query_server::Query as MarketMapQueryService, LastUpdatedRequest, LastUpdatedResponse, @@ -16,7 +17,7 @@ use astria_core::{ ParamsRequest, ParamsResponse, }, - oracle::v1::{ + oracle::v2::{ query_server::Query as OracleService, GetAllCurrencyPairsRequest, GetAllCurrencyPairsResponse, @@ -28,7 +29,6 @@ use astria_core::{ GetPricesResponse, }, }, - slinky::types::v1::CurrencyPair, }; use cnidarium::Storage; use futures::{ @@ -44,7 +44,7 @@ use tracing::instrument; use crate::{ app::StateReadExt as _, - slinky::{ + connect::{ marketmap::state_ext::StateReadExt as _, oracle::state_ext::{ CurrencyPairWithId, @@ -92,7 +92,7 @@ impl MarketMapQueryService for SequencerServer { .map_err(|e| Status::internal(format!("failed to get chain id from storage: {e:#}")))?; Ok(Response::new(MarketMapResponse { - market_map: market_map.map(astria_core::slinky::market_map::v1::MarketMap::into_raw), + market_map: market_map.map(astria_core::connect::market_map::v2::MarketMap::into_raw), last_updated, chain_id: chain_id.to_string(), })) @@ -137,7 +137,7 @@ impl MarketMapQueryService for SequencerServer { })?; Ok(Response::new(ParamsResponse { - params: params.map(astria_core::slinky::market_map::v1::Params::into_raw), + params: params.map(astria_core::connect::market_map::v2::Params::into_raw), })) } } @@ -171,14 +171,10 @@ impl OracleService for SequencerServer { request: Request, ) -> Result, Status> { let request = request.into_inner(); - let Some(currency_pair) = request.currency_pair else { - return Err(Status::invalid_argument("currency pair is required")); + let Ok(currency_pair) = request.currency_pair.parse() else { + return Err(Status::invalid_argument("currency pair is invalid")); }; - let currency_pair = CurrencyPair::try_from_raw(currency_pair).map_err(|e| { - Status::invalid_argument(format!( - "failed to validate currency pair provided in request: {e:#}" - )) - })?; + let snapshot = self.storage.latest_snapshot(); let Some(state) = snapshot .get_currency_pair_state(¤cy_pair) diff --git a/crates/astria-sequencer/src/grpc/mod.rs b/crates/astria-sequencer/src/grpc/mod.rs index d652a0259b..023e7386b5 100644 --- a/crates/astria-sequencer/src/grpc/mod.rs +++ b/crates/astria-sequencer/src/grpc/mod.rs @@ -1,5 +1,5 @@ +pub(crate) mod connect; pub(crate) mod sequencer; -pub(crate) mod slinky; mod state_ext; pub(crate) mod storage; diff --git a/crates/astria-sequencer/src/lib.rs b/crates/astria-sequencer/src/lib.rs index 53cf7d9d4b..b105316761 100644 --- a/crates/astria-sequencer/src/lib.rs +++ b/crates/astria-sequencer/src/lib.rs @@ -11,6 +11,7 @@ pub(crate) mod bridge; mod build_info; pub(crate) mod component; pub mod config; +pub(crate) mod connect; pub(crate) mod fees; pub(crate) mod grpc; pub(crate) mod ibc; @@ -20,7 +21,6 @@ pub(crate) mod proposal; pub(crate) mod rollup_data; mod sequencer; pub(crate) mod service; -pub(crate) mod slinky; pub(crate) mod storage; #[cfg(test)] pub(crate) mod test_utils; diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index 40601eff31..f8bfd18f83 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -1,8 +1,8 @@ use astria_core::generated::{ - astria_vendored::slinky::{ - marketmap::v1::query_server::QueryServer as MarketMapQueryServer, - oracle::v1::query_server::QueryServer as OracleQueryServer, - service::v1::{ + astria_vendored::connect::{ + marketmap::v2::query_server::QueryServer as MarketMapQueryServer, + oracle::v2::query_server::QueryServer as OracleQueryServer, + service::v2::{ oracle_client::OracleClient, QueryPricesRequest, }, @@ -121,13 +121,18 @@ impl Sequencer { None } else { let uri: Uri = config - .slinky_grpc_addr + .connect_grpc_addr .parse() .context("failed parsing oracle grpc address as Uri")?; let endpoint = Endpoint::from(uri.clone()).timeout(std::time::Duration::from_millis( config.oracle_client_timeout_milliseconds, )); - let mut oracle_client = OracleClient::new(endpoint.connect_lazy()); + let mut oracle_client = OracleClient::new( + endpoint + .connect() + .await + .wrap_err("failed to connect to oracle sidecar")?, + ); // ensure the oracle sidecar is reachable // TODO: allow this to retry in case the oracle sidecar is not ready yet @@ -236,8 +241,8 @@ fn start_grpc_server( let ibc = penumbra_ibc::component::rpc::IbcQuery::::new(storage.clone()); let sequencer_api = SequencerServer::new(storage.clone(), mempool); - let market_map_api = crate::grpc::slinky::SequencerServer::new(storage.clone()); - let oracle_api = crate::grpc::slinky::SequencerServer::new(storage.clone()); + let market_map_api = crate::grpc::connect::SequencerServer::new(storage.clone()); + let oracle_api = crate::grpc::connect::SequencerServer::new(storage.clone()); let cors_layer: CorsLayer = CorsLayer::permissive(); // TODO: setup HTTPS? diff --git a/proto/protocolapis/astria/protocol/genesis/v1/types.proto b/proto/protocolapis/astria/protocol/genesis/v1/types.proto index ba63a1fe46..e1627380fb 100644 --- a/proto/protocolapis/astria/protocol/genesis/v1/types.proto +++ b/proto/protocolapis/astria/protocol/genesis/v1/types.proto @@ -4,8 +4,8 @@ package astria.protocol.genesis.v1; import "astria/primitive/v1/types.proto"; import "astria/protocol/fees/v1/types.proto"; -import "astria_vendored/slinky/marketmap/v1/genesis.proto"; -import "astria_vendored/slinky/oracle/v1/genesis.proto"; +import "astria_vendored/connect/marketmap/v2/genesis.proto"; +import "astria_vendored/connect/oracle/v2/genesis.proto"; message GenesisAppState { string chain_id = 1; @@ -18,7 +18,7 @@ message GenesisAppState { IbcParameters ibc_parameters = 8; repeated string allowed_fee_assets = 9; GenesisFees fees = 10; - SlinkyGenesis slinky = 11; + ConnectGenesis connect = 11; } message Account { @@ -61,7 +61,7 @@ message GenesisFees { astria.protocol.fees.v1.ValidatorUpdateFeeComponents validator_update = 14; } -message SlinkyGenesis { - astria_vendored.slinky.marketmap.v1.GenesisState market_map = 1; - astria_vendored.slinky.oracle.v1.GenesisState oracle = 2; +message ConnectGenesis { + astria_vendored.connect.marketmap.v2.GenesisState market_map = 1; + astria_vendored.connect.oracle.v2.GenesisState oracle = 2; } diff --git a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto b/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto deleted file mode 100644 index 7ba82446cc..0000000000 --- a/proto/protocolapis/astria/protocol/genesis/v1alpha1/types.proto +++ /dev/null @@ -1,59 +0,0 @@ -syntax = "proto3"; - -package astria.protocol.genesis.v1alpha1; - -import "astria/primitive/v1/types.proto"; -import "astria/protocol/fees/v1alpha1/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; - GenesisFees fees = 10; -} - -message Account { - astria.primitive.v1.Address address = 1; - astria.primitive.v1.Uint128 balance = 2; -} - -message AddressPrefixes { - // The base prefix used for most Astria Sequencer addresses. - string base = 1; - // The prefix used for sending ics20 transfers to IBC chains - // that enforce a bech32 format of the packet sender. - string ibc_compat = 2; -} - -// 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 GenesisFees { - astria.protocol.fees.v1alpha1.BridgeLockFeeComponents bridge_lock = 1; - astria.protocol.fees.v1alpha1.BridgeSudoChangeFeeComponents bridge_sudo_change = 2; - astria.protocol.fees.v1alpha1.BridgeUnlockFeeComponents bridge_unlock = 3; - astria.protocol.fees.v1alpha1.FeeAssetChangeFeeComponents fee_asset_change = 4; - astria.protocol.fees.v1alpha1.FeeChangeFeeComponents fee_change = 5; - astria.protocol.fees.v1alpha1.IbcRelayFeeComponents ibc_relay = 7; - astria.protocol.fees.v1alpha1.IbcRelayerChangeFeeComponents ibc_relayer_change = 6; - astria.protocol.fees.v1alpha1.IbcSudoChangeFeeComponents ibc_sudo_change = 8; - astria.protocol.fees.v1alpha1.Ics20WithdrawalFeeComponents ics20_withdrawal = 9; - astria.protocol.fees.v1alpha1.InitBridgeAccountFeeComponents init_bridge_account = 10; - astria.protocol.fees.v1alpha1.RollupDataSubmissionFeeComponents rollup_data_submission = 11; - astria.protocol.fees.v1alpha1.SudoAddressChangeFeeComponents sudo_address_change = 12; - astria.protocol.fees.v1alpha1.TransferFeeComponents transfer = 13; - astria.protocol.fees.v1alpha1.ValidatorUpdateFeeComponents validator_update = 14; -} diff --git a/proto/protocolapis/astria_vendored/slinky/abci/v1/vote_extensions.proto b/proto/protocolapis/astria_vendored/connect/abci/v2/vote_extensions.proto similarity index 60% rename from proto/protocolapis/astria_vendored/slinky/abci/v1/vote_extensions.proto rename to proto/protocolapis/astria_vendored/connect/abci/v2/vote_extensions.proto index eeb4da3a31..be8c398957 100644 --- a/proto/protocolapis/astria_vendored/slinky/abci/v1/vote_extensions.proto +++ b/proto/protocolapis/astria_vendored/connect/abci/v2/vote_extensions.proto @@ -1,8 +1,7 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/abci/v1/vote_extensions.proto syntax = "proto3"; -package astria_vendored.slinky.abci.v1; +package astria_vendored.connect.abci.v2; -option go_package = "github.com/skip-mev/slinky/abci/ve/types"; +option go_package = "github.com/skip-mev/connect/v2/abci/ve/types"; // OracleVoteExtension defines the vote extension structure for oracle prices. message OracleVoteExtension { diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/genesis.proto b/proto/protocolapis/astria_vendored/connect/marketmap/v2/genesis.proto similarity index 59% rename from proto/protocolapis/astria_vendored/slinky/marketmap/v1/genesis.proto rename to proto/protocolapis/astria_vendored/connect/marketmap/v2/genesis.proto index befe34e2f4..f3600c295b 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/genesis.proto +++ b/proto/protocolapis/astria_vendored/connect/marketmap/v2/genesis.proto @@ -1,11 +1,10 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/genesis.proto syntax = "proto3"; -package astria_vendored.slinky.marketmap.v1; +package astria_vendored.connect.marketmap.v2; -import "astria_vendored/slinky/marketmap/v1/market.proto"; -import "astria_vendored/slinky/marketmap/v1/params.proto"; +import "astria_vendored/connect/marketmap/v2/market.proto"; +import "astria_vendored/connect/marketmap/v2/params.proto"; -option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; +option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; // GenesisState defines the x/marketmap module's genesis state. message GenesisState { diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/market.proto b/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto similarity index 85% rename from proto/protocolapis/astria_vendored/slinky/marketmap/v1/market.proto rename to proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto index af7b314408..5f91937fe4 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/market.proto +++ b/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto @@ -1,10 +1,9 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/market.proto syntax = "proto3"; -package astria_vendored.slinky.marketmap.v1; +package astria_vendored.connect.marketmap.v2; -import "astria_vendored/slinky/types/v1/currency_pair.proto"; +option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; -option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; +import "astria_vendored/connect/types/v2/currency_pair.proto"; // Market encapsulates a Ticker and its provider-specific configuration. message Market { @@ -22,7 +21,7 @@ message Market { // providers required to consider the ticker valid. message Ticker { // CurrencyPair is the currency pair for this ticker. - slinky.types.v1.CurrencyPair currency_pair = 1; + astria_vendored.connect.types.v2.CurrencyPair currency_pair = 1; // Decimals is the number of decimal places for the ticker. The number of // decimal places is used to convert the price to a human-readable format. @@ -55,7 +54,7 @@ message ProviderConfig { // For example, if the desired Ticker is BTC/USD, this market could be reached // using: OffChainTicker = BTC/USDT NormalizeByPair = USDT/USD This field is // optional and nullable. - slinky.types.v1.CurrencyPair normalize_by_pair = 3; + astria_vendored.connect.types.v2.CurrencyPair normalize_by_pair = 3; // Invert is a boolean indicating if the BASE and QUOTE of the market should // be inverted. i.e. BASE -> QUOTE, QUOTE -> BASE diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/params.proto b/proto/protocolapis/astria_vendored/connect/marketmap/v2/params.proto similarity index 63% rename from proto/protocolapis/astria_vendored/slinky/marketmap/v1/params.proto rename to proto/protocolapis/astria_vendored/connect/marketmap/v2/params.proto index b71a2ae56c..db0678ab07 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/params.proto +++ b/proto/protocolapis/astria_vendored/connect/marketmap/v2/params.proto @@ -1,8 +1,7 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/params.proto syntax = "proto3"; -package astria_vendored.slinky.marketmap.v1; +package astria_vendored.connect.marketmap.v2; -option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; +option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; // Params defines the parameters for the x/marketmap module. message Params { diff --git a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto b/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto similarity index 71% rename from proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto rename to proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto index 26daa6ce58..c6499c41e7 100644 --- a/proto/protocolapis/astria_vendored/slinky/marketmap/v1/query.proto +++ b/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto @@ -1,36 +1,43 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/marketmap/v1/query.proto syntax = "proto3"; -package astria_vendored.slinky.marketmap.v1; +package astria_vendored.connect.marketmap.v2; -import "astria_vendored/slinky/marketmap/v1/market.proto"; -import "astria_vendored/slinky/marketmap/v1/params.proto"; -import "astria_vendored/slinky/types/v1/currency_pair.proto"; import "google/api/annotations.proto"; +import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "astria_vendored/connect/marketmap/v2/market.proto"; +import "astria_vendored/connect/marketmap/v2/params.proto"; -option go_package = "github.com/skip-mev/slinky/x/marketmap/types"; +option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; // Query is the query service for the x/marketmap module. service Query { // MarketMap returns the full market map stored in the x/marketmap // module. rpc MarketMap(MarketMapRequest) returns (MarketMapResponse) { - option (google.api.http).get = "/slinky/marketmap/v1/marketmap"; + option (google.api.http) = { + get : "/connect/marketmap/v2/marketmap" + }; } // Market returns a market stored in the x/marketmap // module. rpc Market(MarketRequest) returns (MarketResponse) { - option (google.api.http).get = "/slinky/marketmap/v1/market"; + option (google.api.http) = { + get : "/connect/marketmap/v2/market" + }; } // LastUpdated returns the last height the market map was updated at. rpc LastUpdated(LastUpdatedRequest) returns (LastUpdatedResponse) { - option (google.api.http).get = "/slinky/marketmap/v1/last_updated"; + option (google.api.http) = { + get : "/connect/marketmap/v2/last_updated" + }; } // Params returns the current x/marketmap module parameters. rpc Params(ParamsRequest) returns (ParamsResponse) { - option (google.api.http) = {get: "/slinky/marketmap/v1/params"}; + option (google.api.http) = { + get : "/connect/marketmap/v2/params" + }; } } @@ -58,7 +65,7 @@ message MarketMapResponse { message MarketRequest { // CurrencyPair is the currency pair associated with the market being // requested. - slinky.types.v1.CurrencyPair currency_pair = 1; + astria_vendored.connect.types.v2.CurrencyPair currency_pair = 1; } // MarketResponse is the query response for the Market query. @@ -71,9 +78,7 @@ message MarketResponse { message ParamsRequest {} // ParamsResponse is the response type for the Query/Params RPC method. -message ParamsResponse { - Params params = 1; -} +message ParamsResponse { Params params = 1; } // LastUpdatedRequest is the request type for the Query/LastUpdated RPC // method. @@ -81,6 +86,4 @@ message LastUpdatedRequest {} // LastUpdatedResponse is the response type for the Query/LastUpdated RPC // method. -message LastUpdatedResponse { - uint64 last_updated = 1; -} +message LastUpdatedResponse { uint64 last_updated = 1; } diff --git a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto b/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto similarity index 85% rename from proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto rename to proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto index bd9b806b8c..2ffc337741 100644 --- a/proto/protocolapis/astria_vendored/slinky/oracle/v1/genesis.proto +++ b/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto @@ -1,11 +1,10 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/oracle/v1/genesis.proto syntax = "proto3"; -package astria_vendored.slinky.oracle.v1; +package astria_vendored.connect.oracle.v2; -import "astria_vendored/slinky/types/v1/currency_pair.proto"; -import "google/protobuf/timestamp.proto"; +option go_package = "github.com/skip-mev/connect/v2/x/oracle/types"; -option go_package = "github.com/skip-mev/slinky/x/oracle/types"; +import "google/protobuf/timestamp.proto"; +import "astria_vendored/connect/types/v2/currency_pair.proto"; // QuotePrice is the representation of the aggregated prices for a CurrencyPair, // where price represents the price of Base in terms of Quote @@ -16,7 +15,7 @@ message QuotePrice { // We include block timestamp alongside the price to ensure that smart // contracts and applications are not utilizing stale oracle prices google.protobuf.Timestamp block_timestamp = 2; - + // BlockHeight is height of block mentioned above uint64 block_height = 3; } @@ -39,7 +38,7 @@ message CurrencyPairState { // CurrencyPair. message CurrencyPairGenesis { // The CurrencyPair to be added to module state - slinky.types.v1.CurrencyPair currency_pair = 1; + astria_vendored.connect.types.v2.CurrencyPair currency_pair = 1; // A genesis price if one exists (note this will be empty, unless it results // from forking the state of this module) QuotePrice currency_pair_price = 2; diff --git a/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto b/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto similarity index 62% rename from proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto rename to proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto index 6a1986d1da..dee406dcd8 100644 --- a/proto/protocolapis/astria_vendored/slinky/oracle/v1/query.proto +++ b/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto @@ -1,35 +1,45 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/oracle/v1/query.proto syntax = "proto3"; -package astria_vendored.slinky.oracle.v1; +package astria_vendored.connect.oracle.v2; -import "astria_vendored/slinky/oracle/v1/genesis.proto"; -import "astria_vendored/slinky/types/v1/currency_pair.proto"; import "google/api/annotations.proto"; +import "astria_vendored/connect/oracle/v2/genesis.proto"; +import "astria_vendored/connect/types/v2/currency_pair.proto"; -option go_package = "github.com/skip-mev/slinky/x/oracle/types"; +option go_package = "github.com/skip-mev/connect/v2/x/oracle/types"; // Query is the query service for the x/oracle module. service Query { // Get all the currency pairs the x/oracle module is tracking price-data for. - rpc GetAllCurrencyPairs(GetAllCurrencyPairsRequest) returns (GetAllCurrencyPairsResponse) { - option (google.api.http).get = "/slinky/oracle/v1/get_all_tickers"; - } + rpc GetAllCurrencyPairs(GetAllCurrencyPairsRequest) + returns (GetAllCurrencyPairsResponse) { + option (google.api.http) = { + get : "/connect/oracle/v2/get_all_tickers" + }; + }; // Given a CurrencyPair (or its identifier) return the latest QuotePrice for // that CurrencyPair. rpc GetPrice(GetPriceRequest) returns (GetPriceResponse) { - option (google.api.http).get = "/slinky/oracle/v1/get_price"; - } + option (google.api.http) = { + get : "/connect/oracle/v2/get_price" + }; + }; rpc GetPrices(GetPricesRequest) returns (GetPricesResponse) { - option (google.api.http).get = "/slinky/oracle/v1/get_prices"; + option (google.api.http) = { + get : "/connect/oracle/v2/get_prices" + }; } // Get the mapping of currency pair ID -> currency pair. This is useful for // indexers that have access to the ID of a currency pair, but no way to get // the underlying currency pair from it. - rpc GetCurrencyPairMapping(GetCurrencyPairMappingRequest) returns (GetCurrencyPairMappingResponse) { - option (google.api.http).get = "/slinky/oracle/v1/get_currency_pair_mapping"; + rpc GetCurrencyPairMapping(GetCurrencyPairMappingRequest) + returns (GetCurrencyPairMappingResponse) { + option (google.api.http) = { + get : "/connect/oracle/v2/get_currency_pair_mapping" + additional_bindings : [] + }; } } @@ -38,14 +48,15 @@ message GetAllCurrencyPairsRequest {} // GetAllCurrencyPairsResponse returns all CurrencyPairs that the module is // currently tracking. message GetAllCurrencyPairsResponse { - repeated slinky.types.v1.CurrencyPair currency_pairs = 1; + repeated astria_vendored.connect.types.v2.CurrencyPair currency_pairs = 1 + ; } -// GetPriceRequest either takes a CurrencyPair, or an identifier for the +// GetPriceRequest takes an identifier for the // CurrencyPair in the format base/quote. message GetPriceRequest { // CurrencyPair represents the pair that the user wishes to query. - slinky.types.v1.CurrencyPair currency_pair = 1; + string currency_pair = 1; } // GetPriceResponse is the response from the GetPrice grpc method exposed from @@ -57,8 +68,7 @@ message GetPriceResponse { // nonce represents the nonce for the CurrencyPair if it exists in state uint64 nonce = 2; // decimals represents the number of decimals that the quote-price is - // represented in. For Pairs where ETHEREUM is the quote this will be 18, - // otherwise it will be 8. + // represented in. It is used to scale the QuotePrice to its proper value. uint64 decimals = 3; // ID represents the identifier for the CurrencyPair. uint64 id = 4; @@ -66,9 +76,7 @@ message GetPriceResponse { // GetPricesRequest takes an identifier for the CurrencyPair // in the format base/quote. -message GetPricesRequest { - repeated string currency_pair_ids = 1; -} +message GetPricesRequest { repeated string currency_pair_ids = 1; } // GetPricesResponse is the response from the GetPrices grpc method exposed from // the x/oracle query service. @@ -83,5 +91,5 @@ message GetCurrencyPairMappingRequest {} message GetCurrencyPairMappingResponse { // currency_pair_mapping is a mapping of the id representing the currency pair // to the currency pair itself. - map currency_pair_mapping = 1; + map currency_pair_mapping = 1; } diff --git a/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto b/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto new file mode 100644 index 0000000000..8d817c220e --- /dev/null +++ b/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; +package astria_vendored.connect.service.v2; + +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "astria_vendored/connect/marketmap/v2/market.proto"; + +option go_package = "github.com/skip-mev/connect/v2/service/servers/oracle/types"; + +// Oracle defines the gRPC oracle service. +service Oracle { + // Prices defines a method for fetching the latest prices. + rpc Prices(QueryPricesRequest) returns (QueryPricesResponse) { + option (google.api.http) = { + get : "/connect/oracle/v2/prices" + }; + } + + // MarketMap defines a method for fetching the latest market map + // configuration. + rpc MarketMap(QueryMarketMapRequest) returns (QueryMarketMapResponse) { + option (google.api.http) = { + get : "/connect/oracle/v2/marketmap" + }; + } + + // Version defines a method for fetching the current version of the oracle + // service. + rpc Version(QueryVersionRequest) returns (QueryVersionResponse) { + option (google.api.http) = { + get : "/connect/oracle/v2/version" + }; + } +} + +// QueryPricesRequest defines the request type for the the Prices method. +message QueryPricesRequest {} + +// QueryPricesResponse defines the response type for the Prices method. +message QueryPricesResponse { + // Prices defines the list of prices. + map prices = 1; + + // Timestamp defines the timestamp of the prices. + google.protobuf.Timestamp timestamp = 2; + + // Version defines the version of the oracle service that provided the prices. + string version = 3; +} + +// QueryMarketMapRequest defines the request type for the MarketMap method. +message QueryMarketMapRequest {} + +// QueryMarketMapResponse defines the response type for the MarketMap method. +message QueryMarketMapResponse { + // MarketMap defines the current market map configuration. + astria_vendored.connect.marketmap.v2.MarketMap market_map = 1; +} + +// QueryVersionRequest defines the request type for the Version method. +message QueryVersionRequest {} + +// QueryVersionResponse defines the response type for the Version method. +message QueryVersionResponse { + // Version defines the current version of the oracle service. + string version = 1; +} diff --git a/proto/protocolapis/astria_vendored/connect/types/v2/currency_pair.proto b/proto/protocolapis/astria_vendored/connect/types/v2/currency_pair.proto new file mode 100644 index 0000000000..9204c2efb9 --- /dev/null +++ b/proto/protocolapis/astria_vendored/connect/types/v2/currency_pair.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package astria_vendored.connect.types.v2; + +option go_package = "github.com/skip-mev/connect/v2/pkg/types"; + +// CurrencyPair is the standard representation of a pair of assets, where one +// (Base) is priced in terms of the other (Quote) +message CurrencyPair { + string Base = 1; + string Quote = 2; +} diff --git a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto b/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto deleted file mode 100644 index d0e36965c1..0000000000 --- a/proto/protocolapis/astria_vendored/slinky/service/v1/oracle.proto +++ /dev/null @@ -1,42 +0,0 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/service/v1/oracle.proto -syntax = "proto3"; -package astria_vendored.slinky.service.v1; - -import "astria_vendored/slinky/marketmap/v1/market.proto"; -import "google/api/annotations.proto"; -import "google/protobuf/timestamp.proto"; - -option go_package = "github.com/skip-mev/slinky/service/servers/oracle/types"; - -// Oracle defines the gRPC oracle service. -service Oracle { - // Prices defines a method for fetching the latest prices. - rpc Prices(QueryPricesRequest) returns (QueryPricesResponse) { - option (google.api.http).get = "/slinky/oracle/v1/prices"; - } - - // MarketMap defines a method for fetching the latest market map - // configuration. - rpc MarketMap(QueryMarketMapRequest) returns (QueryMarketMapResponse) { - option (google.api.http).get = "/slinky/oracle/v1/marketmap"; - } -} - -// QueryPricesRequest defines the request type for the the Prices method. -message QueryPricesRequest {} - -// QueryPricesResponse defines the response type for the Prices method. -message QueryPricesResponse { - // prices defines the list of prices. - map prices = 1; - google.protobuf.Timestamp timestamp = 2; -} - -// QueryMarketMapRequest defines the request type for the MarketMap method. -message QueryMarketMapRequest {} - -// QueryMarketMapResponse defines the response type for the MarketMap method. -message QueryMarketMapResponse { - // market_map defines the current market map configuration. - slinky.marketmap.v1.MarketMap market_map = 1; -} diff --git a/proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto b/proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto deleted file mode 100644 index 1facb29653..0000000000 --- a/proto/protocolapis/astria_vendored/slinky/types/v1/currency_pair.proto +++ /dev/null @@ -1,12 +0,0 @@ -// source: https://github.com/skip-mev/connect/blob/158cde8a4b774ac4eec5c6d1a2c16de6a8c6abb5/proto/slinky/types/v1/currency_pair.proto -syntax = "proto3"; -package astria_vendored.slinky.types.v1; - -option go_package = "github.com/skip-mev/slinky/pkg/types"; - -// CurrencyPair is the standard representation of a pair of assets, where one -// (Base) is priced in terms of the other (Quote) -message CurrencyPair { - string Base = 1; - string Quote = 2; -} diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index fb6a3d1f64..4c3f812547 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -63,10 +63,10 @@ fn main() { .build_client(true) .build_server(true) .emit_rerun_if_changed(false) - .btree_map([".astria_vendored.slinky"]) + .btree_map([".astria_vendored.connect"]) .bytes([ ".astria", - ".astria_vendored.slinky", + ".astria_vendored.connect", ".celestia", ".cosmos", ".tendermint", @@ -95,7 +95,7 @@ fn main() { pbjson_build::Builder::new() .register_descriptors(&descriptor_set) .unwrap() - .btree_map([".astria_vendored.slinky"]) + .btree_map([".astria_vendored.connect"]) .out_dir(&out_dir) .build(&[ ".astria", From 17cbd08ca6f6e65baa4f069ead1eb277407e15b1 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 29 Oct 2024 17:36:31 -0400 Subject: [PATCH 77/89] fmt proto --- .../connect/marketmap/v2/market.proto | 4 +-- .../connect/marketmap/v2/query.proto | 28 +++++++-------- .../connect/oracle/v2/genesis.proto | 8 ++--- .../connect/oracle/v2/query.proto | 35 ++++++++----------- .../connect/service/v2/oracle.proto | 16 +++------ 5 files changed, 37 insertions(+), 54 deletions(-) diff --git a/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto b/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto index 5f91937fe4..d7ff2cf2d9 100644 --- a/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto +++ b/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto @@ -1,10 +1,10 @@ syntax = "proto3"; package astria_vendored.connect.marketmap.v2; -option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; - import "astria_vendored/connect/types/v2/currency_pair.proto"; +option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; + // Market encapsulates a Ticker and its provider-specific configuration. message Market { // Ticker represents a price feed for a given asset pair i.e. BTC/USD. The diff --git a/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto b/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto index c6499c41e7..55f18c6295 100644 --- a/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto +++ b/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto @@ -1,10 +1,10 @@ syntax = "proto3"; package astria_vendored.connect.marketmap.v2; -import "google/api/annotations.proto"; -import "astria_vendored/connect/types/v2/currency_pair.proto"; import "astria_vendored/connect/marketmap/v2/market.proto"; import "astria_vendored/connect/marketmap/v2/params.proto"; +import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "google/api/annotations.proto"; option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; @@ -13,31 +13,23 @@ service Query { // MarketMap returns the full market map stored in the x/marketmap // module. rpc MarketMap(MarketMapRequest) returns (MarketMapResponse) { - option (google.api.http) = { - get : "/connect/marketmap/v2/marketmap" - }; + option (google.api.http) = {get: "/connect/marketmap/v2/marketmap"}; } // Market returns a market stored in the x/marketmap // module. rpc Market(MarketRequest) returns (MarketResponse) { - option (google.api.http) = { - get : "/connect/marketmap/v2/market" - }; + option (google.api.http) = {get: "/connect/marketmap/v2/market"}; } // LastUpdated returns the last height the market map was updated at. rpc LastUpdated(LastUpdatedRequest) returns (LastUpdatedResponse) { - option (google.api.http) = { - get : "/connect/marketmap/v2/last_updated" - }; + option (google.api.http) = {get: "/connect/marketmap/v2/last_updated"}; } // Params returns the current x/marketmap module parameters. rpc Params(ParamsRequest) returns (ParamsResponse) { - option (google.api.http) = { - get : "/connect/marketmap/v2/params" - }; + option (google.api.http) = {get: "/connect/marketmap/v2/params"}; } } @@ -78,7 +70,9 @@ message MarketResponse { message ParamsRequest {} // ParamsResponse is the response type for the Query/Params RPC method. -message ParamsResponse { Params params = 1; } +message ParamsResponse { + Params params = 1; +} // LastUpdatedRequest is the request type for the Query/LastUpdated RPC // method. @@ -86,4 +80,6 @@ message LastUpdatedRequest {} // LastUpdatedResponse is the response type for the Query/LastUpdated RPC // method. -message LastUpdatedResponse { uint64 last_updated = 1; } +message LastUpdatedResponse { + uint64 last_updated = 1; +} diff --git a/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto b/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto index 2ffc337741..d779fc1ded 100644 --- a/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto +++ b/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto @@ -1,10 +1,10 @@ syntax = "proto3"; package astria_vendored.connect.oracle.v2; -option go_package = "github.com/skip-mev/connect/v2/x/oracle/types"; - -import "google/protobuf/timestamp.proto"; import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/skip-mev/connect/v2/x/oracle/types"; // QuotePrice is the representation of the aggregated prices for a CurrencyPair, // where price represents the price of Base in terms of Quote @@ -15,7 +15,7 @@ message QuotePrice { // We include block timestamp alongside the price to ensure that smart // contracts and applications are not utilizing stale oracle prices google.protobuf.Timestamp block_timestamp = 2; - + // BlockHeight is height of block mentioned above uint64 block_height = 3; } diff --git a/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto b/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto index dee406dcd8..859250462d 100644 --- a/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto +++ b/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto @@ -1,44 +1,36 @@ syntax = "proto3"; package astria_vendored.connect.oracle.v2; -import "google/api/annotations.proto"; import "astria_vendored/connect/oracle/v2/genesis.proto"; import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "google/api/annotations.proto"; option go_package = "github.com/skip-mev/connect/v2/x/oracle/types"; // Query is the query service for the x/oracle module. service Query { // Get all the currency pairs the x/oracle module is tracking price-data for. - rpc GetAllCurrencyPairs(GetAllCurrencyPairsRequest) - returns (GetAllCurrencyPairsResponse) { - option (google.api.http) = { - get : "/connect/oracle/v2/get_all_tickers" - }; - }; + rpc GetAllCurrencyPairs(GetAllCurrencyPairsRequest) returns (GetAllCurrencyPairsResponse) { + option (google.api.http) = {get: "/connect/oracle/v2/get_all_tickers"}; + } // Given a CurrencyPair (or its identifier) return the latest QuotePrice for // that CurrencyPair. rpc GetPrice(GetPriceRequest) returns (GetPriceResponse) { - option (google.api.http) = { - get : "/connect/oracle/v2/get_price" - }; - }; + option (google.api.http) = {get: "/connect/oracle/v2/get_price"}; + } rpc GetPrices(GetPricesRequest) returns (GetPricesResponse) { - option (google.api.http) = { - get : "/connect/oracle/v2/get_prices" - }; + option (google.api.http) = {get: "/connect/oracle/v2/get_prices"}; } // Get the mapping of currency pair ID -> currency pair. This is useful for // indexers that have access to the ID of a currency pair, but no way to get // the underlying currency pair from it. - rpc GetCurrencyPairMapping(GetCurrencyPairMappingRequest) - returns (GetCurrencyPairMappingResponse) { + rpc GetCurrencyPairMapping(GetCurrencyPairMappingRequest) returns (GetCurrencyPairMappingResponse) { option (google.api.http) = { - get : "/connect/oracle/v2/get_currency_pair_mapping" - additional_bindings : [] + get: "/connect/oracle/v2/get_currency_pair_mapping" + additional_bindings: [] }; } } @@ -48,8 +40,7 @@ message GetAllCurrencyPairsRequest {} // GetAllCurrencyPairsResponse returns all CurrencyPairs that the module is // currently tracking. message GetAllCurrencyPairsResponse { - repeated astria_vendored.connect.types.v2.CurrencyPair currency_pairs = 1 - ; + repeated astria_vendored.connect.types.v2.CurrencyPair currency_pairs = 1; } // GetPriceRequest takes an identifier for the @@ -76,7 +67,9 @@ message GetPriceResponse { // GetPricesRequest takes an identifier for the CurrencyPair // in the format base/quote. -message GetPricesRequest { repeated string currency_pair_ids = 1; } +message GetPricesRequest { + repeated string currency_pair_ids = 1; +} // GetPricesResponse is the response from the GetPrices grpc method exposed from // the x/oracle query service. diff --git a/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto b/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto index 8d817c220e..67f5c09b80 100644 --- a/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto +++ b/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto @@ -1,9 +1,9 @@ syntax = "proto3"; package astria_vendored.connect.service.v2; +import "astria_vendored/connect/marketmap/v2/market.proto"; import "google/api/annotations.proto"; import "google/protobuf/timestamp.proto"; -import "astria_vendored/connect/marketmap/v2/market.proto"; option go_package = "github.com/skip-mev/connect/v2/service/servers/oracle/types"; @@ -11,25 +11,19 @@ option go_package = "github.com/skip-mev/connect/v2/service/servers/oracle/types service Oracle { // Prices defines a method for fetching the latest prices. rpc Prices(QueryPricesRequest) returns (QueryPricesResponse) { - option (google.api.http) = { - get : "/connect/oracle/v2/prices" - }; + option (google.api.http) = {get: "/connect/oracle/v2/prices"}; } // MarketMap defines a method for fetching the latest market map // configuration. rpc MarketMap(QueryMarketMapRequest) returns (QueryMarketMapResponse) { - option (google.api.http) = { - get : "/connect/oracle/v2/marketmap" - }; + option (google.api.http) = {get: "/connect/oracle/v2/marketmap"}; } // Version defines a method for fetching the current version of the oracle // service. rpc Version(QueryVersionRequest) returns (QueryVersionResponse) { - option (google.api.http) = { - get : "/connect/oracle/v2/version" - }; + option (google.api.http) = {get: "/connect/oracle/v2/version"}; } } @@ -43,7 +37,7 @@ message QueryPricesResponse { // Timestamp defines the timestamp of the prices. google.protobuf.Timestamp timestamp = 2; - + // Version defines the version of the oracle service that provided the prices. string version = 3; } From e2238b912d11fae9c8a4f7d7f8cebc3116966bd3 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 30 Oct 2024 16:26:51 -0400 Subject: [PATCH 78/89] move connect protos to vendored, not astria_vendored --- buf.lock | 4 +- crates/astria-core/src/connect/abci.rs | 4 +- crates/astria-core/src/connect/market_map.rs | 2 +- crates/astria-core/src/connect/oracle.rs | 2 +- crates/astria-core/src/connect/service.rs | 2 +- crates/astria-core/src/connect/types.rs | 2 +- .../generated/astria.protocol.genesis.v1.rs | 4 +- ....connect.abci.v2.rs => connect.abci.v2.rs} | 4 +- ...i.v2.serde.rs => connect.abci.v2.serde.rs} | 6 +- ...arketmap.v2.rs => connect.marketmap.v2.rs} | 102 ++++++-------- ...serde.rs => connect.marketmap.v2.serde.rs} | 84 ++++++------ ...nect.oracle.v2.rs => connect.oracle.v2.rs} | 90 +++++-------- ...v2.serde.rs => connect.oracle.v2.serde.rs} | 72 +++++----- ...ct.service.v2.rs => connect.service.v2.rs} | 59 +++----- ...2.serde.rs => connect.service.v2.serde.rs} | 36 ++--- ...onnect.types.v2.rs => connect.types.v2.rs} | 4 +- ....v2.serde.rs => connect.types.v2.serde.rs} | 6 +- crates/astria-core/src/generated/mod.rs | 126 +++++++++--------- .../src/genesis_example.rs | 6 +- .../src/app/benchmark_and_test_utils.rs | 10 +- .../src/app/vote_extension.rs | 2 +- crates/astria-sequencer/src/grpc/connect.rs | 2 +- crates/astria-sequencer/src/sequencer.rs | 10 +- .../astria/protocol/genesis/v1/types.proto | 8 +- .../connect/abci/v2/vote_extensions.proto | 2 +- .../connect/marketmap/v2/genesis.proto | 6 +- .../connect/marketmap/v2/market.proto | 8 +- .../connect/marketmap/v2/params.proto | 2 +- .../connect/marketmap/v2/query.proto | 10 +- .../connect/oracle/v2/genesis.proto | 6 +- .../connect/oracle/v2/query.proto | 10 +- .../connect/service/v2/oracle.proto | 6 +- .../connect/types/v2/currency_pair.proto | 2 +- tools/protobuf-compiler/src/main.rs | 9 +- 34 files changed, 327 insertions(+), 381 deletions(-) rename crates/astria-core/src/generated/{astria_vendored.connect.abci.v2.rs => connect.abci.v2.rs} (82%) rename crates/astria-core/src/generated/{astria_vendored.connect.abci.v2.serde.rs => connect.abci.v2.serde.rs} (91%) rename crates/astria-core/src/generated/{astria_vendored.connect.marketmap.v2.rs => connect.marketmap.v2.rs} (89%) rename crates/astria-core/src/generated/{astria_vendored.connect.marketmap.v2.serde.rs => connect.marketmap.v2.serde.rs} (92%) rename crates/astria-core/src/generated/{astria_vendored.connect.oracle.v2.rs => connect.oracle.v2.rs} (90%) rename crates/astria-core/src/generated/{astria_vendored.connect.oracle.v2.serde.rs => connect.oracle.v2.serde.rs} (91%) rename crates/astria-core/src/generated/{astria_vendored.connect.service.v2.rs => connect.service.v2.rs} (91%) rename crates/astria-core/src/generated/{astria_vendored.connect.service.v2.serde.rs => connect.service.v2.serde.rs} (89%) rename crates/astria-core/src/generated/{astria_vendored.connect.types.v2.rs => connect.types.v2.rs} (78%) rename crates/astria-core/src/generated/{astria_vendored.connect.types.v2.serde.rs => connect.types.v2.serde.rs} (92%) rename proto/{protocolapis/astria_vendored => vendored}/connect/abci/v2/vote_extensions.proto (91%) rename proto/{protocolapis/astria_vendored => vendored}/connect/marketmap/v2/genesis.proto (78%) rename proto/{protocolapis/astria_vendored => vendored}/connect/marketmap/v2/market.proto (91%) rename proto/{protocolapis/astria_vendored => vendored}/connect/marketmap/v2/params.proto (91%) rename proto/{protocolapis/astria_vendored => vendored}/connect/marketmap/v2/query.proto (89%) rename proto/{protocolapis/astria_vendored => vendored}/connect/oracle/v2/genesis.proto (92%) rename proto/{protocolapis/astria_vendored => vendored}/connect/oracle/v2/query.proto (90%) rename proto/{protocolapis/astria_vendored => vendored}/connect/service/v2/oracle.proto (92%) rename proto/{protocolapis/astria_vendored => vendored}/connect/types/v2/currency_pair.proto (86%) diff --git a/buf.lock b/buf.lock index c6571c8f90..077deea8e6 100644 --- a/buf.lock +++ b/buf.lock @@ -2,5 +2,5 @@ version: v2 deps: - name: buf.build/googleapis/googleapis - commit: 4ed3bc159a8b4ac68fe253218760d035 - digest: b5:74a7798987b123218c004cf28543a2835e432ca04a69de99cd394a29dbad24d9ed38344f0b7c97ad6476039506c4eb38c2f4a8eef9cec3da2e38e4216a22d495 + commit: f52d4f76a8434cc5966798b1d3b4f110 + digest: b5:5e634ff0ee118aea188b3e9c13ad7d285a192ef6c591bc20ff5a3360438d6ca310cfe9d663b20d60e1daa21789add35b919eac84e8e94a4d576e83a50dd2d62c diff --git a/crates/astria-core/src/connect/abci.rs b/crates/astria-core/src/connect/abci.rs index 27fb6ac9ca..0394cec7fb 100644 --- a/crates/astria-core/src/connect/abci.rs +++ b/crates/astria-core/src/connect/abci.rs @@ -7,7 +7,7 @@ pub mod v2 { CurrencyPairId, Price, }, - generated::astria_vendored::connect::abci::v2 as raw, + generated::connect::abci::v2 as raw, }; #[derive(Debug, thiserror::Error)] @@ -15,7 +15,7 @@ pub mod v2 { pub struct OracleVoteExtensionError(#[from] OracleVoteExtensionErrorKind); #[derive(Debug, thiserror::Error)] - #[error("failed to validate astria_vendored.connect.abci.v1.OracleVoteExtension")] + #[error("failed to validate connect.abci.v2.OracleVoteExtension")] enum OracleVoteExtensionErrorKind { #[error("failed decoding price value in .prices field for key `{id}`")] DecodePrice { diff --git a/crates/astria-core/src/connect/market_map.rs b/crates/astria-core/src/connect/market_map.rs index eea7654e86..19b9249294 100644 --- a/crates/astria-core/src/connect/market_map.rs +++ b/crates/astria-core/src/connect/market_map.rs @@ -8,7 +8,7 @@ pub mod v2 { CurrencyPair, CurrencyPairError, }, - generated::astria_vendored::connect::marketmap::v2 as raw, + generated::connect::marketmap::v2 as raw, primitive::v1::{ Address, AddressError, diff --git a/crates/astria-core/src/connect/oracle.rs b/crates/astria-core/src/connect/oracle.rs index 3397fc5e6d..8cd723c542 100644 --- a/crates/astria-core/src/connect/oracle.rs +++ b/crates/astria-core/src/connect/oracle.rs @@ -10,7 +10,7 @@ pub mod v2 { ParsePriceError, Price, }, - generated::astria_vendored::connect::oracle::v2 as raw, + generated::connect::oracle::v2 as raw, Protobuf, }; diff --git a/crates/astria-core/src/connect/service.rs b/crates/astria-core/src/connect/service.rs index 36f7da5120..3af6c5848f 100644 --- a/crates/astria-core/src/connect/service.rs +++ b/crates/astria-core/src/connect/service.rs @@ -8,7 +8,7 @@ pub mod v2 { ParsePriceError, Price, }, - generated::astria_vendored::connect::service::v2 as raw, + generated::connect::service::v2 as raw, }; #[derive(Debug, thiserror::Error)] diff --git a/crates/astria-core/src/connect/types.rs b/crates/astria-core/src/connect/types.rs index 633b2c9981..2009a73c38 100644 --- a/crates/astria-core/src/connect/types.rs +++ b/crates/astria-core/src/connect/types.rs @@ -7,7 +7,7 @@ pub mod v2 { use bytes::Bytes; - use crate::generated::astria_vendored::connect::types::v2 as raw; + use crate::generated::connect::types::v2 as raw; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Price(u128); diff --git a/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs b/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs index 20b5ec8320..991867b813 100644 --- a/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs +++ b/crates/astria-core/src/generated/astria.protocol.genesis.v1.rs @@ -160,11 +160,11 @@ impl ::prost::Name for GenesisFees { pub struct ConnectGenesis { #[prost(message, optional, tag = "1")] pub market_map: ::core::option::Option< - super::super::super::super::astria_vendored::connect::marketmap::v2::GenesisState, + super::super::super::super::connect::marketmap::v2::GenesisState, >, #[prost(message, optional, tag = "2")] pub oracle: ::core::option::Option< - super::super::super::super::astria_vendored::connect::oracle::v2::GenesisState, + super::super::super::super::connect::oracle::v2::GenesisState, >, } impl ::prost::Name for ConnectGenesis { diff --git a/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.rs b/crates/astria-core/src/generated/connect.abci.v2.rs similarity index 82% rename from crates/astria-core/src/generated/astria_vendored.connect.abci.v2.rs rename to crates/astria-core/src/generated/connect.abci.v2.rs index 287d08a45f..6940f8594b 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.rs +++ b/crates/astria-core/src/generated/connect.abci.v2.rs @@ -10,8 +10,8 @@ pub struct OracleVoteExtension { } impl ::prost::Name for OracleVoteExtension { const NAME: &'static str = "OracleVoteExtension"; - const PACKAGE: &'static str = "astria_vendored.connect.abci.v2"; + const PACKAGE: &'static str = "connect.abci.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.abci.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.abci.v2.{}", Self::NAME) } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.serde.rs b/crates/astria-core/src/generated/connect.abci.v2.serde.rs similarity index 91% rename from crates/astria-core/src/generated/astria_vendored.connect.abci.v2.serde.rs rename to crates/astria-core/src/generated/connect.abci.v2.serde.rs index be62d28c24..b909661c94 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.abci.v2.serde.rs +++ b/crates/astria-core/src/generated/connect.abci.v2.serde.rs @@ -9,7 +9,7 @@ impl serde::Serialize for OracleVoteExtension { if !self.prices.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.abci.v2.OracleVoteExtension", len)?; + let mut struct_ser = serializer.serialize_struct("connect.abci.v2.OracleVoteExtension", len)?; if !self.prices.is_empty() { let v: std::collections::HashMap<_, _> = self.prices.iter() .map(|(k, v)| (k, pbjson::private::base64::encode(v))).collect(); @@ -65,7 +65,7 @@ impl<'de> serde::Deserialize<'de> for OracleVoteExtension { type Value = OracleVoteExtension; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.abci.v2.OracleVoteExtension") + formatter.write_str("struct connect.abci.v2.OracleVoteExtension") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -91,6 +91,6 @@ impl<'de> serde::Deserialize<'de> for OracleVoteExtension { }) } } - deserializer.deserialize_struct("astria_vendored.connect.abci.v2.OracleVoteExtension", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.abci.v2.OracleVoteExtension", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.rs b/crates/astria-core/src/generated/connect.marketmap.v2.rs similarity index 89% rename from crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.rs rename to crates/astria-core/src/generated/connect.marketmap.v2.rs index f632496531..148e1e3db5 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.rs +++ b/crates/astria-core/src/generated/connect.marketmap.v2.rs @@ -13,9 +13,9 @@ pub struct Market { } impl ::prost::Name for Market { const NAME: &'static str = "Market"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// Ticker represents a price feed for a given asset pair i.e. BTC/USD. The price @@ -46,9 +46,9 @@ pub struct Ticker { } impl ::prost::Name for Ticker { const NAME: &'static str = "Ticker"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -80,9 +80,9 @@ pub struct ProviderConfig { } impl ::prost::Name for ProviderConfig { const NAME: &'static str = "ProviderConfig"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// MarketMap maps ticker strings to their Markets. @@ -99,9 +99,9 @@ pub struct MarketMap { } impl ::prost::Name for MarketMap { const NAME: &'static str = "MarketMap"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// Params defines the parameters for the x/marketmap module. @@ -119,9 +119,9 @@ pub struct Params { } impl ::prost::Name for Params { const NAME: &'static str = "Params"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// GenesisState defines the x/marketmap module's genesis state. @@ -143,9 +143,9 @@ pub struct GenesisState { } impl ::prost::Name for GenesisState { const NAME: &'static str = "GenesisState"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// MarketMapRequest is the query request for the MarketMap query. @@ -155,9 +155,9 @@ impl ::prost::Name for GenesisState { pub struct MarketMapRequest {} impl ::prost::Name for MarketMapRequest { const NAME: &'static str = "MarketMapRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// MarketMapResponse is the query response for the MarketMap query. @@ -179,9 +179,9 @@ pub struct MarketMapResponse { } impl ::prost::Name for MarketMapResponse { const NAME: &'static str = "MarketMapResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// MarketRequest is the query request for the Market query. @@ -196,9 +196,9 @@ pub struct MarketRequest { } impl ::prost::Name for MarketRequest { const NAME: &'static str = "MarketRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// MarketResponse is the query response for the Market query. @@ -211,9 +211,9 @@ pub struct MarketResponse { } impl ::prost::Name for MarketResponse { const NAME: &'static str = "MarketResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// ParamsRequest is the request type for the Query/Params RPC method. @@ -222,9 +222,9 @@ impl ::prost::Name for MarketResponse { pub struct ParamsRequest {} impl ::prost::Name for ParamsRequest { const NAME: &'static str = "ParamsRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// ParamsResponse is the response type for the Query/Params RPC method. @@ -236,9 +236,9 @@ pub struct ParamsResponse { } impl ::prost::Name for ParamsResponse { const NAME: &'static str = "ParamsResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// LastUpdatedRequest is the request type for the Query/LastUpdated RPC @@ -248,9 +248,9 @@ impl ::prost::Name for ParamsResponse { pub struct LastUpdatedRequest {} impl ::prost::Name for LastUpdatedRequest { const NAME: &'static str = "LastUpdatedRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// LastUpdatedResponse is the response type for the Query/LastUpdated RPC @@ -263,9 +263,9 @@ pub struct LastUpdatedResponse { } impl ::prost::Name for LastUpdatedResponse { const NAME: &'static str = "LastUpdatedResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.marketmap.v2"; + const PACKAGE: &'static str = "connect.marketmap.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.marketmap.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.marketmap.v2.{}", Self::NAME) } } /// Generated client implementations. @@ -375,16 +375,11 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.marketmap.v2.Query/MarketMap", + "/connect.marketmap.v2.Query/MarketMap", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.marketmap.v2.Query", - "MarketMap", - ), - ); + .insert(GrpcMethod::new("connect.marketmap.v2.Query", "MarketMap")); self.inner.unary(req, path, codec).await } /// Market returns a market stored in the x/marketmap @@ -404,16 +399,11 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.marketmap.v2.Query/Market", + "/connect.marketmap.v2.Query/Market", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.marketmap.v2.Query", - "Market", - ), - ); + .insert(GrpcMethod::new("connect.marketmap.v2.Query", "Market")); self.inner.unary(req, path, codec).await } /// LastUpdated returns the last height the market map was updated at. @@ -435,16 +425,11 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.marketmap.v2.Query/LastUpdated", + "/connect.marketmap.v2.Query/LastUpdated", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.marketmap.v2.Query", - "LastUpdated", - ), - ); + .insert(GrpcMethod::new("connect.marketmap.v2.Query", "LastUpdated")); self.inner.unary(req, path, codec).await } /// Params returns the current x/marketmap module parameters. @@ -463,16 +448,11 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.marketmap.v2.Query/Params", + "/connect.marketmap.v2.Query/Params", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.marketmap.v2.Query", - "Params", - ), - ); + .insert(GrpcMethod::new("connect.marketmap.v2.Query", "Params")); self.inner.unary(req, path, codec).await } } @@ -594,7 +574,7 @@ pub mod query_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { - "/astria_vendored.connect.marketmap.v2.Query/MarketMap" => { + "/connect.marketmap.v2.Query/MarketMap" => { #[allow(non_camel_case_types)] struct MarketMapSvc(pub Arc); impl tonic::server::UnaryService @@ -638,7 +618,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.connect.marketmap.v2.Query/Market" => { + "/connect.marketmap.v2.Query/Market" => { #[allow(non_camel_case_types)] struct MarketSvc(pub Arc); impl tonic::server::UnaryService @@ -682,7 +662,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.connect.marketmap.v2.Query/LastUpdated" => { + "/connect.marketmap.v2.Query/LastUpdated" => { #[allow(non_camel_case_types)] struct LastUpdatedSvc(pub Arc); impl tonic::server::UnaryService @@ -726,7 +706,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.connect.marketmap.v2.Query/Params" => { + "/connect.marketmap.v2.Query/Params" => { #[allow(non_camel_case_types)] struct ParamsSvc(pub Arc); impl tonic::server::UnaryService @@ -808,6 +788,6 @@ pub mod query_server { } } impl tonic::server::NamedService for QueryServer { - const NAME: &'static str = "astria_vendored.connect.marketmap.v2.Query"; + const NAME: &'static str = "connect.marketmap.v2.Query"; } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.serde.rs b/crates/astria-core/src/generated/connect.marketmap.v2.serde.rs similarity index 92% rename from crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.serde.rs rename to crates/astria-core/src/generated/connect.marketmap.v2.serde.rs index f3e7392f13..f73db9da9b 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.marketmap.v2.serde.rs +++ b/crates/astria-core/src/generated/connect.marketmap.v2.serde.rs @@ -15,7 +15,7 @@ impl serde::Serialize for GenesisState { if self.params.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.GenesisState", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.GenesisState", len)?; if let Some(v) = self.market_map.as_ref() { struct_ser.serialize_field("marketMap", v)?; } @@ -84,7 +84,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { type Value = GenesisState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.GenesisState") + formatter.write_str("struct connect.marketmap.v2.GenesisState") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -125,7 +125,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.GenesisState", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.GenesisState", FIELDS, GeneratedVisitor) } } impl serde::Serialize for LastUpdatedRequest { @@ -136,7 +136,7 @@ impl serde::Serialize for LastUpdatedRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.marketmap.v2.LastUpdatedRequest", len)?; struct_ser.end() } } @@ -182,7 +182,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedRequest { type Value = LastUpdatedRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.LastUpdatedRequest") + formatter.write_str("struct connect.marketmap.v2.LastUpdatedRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -196,7 +196,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.LastUpdatedRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for LastUpdatedResponse { @@ -210,7 +210,7 @@ impl serde::Serialize for LastUpdatedResponse { if self.last_updated != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.LastUpdatedResponse", len)?; if self.last_updated != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("lastUpdated", ToString::to_string(&self.last_updated).as_str())?; @@ -266,7 +266,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedResponse { type Value = LastUpdatedResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.LastUpdatedResponse") + formatter.write_str("struct connect.marketmap.v2.LastUpdatedResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -291,7 +291,7 @@ impl<'de> serde::Deserialize<'de> for LastUpdatedResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.LastUpdatedResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.LastUpdatedResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for Market { @@ -308,7 +308,7 @@ impl serde::Serialize for Market { if !self.provider_configs.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.Market", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.Market", len)?; if let Some(v) = self.ticker.as_ref() { struct_ser.serialize_field("ticker", v)?; } @@ -369,7 +369,7 @@ impl<'de> serde::Deserialize<'de> for Market { type Value = Market; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.Market") + formatter.write_str("struct connect.marketmap.v2.Market") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -400,7 +400,7 @@ impl<'de> serde::Deserialize<'de> for Market { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.Market", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.Market", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketMap { @@ -414,7 +414,7 @@ impl serde::Serialize for MarketMap { if !self.markets.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketMap", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.MarketMap", len)?; if !self.markets.is_empty() { struct_ser.serialize_field("markets", &self.markets)?; } @@ -468,7 +468,7 @@ impl<'de> serde::Deserialize<'de> for MarketMap { type Value = MarketMap; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketMap") + formatter.write_str("struct connect.marketmap.v2.MarketMap") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -493,7 +493,7 @@ impl<'de> serde::Deserialize<'de> for MarketMap { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketMap", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.MarketMap", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketMapRequest { @@ -504,7 +504,7 @@ impl serde::Serialize for MarketMapRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketMapRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.marketmap.v2.MarketMapRequest", len)?; struct_ser.end() } } @@ -550,7 +550,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapRequest { type Value = MarketMapRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketMapRequest") + formatter.write_str("struct connect.marketmap.v2.MarketMapRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -564,7 +564,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketMapRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.MarketMapRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketMapResponse { @@ -584,7 +584,7 @@ impl serde::Serialize for MarketMapResponse { if !self.chain_id.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketMapResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.MarketMapResponse", len)?; if let Some(v) = self.market_map.as_ref() { struct_ser.serialize_field("marketMap", v)?; } @@ -654,7 +654,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapResponse { type Value = MarketMapResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketMapResponse") + formatter.write_str("struct connect.marketmap.v2.MarketMapResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -695,7 +695,7 @@ impl<'de> serde::Deserialize<'de> for MarketMapResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketMapResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.MarketMapResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketRequest { @@ -709,7 +709,7 @@ impl serde::Serialize for MarketRequest { if self.currency_pair.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketRequest", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.MarketRequest", len)?; if let Some(v) = self.currency_pair.as_ref() { struct_ser.serialize_field("currencyPair", v)?; } @@ -764,7 +764,7 @@ impl<'de> serde::Deserialize<'de> for MarketRequest { type Value = MarketRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketRequest") + formatter.write_str("struct connect.marketmap.v2.MarketRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -787,7 +787,7 @@ impl<'de> serde::Deserialize<'de> for MarketRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.MarketRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MarketResponse { @@ -801,7 +801,7 @@ impl serde::Serialize for MarketResponse { if self.market.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.MarketResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.MarketResponse", len)?; if let Some(v) = self.market.as_ref() { struct_ser.serialize_field("market", v)?; } @@ -855,7 +855,7 @@ impl<'de> serde::Deserialize<'de> for MarketResponse { type Value = MarketResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.MarketResponse") + formatter.write_str("struct connect.marketmap.v2.MarketResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -878,7 +878,7 @@ impl<'de> serde::Deserialize<'de> for MarketResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.MarketResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.MarketResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for Params { @@ -895,7 +895,7 @@ impl serde::Serialize for Params { if !self.admin.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.Params", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.Params", len)?; if !self.market_authorities.is_empty() { struct_ser.serialize_field("marketAuthorities", &self.market_authorities)?; } @@ -956,7 +956,7 @@ impl<'de> serde::Deserialize<'de> for Params { type Value = Params; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.Params") + formatter.write_str("struct connect.marketmap.v2.Params") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -987,7 +987,7 @@ impl<'de> serde::Deserialize<'de> for Params { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.Params", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.Params", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ParamsRequest { @@ -998,7 +998,7 @@ impl serde::Serialize for ParamsRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.ParamsRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.marketmap.v2.ParamsRequest", len)?; struct_ser.end() } } @@ -1044,7 +1044,7 @@ impl<'de> serde::Deserialize<'de> for ParamsRequest { type Value = ParamsRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.ParamsRequest") + formatter.write_str("struct connect.marketmap.v2.ParamsRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1058,7 +1058,7 @@ impl<'de> serde::Deserialize<'de> for ParamsRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.ParamsRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.ParamsRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ParamsResponse { @@ -1072,7 +1072,7 @@ impl serde::Serialize for ParamsResponse { if self.params.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.ParamsResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.ParamsResponse", len)?; if let Some(v) = self.params.as_ref() { struct_ser.serialize_field("params", v)?; } @@ -1126,7 +1126,7 @@ impl<'de> serde::Deserialize<'de> for ParamsResponse { type Value = ParamsResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.ParamsResponse") + formatter.write_str("struct connect.marketmap.v2.ParamsResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1149,7 +1149,7 @@ impl<'de> serde::Deserialize<'de> for ParamsResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.ParamsResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.ParamsResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ProviderConfig { @@ -1175,7 +1175,7 @@ impl serde::Serialize for ProviderConfig { if !self.metadata_json.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.ProviderConfig", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.ProviderConfig", len)?; if !self.name.is_empty() { struct_ser.serialize_field("name", &self.name)?; } @@ -1256,7 +1256,7 @@ impl<'de> serde::Deserialize<'de> for ProviderConfig { type Value = ProviderConfig; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.ProviderConfig") + formatter.write_str("struct connect.marketmap.v2.ProviderConfig") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1311,7 +1311,7 @@ impl<'de> serde::Deserialize<'de> for ProviderConfig { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.ProviderConfig", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.ProviderConfig", FIELDS, GeneratedVisitor) } } impl serde::Serialize for Ticker { @@ -1337,7 +1337,7 @@ impl serde::Serialize for Ticker { if !self.metadata_json.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.marketmap.v2.Ticker", len)?; + let mut struct_ser = serializer.serialize_struct("connect.marketmap.v2.Ticker", len)?; if let Some(v) = self.currency_pair.as_ref() { struct_ser.serialize_field("currencyPair", v)?; } @@ -1420,7 +1420,7 @@ impl<'de> serde::Deserialize<'de> for Ticker { type Value = Ticker; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.marketmap.v2.Ticker") + formatter.write_str("struct connect.marketmap.v2.Ticker") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1479,6 +1479,6 @@ impl<'de> serde::Deserialize<'de> for Ticker { }) } } - deserializer.deserialize_struct("astria_vendored.connect.marketmap.v2.Ticker", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.marketmap.v2.Ticker", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.rs b/crates/astria-core/src/generated/connect.oracle.v2.rs similarity index 90% rename from crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.rs rename to crates/astria-core/src/generated/connect.oracle.v2.rs index a851fa6405..0815919721 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.rs +++ b/crates/astria-core/src/generated/connect.oracle.v2.rs @@ -16,9 +16,9 @@ pub struct QuotePrice { } impl ::prost::Name for QuotePrice { const NAME: &'static str = "QuotePrice"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// CurrencyPairState represents the stateful information tracked by the x/oracle @@ -39,9 +39,9 @@ pub struct CurrencyPairState { } impl ::prost::Name for CurrencyPairState { const NAME: &'static str = "CurrencyPairState"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// CurrencyPairGenesis is the information necessary for initialization of a @@ -66,9 +66,9 @@ pub struct CurrencyPairGenesis { } impl ::prost::Name for CurrencyPairGenesis { const NAME: &'static str = "CurrencyPairGenesis"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GenesisState is the genesis-state for the x/oracle module, it takes a set of @@ -87,9 +87,9 @@ pub struct GenesisState { } impl ::prost::Name for GenesisState { const NAME: &'static str = "GenesisState"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -97,9 +97,9 @@ impl ::prost::Name for GenesisState { pub struct GetAllCurrencyPairsRequest {} impl ::prost::Name for GetAllCurrencyPairsRequest { const NAME: &'static str = "GetAllCurrencyPairsRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GetAllCurrencyPairsResponse returns all CurrencyPairs that the module is @@ -112,9 +112,9 @@ pub struct GetAllCurrencyPairsResponse { } impl ::prost::Name for GetAllCurrencyPairsResponse { const NAME: &'static str = "GetAllCurrencyPairsResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GetPriceRequest takes an identifier for the @@ -128,9 +128,9 @@ pub struct GetPriceRequest { } impl ::prost::Name for GetPriceRequest { const NAME: &'static str = "GetPriceRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GetPriceResponse is the response from the GetPrice grpc method exposed from @@ -155,9 +155,9 @@ pub struct GetPriceResponse { } impl ::prost::Name for GetPriceResponse { const NAME: &'static str = "GetPriceResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GetPricesRequest takes an identifier for the CurrencyPair @@ -170,9 +170,9 @@ pub struct GetPricesRequest { } impl ::prost::Name for GetPricesRequest { const NAME: &'static str = "GetPricesRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GetPricesResponse is the response from the GetPrices grpc method exposed from @@ -185,9 +185,9 @@ pub struct GetPricesResponse { } impl ::prost::Name for GetPricesResponse { const NAME: &'static str = "GetPricesResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GetCurrencyPairMappingRequest is the GetCurrencyPairMapping request type. @@ -196,9 +196,9 @@ impl ::prost::Name for GetPricesResponse { pub struct GetCurrencyPairMappingRequest {} impl ::prost::Name for GetCurrencyPairMappingRequest { const NAME: &'static str = "GetCurrencyPairMappingRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// GetCurrencyPairMappingResponse is the GetCurrencyPairMapping response type. @@ -215,9 +215,9 @@ pub struct GetCurrencyPairMappingResponse { } impl ::prost::Name for GetCurrencyPairMappingResponse { const NAME: &'static str = "GetCurrencyPairMappingResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.oracle.v2"; + const PACKAGE: &'static str = "connect.oracle.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.oracle.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.oracle.v2.{}", Self::NAME) } } /// Generated client implementations. @@ -326,15 +326,12 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.oracle.v2.Query/GetAllCurrencyPairs", + "/connect.oracle.v2.Query/GetAllCurrencyPairs", ); let mut req = request.into_request(); req.extensions_mut() .insert( - GrpcMethod::new( - "astria_vendored.connect.oracle.v2.Query", - "GetAllCurrencyPairs", - ), + GrpcMethod::new("connect.oracle.v2.Query", "GetAllCurrencyPairs"), ); self.inner.unary(req, path, codec).await } @@ -358,16 +355,11 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.oracle.v2.Query/GetPrice", + "/connect.oracle.v2.Query/GetPrice", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.oracle.v2.Query", - "GetPrice", - ), - ); + .insert(GrpcMethod::new("connect.oracle.v2.Query", "GetPrice")); self.inner.unary(req, path, codec).await } pub async fn get_prices( @@ -388,16 +380,11 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.oracle.v2.Query/GetPrices", + "/connect.oracle.v2.Query/GetPrices", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.oracle.v2.Query", - "GetPrices", - ), - ); + .insert(GrpcMethod::new("connect.oracle.v2.Query", "GetPrices")); self.inner.unary(req, path, codec).await } /// Get the mapping of currency pair ID -> currency pair. This is useful for @@ -421,15 +408,12 @@ pub mod query_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.oracle.v2.Query/GetCurrencyPairMapping", + "/connect.oracle.v2.Query/GetCurrencyPairMapping", ); let mut req = request.into_request(); req.extensions_mut() .insert( - GrpcMethod::new( - "astria_vendored.connect.oracle.v2.Query", - "GetCurrencyPairMapping", - ), + GrpcMethod::new("connect.oracle.v2.Query", "GetCurrencyPairMapping"), ); self.inner.unary(req, path, codec).await } @@ -558,7 +542,7 @@ pub mod query_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { - "/astria_vendored.connect.oracle.v2.Query/GetAllCurrencyPairs" => { + "/connect.oracle.v2.Query/GetAllCurrencyPairs" => { #[allow(non_camel_case_types)] struct GetAllCurrencyPairsSvc(pub Arc); impl< @@ -604,7 +588,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.connect.oracle.v2.Query/GetPrice" => { + "/connect.oracle.v2.Query/GetPrice" => { #[allow(non_camel_case_types)] struct GetPriceSvc(pub Arc); impl tonic::server::UnaryService @@ -648,7 +632,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.connect.oracle.v2.Query/GetPrices" => { + "/connect.oracle.v2.Query/GetPrices" => { #[allow(non_camel_case_types)] struct GetPricesSvc(pub Arc); impl tonic::server::UnaryService @@ -692,7 +676,7 @@ pub mod query_server { }; Box::pin(fut) } - "/astria_vendored.connect.oracle.v2.Query/GetCurrencyPairMapping" => { + "/connect.oracle.v2.Query/GetCurrencyPairMapping" => { #[allow(non_camel_case_types)] struct GetCurrencyPairMappingSvc(pub Arc); impl< @@ -777,6 +761,6 @@ pub mod query_server { } } impl tonic::server::NamedService for QueryServer { - const NAME: &'static str = "astria_vendored.connect.oracle.v2.Query"; + const NAME: &'static str = "connect.oracle.v2.Query"; } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.serde.rs b/crates/astria-core/src/generated/connect.oracle.v2.serde.rs similarity index 91% rename from crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.serde.rs rename to crates/astria-core/src/generated/connect.oracle.v2.serde.rs index efb0030d28..5bb4c9fa6b 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.oracle.v2.serde.rs +++ b/crates/astria-core/src/generated/connect.oracle.v2.serde.rs @@ -18,7 +18,7 @@ impl serde::Serialize for CurrencyPairGenesis { if self.id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairGenesis", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.CurrencyPairGenesis", len)?; if let Some(v) = self.currency_pair.as_ref() { struct_ser.serialize_field("currencyPair", v)?; } @@ -94,7 +94,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairGenesis { type Value = CurrencyPairGenesis; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.CurrencyPairGenesis") + formatter.write_str("struct connect.oracle.v2.CurrencyPairGenesis") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -145,7 +145,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairGenesis { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairGenesis", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.CurrencyPairGenesis", FIELDS, GeneratedVisitor) } } impl serde::Serialize for CurrencyPairState { @@ -165,7 +165,7 @@ impl serde::Serialize for CurrencyPairState { if self.id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairState", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.CurrencyPairState", len)?; if let Some(v) = self.price.as_ref() { struct_ser.serialize_field("price", v)?; } @@ -233,7 +233,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairState { type Value = CurrencyPairState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.CurrencyPairState") + formatter.write_str("struct connect.oracle.v2.CurrencyPairState") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -276,7 +276,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPairState { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.CurrencyPairState", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.CurrencyPairState", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GenesisState { @@ -293,7 +293,7 @@ impl serde::Serialize for GenesisState { if self.next_id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GenesisState", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.GenesisState", len)?; if !self.currency_pair_genesis.is_empty() { struct_ser.serialize_field("currencyPairGenesis", &self.currency_pair_genesis)?; } @@ -356,7 +356,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { type Value = GenesisState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GenesisState") + formatter.write_str("struct connect.oracle.v2.GenesisState") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -389,7 +389,7 @@ impl<'de> serde::Deserialize<'de> for GenesisState { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GenesisState", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GenesisState", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetAllCurrencyPairsRequest { @@ -400,7 +400,7 @@ impl serde::Serialize for GetAllCurrencyPairsRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.oracle.v2.GetAllCurrencyPairsRequest", len)?; struct_ser.end() } } @@ -446,7 +446,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsRequest { type Value = GetAllCurrencyPairsRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetAllCurrencyPairsRequest") + formatter.write_str("struct connect.oracle.v2.GetAllCurrencyPairsRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -460,7 +460,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetAllCurrencyPairsRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetAllCurrencyPairsResponse { @@ -474,7 +474,7 @@ impl serde::Serialize for GetAllCurrencyPairsResponse { if !self.currency_pairs.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.GetAllCurrencyPairsResponse", len)?; if !self.currency_pairs.is_empty() { struct_ser.serialize_field("currencyPairs", &self.currency_pairs)?; } @@ -529,7 +529,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsResponse { type Value = GetAllCurrencyPairsResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetAllCurrencyPairsResponse") + formatter.write_str("struct connect.oracle.v2.GetAllCurrencyPairsResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -552,7 +552,7 @@ impl<'de> serde::Deserialize<'de> for GetAllCurrencyPairsResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetAllCurrencyPairsResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetAllCurrencyPairsResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetCurrencyPairMappingRequest { @@ -563,7 +563,7 @@ impl serde::Serialize for GetCurrencyPairMappingRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.oracle.v2.GetCurrencyPairMappingRequest", len)?; struct_ser.end() } } @@ -609,7 +609,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingRequest { type Value = GetCurrencyPairMappingRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetCurrencyPairMappingRequest") + formatter.write_str("struct connect.oracle.v2.GetCurrencyPairMappingRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -623,7 +623,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetCurrencyPairMappingRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetCurrencyPairMappingResponse { @@ -637,7 +637,7 @@ impl serde::Serialize for GetCurrencyPairMappingResponse { if !self.currency_pair_mapping.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.GetCurrencyPairMappingResponse", len)?; if !self.currency_pair_mapping.is_empty() { struct_ser.serialize_field("currencyPairMapping", &self.currency_pair_mapping)?; } @@ -692,7 +692,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingResponse { type Value = GetCurrencyPairMappingResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetCurrencyPairMappingResponse") + formatter.write_str("struct connect.oracle.v2.GetCurrencyPairMappingResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -718,7 +718,7 @@ impl<'de> serde::Deserialize<'de> for GetCurrencyPairMappingResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetCurrencyPairMappingResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetCurrencyPairMappingResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPriceRequest { @@ -732,7 +732,7 @@ impl serde::Serialize for GetPriceRequest { if !self.currency_pair.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPriceRequest", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.GetPriceRequest", len)?; if !self.currency_pair.is_empty() { struct_ser.serialize_field("currencyPair", &self.currency_pair)?; } @@ -787,7 +787,7 @@ impl<'de> serde::Deserialize<'de> for GetPriceRequest { type Value = GetPriceRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPriceRequest") + formatter.write_str("struct connect.oracle.v2.GetPriceRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -810,7 +810,7 @@ impl<'de> serde::Deserialize<'de> for GetPriceRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPriceRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetPriceRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPriceResponse { @@ -833,7 +833,7 @@ impl serde::Serialize for GetPriceResponse { if self.id != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPriceResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.GetPriceResponse", len)?; if let Some(v) = self.price.as_ref() { struct_ser.serialize_field("price", v)?; } @@ -908,7 +908,7 @@ impl<'de> serde::Deserialize<'de> for GetPriceResponse { type Value = GetPriceResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPriceResponse") + formatter.write_str("struct connect.oracle.v2.GetPriceResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -961,7 +961,7 @@ impl<'de> serde::Deserialize<'de> for GetPriceResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPriceResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetPriceResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPricesRequest { @@ -975,7 +975,7 @@ impl serde::Serialize for GetPricesRequest { if !self.currency_pair_ids.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPricesRequest", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.GetPricesRequest", len)?; if !self.currency_pair_ids.is_empty() { struct_ser.serialize_field("currencyPairIds", &self.currency_pair_ids)?; } @@ -1030,7 +1030,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesRequest { type Value = GetPricesRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPricesRequest") + formatter.write_str("struct connect.oracle.v2.GetPricesRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1053,7 +1053,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPricesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetPricesRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GetPricesResponse { @@ -1067,7 +1067,7 @@ impl serde::Serialize for GetPricesResponse { if !self.prices.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.GetPricesResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.GetPricesResponse", len)?; if !self.prices.is_empty() { struct_ser.serialize_field("prices", &self.prices)?; } @@ -1121,7 +1121,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesResponse { type Value = GetPricesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.GetPricesResponse") + formatter.write_str("struct connect.oracle.v2.GetPricesResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1144,7 +1144,7 @@ impl<'de> serde::Deserialize<'de> for GetPricesResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.GetPricesResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.GetPricesResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QuotePrice { @@ -1164,7 +1164,7 @@ impl serde::Serialize for QuotePrice { if self.block_height != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.oracle.v2.QuotePrice", len)?; + let mut struct_ser = serializer.serialize_struct("connect.oracle.v2.QuotePrice", len)?; if !self.price.is_empty() { struct_ser.serialize_field("price", &self.price)?; } @@ -1233,7 +1233,7 @@ impl<'de> serde::Deserialize<'de> for QuotePrice { type Value = QuotePrice; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.oracle.v2.QuotePrice") + formatter.write_str("struct connect.oracle.v2.QuotePrice") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -1274,6 +1274,6 @@ impl<'de> serde::Deserialize<'de> for QuotePrice { }) } } - deserializer.deserialize_struct("astria_vendored.connect.oracle.v2.QuotePrice", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.oracle.v2.QuotePrice", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.service.v2.rs b/crates/astria-core/src/generated/connect.service.v2.rs similarity index 91% rename from crates/astria-core/src/generated/astria_vendored.connect.service.v2.rs rename to crates/astria-core/src/generated/connect.service.v2.rs index ab3e31e083..fadf917481 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.service.v2.rs +++ b/crates/astria-core/src/generated/connect.service.v2.rs @@ -4,9 +4,9 @@ pub struct QueryPricesRequest {} impl ::prost::Name for QueryPricesRequest { const NAME: &'static str = "QueryPricesRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + const PACKAGE: &'static str = "connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.service.v2.{}", Self::NAME) } } /// QueryPricesResponse defines the response type for the Prices method. @@ -28,9 +28,9 @@ pub struct QueryPricesResponse { } impl ::prost::Name for QueryPricesResponse { const NAME: &'static str = "QueryPricesResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + const PACKAGE: &'static str = "connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.service.v2.{}", Self::NAME) } } /// QueryMarketMapRequest defines the request type for the MarketMap method. @@ -39,9 +39,9 @@ impl ::prost::Name for QueryPricesResponse { pub struct QueryMarketMapRequest {} impl ::prost::Name for QueryMarketMapRequest { const NAME: &'static str = "QueryMarketMapRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + const PACKAGE: &'static str = "connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.service.v2.{}", Self::NAME) } } /// QueryMarketMapResponse defines the response type for the MarketMap method. @@ -54,9 +54,9 @@ pub struct QueryMarketMapResponse { } impl ::prost::Name for QueryMarketMapResponse { const NAME: &'static str = "QueryMarketMapResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + const PACKAGE: &'static str = "connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.service.v2.{}", Self::NAME) } } /// QueryVersionRequest defines the request type for the Version method. @@ -65,9 +65,9 @@ impl ::prost::Name for QueryMarketMapResponse { pub struct QueryVersionRequest {} impl ::prost::Name for QueryVersionRequest { const NAME: &'static str = "QueryVersionRequest"; - const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + const PACKAGE: &'static str = "connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.service.v2.{}", Self::NAME) } } /// QueryVersionResponse defines the response type for the Version method. @@ -80,9 +80,9 @@ pub struct QueryVersionResponse { } impl ::prost::Name for QueryVersionResponse { const NAME: &'static str = "QueryVersionResponse"; - const PACKAGE: &'static str = "astria_vendored.connect.service.v2"; + const PACKAGE: &'static str = "connect.service.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.service.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.service.v2.{}", Self::NAME) } } /// Generated client implementations. @@ -191,16 +191,11 @@ pub mod oracle_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.service.v2.Oracle/Prices", + "/connect.service.v2.Oracle/Prices", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.service.v2.Oracle", - "Prices", - ), - ); + .insert(GrpcMethod::new("connect.service.v2.Oracle", "Prices")); self.inner.unary(req, path, codec).await } /// MarketMap defines a method for fetching the latest market map @@ -223,16 +218,11 @@ pub mod oracle_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.service.v2.Oracle/MarketMap", + "/connect.service.v2.Oracle/MarketMap", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.service.v2.Oracle", - "MarketMap", - ), - ); + .insert(GrpcMethod::new("connect.service.v2.Oracle", "MarketMap")); self.inner.unary(req, path, codec).await } /// Version defines a method for fetching the current version of the oracle @@ -255,16 +245,11 @@ pub mod oracle_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/astria_vendored.connect.service.v2.Oracle/Version", + "/connect.service.v2.Oracle/Version", ); let mut req = request.into_request(); req.extensions_mut() - .insert( - GrpcMethod::new( - "astria_vendored.connect.service.v2.Oracle", - "Version", - ), - ); + .insert(GrpcMethod::new("connect.service.v2.Oracle", "Version")); self.inner.unary(req, path, codec).await } } @@ -384,7 +369,7 @@ pub mod oracle_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { - "/astria_vendored.connect.service.v2.Oracle/Prices" => { + "/connect.service.v2.Oracle/Prices" => { #[allow(non_camel_case_types)] struct PricesSvc(pub Arc); impl< @@ -430,7 +415,7 @@ pub mod oracle_server { }; Box::pin(fut) } - "/astria_vendored.connect.service.v2.Oracle/MarketMap" => { + "/connect.service.v2.Oracle/MarketMap" => { #[allow(non_camel_case_types)] struct MarketMapSvc(pub Arc); impl< @@ -476,7 +461,7 @@ pub mod oracle_server { }; Box::pin(fut) } - "/astria_vendored.connect.service.v2.Oracle/Version" => { + "/connect.service.v2.Oracle/Version" => { #[allow(non_camel_case_types)] struct VersionSvc(pub Arc); impl< @@ -560,6 +545,6 @@ pub mod oracle_server { } } impl tonic::server::NamedService for OracleServer { - const NAME: &'static str = "astria_vendored.connect.service.v2.Oracle"; + const NAME: &'static str = "connect.service.v2.Oracle"; } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.service.v2.serde.rs b/crates/astria-core/src/generated/connect.service.v2.serde.rs similarity index 89% rename from crates/astria-core/src/generated/astria_vendored.connect.service.v2.serde.rs rename to crates/astria-core/src/generated/connect.service.v2.serde.rs index 2760b1501e..a67adb978d 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.service.v2.serde.rs +++ b/crates/astria-core/src/generated/connect.service.v2.serde.rs @@ -6,7 +6,7 @@ impl serde::Serialize for QueryMarketMapRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryMarketMapRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.service.v2.QueryMarketMapRequest", len)?; struct_ser.end() } } @@ -52,7 +52,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapRequest { type Value = QueryMarketMapRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.service.v2.QueryMarketMapRequest") + formatter.write_str("struct connect.service.v2.QueryMarketMapRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -66,7 +66,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryMarketMapRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.service.v2.QueryMarketMapRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryMarketMapResponse { @@ -80,7 +80,7 @@ impl serde::Serialize for QueryMarketMapResponse { if self.market_map.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryMarketMapResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.service.v2.QueryMarketMapResponse", len)?; if let Some(v) = self.market_map.as_ref() { struct_ser.serialize_field("marketMap", v)?; } @@ -135,7 +135,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapResponse { type Value = QueryMarketMapResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.service.v2.QueryMarketMapResponse") + formatter.write_str("struct connect.service.v2.QueryMarketMapResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -158,7 +158,7 @@ impl<'de> serde::Deserialize<'de> for QueryMarketMapResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryMarketMapResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.service.v2.QueryMarketMapResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryPricesRequest { @@ -169,7 +169,7 @@ impl serde::Serialize for QueryPricesRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryPricesRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.service.v2.QueryPricesRequest", len)?; struct_ser.end() } } @@ -215,7 +215,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesRequest { type Value = QueryPricesRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.service.v2.QueryPricesRequest") + formatter.write_str("struct connect.service.v2.QueryPricesRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -229,7 +229,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryPricesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.service.v2.QueryPricesRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryPricesResponse { @@ -249,7 +249,7 @@ impl serde::Serialize for QueryPricesResponse { if !self.version.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryPricesResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.service.v2.QueryPricesResponse", len)?; if !self.prices.is_empty() { struct_ser.serialize_field("prices", &self.prices)?; } @@ -315,7 +315,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { type Value = QueryPricesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.service.v2.QueryPricesResponse") + formatter.write_str("struct connect.service.v2.QueryPricesResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -356,7 +356,7 @@ impl<'de> serde::Deserialize<'de> for QueryPricesResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryPricesResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.service.v2.QueryPricesResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryVersionRequest { @@ -367,7 +367,7 @@ impl serde::Serialize for QueryVersionRequest { { use serde::ser::SerializeStruct; let len = 0; - let struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryVersionRequest", len)?; + let struct_ser = serializer.serialize_struct("connect.service.v2.QueryVersionRequest", len)?; struct_ser.end() } } @@ -413,7 +413,7 @@ impl<'de> serde::Deserialize<'de> for QueryVersionRequest { type Value = QueryVersionRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.service.v2.QueryVersionRequest") + formatter.write_str("struct connect.service.v2.QueryVersionRequest") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -427,7 +427,7 @@ impl<'de> serde::Deserialize<'de> for QueryVersionRequest { }) } } - deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryVersionRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.service.v2.QueryVersionRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for QueryVersionResponse { @@ -441,7 +441,7 @@ impl serde::Serialize for QueryVersionResponse { if !self.version.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.service.v2.QueryVersionResponse", len)?; + let mut struct_ser = serializer.serialize_struct("connect.service.v2.QueryVersionResponse", len)?; if !self.version.is_empty() { struct_ser.serialize_field("version", &self.version)?; } @@ -495,7 +495,7 @@ impl<'de> serde::Deserialize<'de> for QueryVersionResponse { type Value = QueryVersionResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.service.v2.QueryVersionResponse") + formatter.write_str("struct connect.service.v2.QueryVersionResponse") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -518,6 +518,6 @@ impl<'de> serde::Deserialize<'de> for QueryVersionResponse { }) } } - deserializer.deserialize_struct("astria_vendored.connect.service.v2.QueryVersionResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.service.v2.QueryVersionResponse", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.types.v2.rs b/crates/astria-core/src/generated/connect.types.v2.rs similarity index 78% rename from crates/astria-core/src/generated/astria_vendored.connect.types.v2.rs rename to crates/astria-core/src/generated/connect.types.v2.rs index ee4429a051..4ee9cb5851 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.types.v2.rs +++ b/crates/astria-core/src/generated/connect.types.v2.rs @@ -10,8 +10,8 @@ pub struct CurrencyPair { } impl ::prost::Name for CurrencyPair { const NAME: &'static str = "CurrencyPair"; - const PACKAGE: &'static str = "astria_vendored.connect.types.v2"; + const PACKAGE: &'static str = "connect.types.v2"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria_vendored.connect.types.v2.{}", Self::NAME) + ::prost::alloc::format!("connect.types.v2.{}", Self::NAME) } } diff --git a/crates/astria-core/src/generated/astria_vendored.connect.types.v2.serde.rs b/crates/astria-core/src/generated/connect.types.v2.serde.rs similarity index 92% rename from crates/astria-core/src/generated/astria_vendored.connect.types.v2.serde.rs rename to crates/astria-core/src/generated/connect.types.v2.serde.rs index 2ea87a7889..695fdcca9e 100644 --- a/crates/astria-core/src/generated/astria_vendored.connect.types.v2.serde.rs +++ b/crates/astria-core/src/generated/connect.types.v2.serde.rs @@ -12,7 +12,7 @@ impl serde::Serialize for CurrencyPair { if !self.quote.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("astria_vendored.connect.types.v2.CurrencyPair", len)?; + let mut struct_ser = serializer.serialize_struct("connect.types.v2.CurrencyPair", len)?; if !self.base.is_empty() { struct_ser.serialize_field("Base", &self.base)?; } @@ -72,7 +72,7 @@ impl<'de> serde::Deserialize<'de> for CurrencyPair { type Value = CurrencyPair; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria_vendored.connect.types.v2.CurrencyPair") + formatter.write_str("struct connect.types.v2.CurrencyPair") } fn visit_map(self, mut map_: V) -> std::result::Result @@ -103,6 +103,6 @@ impl<'de> serde::Deserialize<'de> for CurrencyPair { }) } } - deserializer.deserialize_struct("astria_vendored.connect.types.v2.CurrencyPair", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("connect.types.v2.CurrencyPair", FIELDS, GeneratedVisitor) } } diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index 7adccab543..82f6420c7b 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -37,69 +37,6 @@ pub mod astria_vendored { } } } - - #[path = ""] - pub mod connect { - pub mod abci { - pub mod v2 { - include!("astria_vendored.connect.abci.v2.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria_vendored.connect.abci.v2.serde.rs"); - } - } - } - - pub mod marketmap { - pub mod v2 { - include!("astria_vendored.connect.marketmap.v2.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria_vendored.connect.marketmap.v2.serde.rs"); - } - } - } - - pub mod oracle { - pub mod v2 { - include!("astria_vendored.connect.oracle.v2.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria_vendored.connect.oracle.v2.serde.rs"); - } - } - } - - pub mod service { - pub mod v2 { - include!("astria_vendored.connect.service.v2.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria_vendored.connect.service.v2.serde.rs"); - } - } - } - - pub mod types { - pub mod v2 { - include!("astria_vendored.connect.types.v2.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria_vendored.connect.types.v2.serde.rs"); - } - } - } - } } #[path = ""] @@ -245,6 +182,69 @@ pub mod celestia { } } +#[path = ""] +pub mod connect { + pub mod abci { + pub mod v2 { + include!("connect.abci.v2.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("connect.abci.v2.serde.rs"); + } + } + } + + pub mod marketmap { + pub mod v2 { + include!("connect.marketmap.v2.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("connect.marketmap.v2.serde.rs"); + } + } + } + + pub mod oracle { + pub mod v2 { + include!("connect.oracle.v2.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("connect.oracle.v2.serde.rs"); + } + } + } + + pub mod service { + pub mod v2 { + include!("connect.service.v2.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("connect.service.v2.serde.rs"); + } + } + } + + pub mod types { + pub mod v2 { + include!("connect.types.v2.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("connect.types.v2.serde.rs"); + } + } + } +} + #[path = ""] pub mod cosmos { pub mod auth { diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index 3bd402f93e..0769de29e7 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -6,7 +6,7 @@ use std::{ use astria_core::{ generated::{ - astria_vendored::connect::{ + connect::{ marketmap, marketmap::v2::{ Market, @@ -82,7 +82,7 @@ fn charlie() -> Address { } fn genesis_state_markets() -> MarketMap { - use astria_core::generated::astria_vendored::connect::marketmap::v2::{ + use astria_core::generated::connect::marketmap::v2::{ ProviderConfig, Ticker, }; @@ -184,7 +184,7 @@ fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::Genes connect: Some( astria_core::generated::protocol::genesis::v1::ConnectGenesis { market_map: Some( - astria_core::generated::astria_vendored::connect::marketmap::v2::GenesisState { + astria_core::generated::connect::marketmap::v2::GenesisState { market_map: Some(genesis_state_markets()), last_updated: 0, params: Some(marketmap::v2::Params { diff --git a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs index f86903f052..27d153e211 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -172,12 +172,10 @@ pub(crate) fn proto_genesis_state() -> astria_core::generated::protocol::genesis } .into_raw(), ), - oracle: Some( - astria_core::generated::astria_vendored::connect::oracle::v2::GenesisState { - currency_pair_genesis: vec![], - next_id: 0, - }, - ), + oracle: Some(astria_core::generated::connect::oracle::v2::GenesisState { + currency_pair_genesis: vec![], + next_id: 0, + }), }), } } diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index d21e3b1765..6d8470036e 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -11,7 +11,7 @@ use astria_core::{ }, }, crypto::Signature, - generated::astria_vendored::connect::{ + generated::connect::{ abci::v2::OracleVoteExtension as RawOracleVoteExtension, service::v2::{ oracle_client::OracleClient, diff --git a/crates/astria-sequencer/src/grpc/connect.rs b/crates/astria-sequencer/src/grpc/connect.rs index 2658e6275d..bb1b372f96 100644 --- a/crates/astria-sequencer/src/grpc/connect.rs +++ b/crates/astria-sequencer/src/grpc/connect.rs @@ -5,7 +5,7 @@ use std::{ use astria_core::{ connect::types::v2::CurrencyPair, - generated::astria_vendored::connect::{ + generated::connect::{ marketmap::v2::{ query_server::Query as MarketMapQueryService, LastUpdatedRequest, diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index f8bfd18f83..59e479309a 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -1,5 +1,5 @@ use astria_core::generated::{ - astria_vendored::connect::{ + connect::{ marketmap::v2::query_server::QueryServer as MarketMapQueryServer, oracle::v2::query_server::QueryServer as OracleQueryServer, service::v2::{ @@ -136,12 +136,8 @@ impl Sequencer { // ensure the oracle sidecar is reachable // TODO: allow this to retry in case the oracle sidecar is not ready yet - if oracle_client - .prices(QueryPricesRequest::default()) - .await - .is_err() - { - warn!(uri = %uri, "oracle sidecar is unreachable"); + if let Err(e) = oracle_client.prices(QueryPricesRequest::default()).await { + warn!(uri = %uri, error = %e, "oracle sidecar is unreachable"); } else { debug!(uri = %uri, "oracle sidecar is reachable"); }; diff --git a/proto/protocolapis/astria/protocol/genesis/v1/types.proto b/proto/protocolapis/astria/protocol/genesis/v1/types.proto index e1627380fb..2fb05eb0ea 100644 --- a/proto/protocolapis/astria/protocol/genesis/v1/types.proto +++ b/proto/protocolapis/astria/protocol/genesis/v1/types.proto @@ -4,8 +4,8 @@ package astria.protocol.genesis.v1; import "astria/primitive/v1/types.proto"; import "astria/protocol/fees/v1/types.proto"; -import "astria_vendored/connect/marketmap/v2/genesis.proto"; -import "astria_vendored/connect/oracle/v2/genesis.proto"; +import "connect/marketmap/v2/genesis.proto"; +import "connect/oracle/v2/genesis.proto"; message GenesisAppState { string chain_id = 1; @@ -62,6 +62,6 @@ message GenesisFees { } message ConnectGenesis { - astria_vendored.connect.marketmap.v2.GenesisState market_map = 1; - astria_vendored.connect.oracle.v2.GenesisState oracle = 2; + connect.marketmap.v2.GenesisState market_map = 1; + connect.oracle.v2.GenesisState oracle = 2; } diff --git a/proto/protocolapis/astria_vendored/connect/abci/v2/vote_extensions.proto b/proto/vendored/connect/abci/v2/vote_extensions.proto similarity index 91% rename from proto/protocolapis/astria_vendored/connect/abci/v2/vote_extensions.proto rename to proto/vendored/connect/abci/v2/vote_extensions.proto index be8c398957..5fcdcf7f2b 100644 --- a/proto/protocolapis/astria_vendored/connect/abci/v2/vote_extensions.proto +++ b/proto/vendored/connect/abci/v2/vote_extensions.proto @@ -1,5 +1,5 @@ syntax = "proto3"; -package astria_vendored.connect.abci.v2; +package connect.abci.v2; option go_package = "github.com/skip-mev/connect/v2/abci/ve/types"; diff --git a/proto/protocolapis/astria_vendored/connect/marketmap/v2/genesis.proto b/proto/vendored/connect/marketmap/v2/genesis.proto similarity index 78% rename from proto/protocolapis/astria_vendored/connect/marketmap/v2/genesis.proto rename to proto/vendored/connect/marketmap/v2/genesis.proto index f3600c295b..3ce6bff2a0 100644 --- a/proto/protocolapis/astria_vendored/connect/marketmap/v2/genesis.proto +++ b/proto/vendored/connect/marketmap/v2/genesis.proto @@ -1,8 +1,8 @@ syntax = "proto3"; -package astria_vendored.connect.marketmap.v2; +package connect.marketmap.v2; -import "astria_vendored/connect/marketmap/v2/market.proto"; -import "astria_vendored/connect/marketmap/v2/params.proto"; +import "connect/marketmap/v2/market.proto"; +import "connect/marketmap/v2/params.proto"; option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; diff --git a/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto b/proto/vendored/connect/marketmap/v2/market.proto similarity index 91% rename from proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto rename to proto/vendored/connect/marketmap/v2/market.proto index d7ff2cf2d9..0ceda1e520 100644 --- a/proto/protocolapis/astria_vendored/connect/marketmap/v2/market.proto +++ b/proto/vendored/connect/marketmap/v2/market.proto @@ -1,7 +1,7 @@ syntax = "proto3"; -package astria_vendored.connect.marketmap.v2; +package connect.marketmap.v2; -import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "connect/types/v2/currency_pair.proto"; option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; @@ -21,7 +21,7 @@ message Market { // providers required to consider the ticker valid. message Ticker { // CurrencyPair is the currency pair for this ticker. - astria_vendored.connect.types.v2.CurrencyPair currency_pair = 1; + connect.types.v2.CurrencyPair currency_pair = 1; // Decimals is the number of decimal places for the ticker. The number of // decimal places is used to convert the price to a human-readable format. @@ -54,7 +54,7 @@ message ProviderConfig { // For example, if the desired Ticker is BTC/USD, this market could be reached // using: OffChainTicker = BTC/USDT NormalizeByPair = USDT/USD This field is // optional and nullable. - astria_vendored.connect.types.v2.CurrencyPair normalize_by_pair = 3; + connect.types.v2.CurrencyPair normalize_by_pair = 3; // Invert is a boolean indicating if the BASE and QUOTE of the market should // be inverted. i.e. BASE -> QUOTE, QUOTE -> BASE diff --git a/proto/protocolapis/astria_vendored/connect/marketmap/v2/params.proto b/proto/vendored/connect/marketmap/v2/params.proto similarity index 91% rename from proto/protocolapis/astria_vendored/connect/marketmap/v2/params.proto rename to proto/vendored/connect/marketmap/v2/params.proto index db0678ab07..b880804f07 100644 --- a/proto/protocolapis/astria_vendored/connect/marketmap/v2/params.proto +++ b/proto/vendored/connect/marketmap/v2/params.proto @@ -1,5 +1,5 @@ syntax = "proto3"; -package astria_vendored.connect.marketmap.v2; +package connect.marketmap.v2; option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; diff --git a/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto b/proto/vendored/connect/marketmap/v2/query.proto similarity index 89% rename from proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto rename to proto/vendored/connect/marketmap/v2/query.proto index 55f18c6295..765dbf29d0 100644 --- a/proto/protocolapis/astria_vendored/connect/marketmap/v2/query.proto +++ b/proto/vendored/connect/marketmap/v2/query.proto @@ -1,9 +1,9 @@ syntax = "proto3"; -package astria_vendored.connect.marketmap.v2; +package connect.marketmap.v2; -import "astria_vendored/connect/marketmap/v2/market.proto"; -import "astria_vendored/connect/marketmap/v2/params.proto"; -import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "connect/marketmap/v2/market.proto"; +import "connect/marketmap/v2/params.proto"; +import "connect/types/v2/currency_pair.proto"; import "google/api/annotations.proto"; option go_package = "github.com/skip-mev/connect/v2/x/marketmap/types"; @@ -57,7 +57,7 @@ message MarketMapResponse { message MarketRequest { // CurrencyPair is the currency pair associated with the market being // requested. - astria_vendored.connect.types.v2.CurrencyPair currency_pair = 1; + connect.types.v2.CurrencyPair currency_pair = 1; } // MarketResponse is the query response for the Market query. diff --git a/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto b/proto/vendored/connect/oracle/v2/genesis.proto similarity index 92% rename from proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto rename to proto/vendored/connect/oracle/v2/genesis.proto index d779fc1ded..fa371fa6e1 100644 --- a/proto/protocolapis/astria_vendored/connect/oracle/v2/genesis.proto +++ b/proto/vendored/connect/oracle/v2/genesis.proto @@ -1,7 +1,7 @@ syntax = "proto3"; -package astria_vendored.connect.oracle.v2; +package connect.oracle.v2; -import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "connect/types/v2/currency_pair.proto"; import "google/protobuf/timestamp.proto"; option go_package = "github.com/skip-mev/connect/v2/x/oracle/types"; @@ -38,7 +38,7 @@ message CurrencyPairState { // CurrencyPair. message CurrencyPairGenesis { // The CurrencyPair to be added to module state - astria_vendored.connect.types.v2.CurrencyPair currency_pair = 1; + connect.types.v2.CurrencyPair currency_pair = 1; // A genesis price if one exists (note this will be empty, unless it results // from forking the state of this module) QuotePrice currency_pair_price = 2; diff --git a/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto b/proto/vendored/connect/oracle/v2/query.proto similarity index 90% rename from proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto rename to proto/vendored/connect/oracle/v2/query.proto index 859250462d..fbdd3e0e55 100644 --- a/proto/protocolapis/astria_vendored/connect/oracle/v2/query.proto +++ b/proto/vendored/connect/oracle/v2/query.proto @@ -1,8 +1,8 @@ syntax = "proto3"; -package astria_vendored.connect.oracle.v2; +package connect.oracle.v2; -import "astria_vendored/connect/oracle/v2/genesis.proto"; -import "astria_vendored/connect/types/v2/currency_pair.proto"; +import "connect/oracle/v2/genesis.proto"; +import "connect/types/v2/currency_pair.proto"; import "google/api/annotations.proto"; option go_package = "github.com/skip-mev/connect/v2/x/oracle/types"; @@ -40,7 +40,7 @@ message GetAllCurrencyPairsRequest {} // GetAllCurrencyPairsResponse returns all CurrencyPairs that the module is // currently tracking. message GetAllCurrencyPairsResponse { - repeated astria_vendored.connect.types.v2.CurrencyPair currency_pairs = 1; + repeated connect.types.v2.CurrencyPair currency_pairs = 1; } // GetPriceRequest takes an identifier for the @@ -84,5 +84,5 @@ message GetCurrencyPairMappingRequest {} message GetCurrencyPairMappingResponse { // currency_pair_mapping is a mapping of the id representing the currency pair // to the currency pair itself. - map currency_pair_mapping = 1; + map currency_pair_mapping = 1; } diff --git a/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto b/proto/vendored/connect/service/v2/oracle.proto similarity index 92% rename from proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto rename to proto/vendored/connect/service/v2/oracle.proto index 67f5c09b80..1beb98f429 100644 --- a/proto/protocolapis/astria_vendored/connect/service/v2/oracle.proto +++ b/proto/vendored/connect/service/v2/oracle.proto @@ -1,7 +1,7 @@ syntax = "proto3"; -package astria_vendored.connect.service.v2; +package connect.service.v2; -import "astria_vendored/connect/marketmap/v2/market.proto"; +import "connect/marketmap/v2/market.proto"; import "google/api/annotations.proto"; import "google/protobuf/timestamp.proto"; @@ -48,7 +48,7 @@ message QueryMarketMapRequest {} // QueryMarketMapResponse defines the response type for the MarketMap method. message QueryMarketMapResponse { // MarketMap defines the current market map configuration. - astria_vendored.connect.marketmap.v2.MarketMap market_map = 1; + connect.marketmap.v2.MarketMap market_map = 1; } // QueryVersionRequest defines the request type for the Version method. diff --git a/proto/protocolapis/astria_vendored/connect/types/v2/currency_pair.proto b/proto/vendored/connect/types/v2/currency_pair.proto similarity index 86% rename from proto/protocolapis/astria_vendored/connect/types/v2/currency_pair.proto rename to proto/vendored/connect/types/v2/currency_pair.proto index 9204c2efb9..67c2e86d12 100644 --- a/proto/protocolapis/astria_vendored/connect/types/v2/currency_pair.proto +++ b/proto/vendored/connect/types/v2/currency_pair.proto @@ -1,5 +1,5 @@ syntax = "proto3"; -package astria_vendored.connect.types.v2; +package connect.types.v2; option go_package = "github.com/skip-mev/connect/v2/pkg/types"; diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index 4c3f812547..8d4716189a 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -63,11 +63,12 @@ fn main() { .build_client(true) .build_server(true) .emit_rerun_if_changed(false) - .btree_map([".astria_vendored.connect"]) + .btree_map([".connect"]) .bytes([ ".astria", - ".astria_vendored.connect", + ".connect", ".celestia", + ".connect", ".cosmos", ".tendermint", ]) @@ -95,12 +96,13 @@ fn main() { pbjson_build::Builder::new() .register_descriptors(&descriptor_set) .unwrap() - .btree_map([".astria_vendored.connect"]) + .btree_map([".connect"]) .out_dir(&out_dir) .build(&[ ".astria", ".astria_vendored", ".celestia", + ".connect", ".cosmos", ".tendermint", ]) @@ -140,6 +142,7 @@ fn clean_non_astria_code(generated: &mut ContentMap) { !name.starts_with("astria.") && !name.starts_with("astria_vendored.") && !name.starts_with("celestia.") + && !name.starts_with("connect.") && !name.starts_with("cosmos.") && !name.starts_with("tendermint.") }) From 33d8dd95e5773262354ec0501bccc10632b38892 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 30 Oct 2024 16:27:35 -0400 Subject: [PATCH 79/89] revert buf.lock --- buf.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buf.lock b/buf.lock index 077deea8e6..c6571c8f90 100644 --- a/buf.lock +++ b/buf.lock @@ -2,5 +2,5 @@ version: v2 deps: - name: buf.build/googleapis/googleapis - commit: f52d4f76a8434cc5966798b1d3b4f110 - digest: b5:5e634ff0ee118aea188b3e9c13ad7d285a192ef6c591bc20ff5a3360438d6ca310cfe9d663b20d60e1daa21789add35b919eac84e8e94a4d576e83a50dd2d62c + commit: 4ed3bc159a8b4ac68fe253218760d035 + digest: b5:74a7798987b123218c004cf28543a2835e432ca04a69de99cd394a29dbad24d9ed38344f0b7c97ad6476039506c4eb38c2f4a8eef9cec3da2e38e4216a22d495 From e5bc4cee932ebbc5ccff8303cf49ea58feda2748 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 30 Oct 2024 16:29:52 -0400 Subject: [PATCH 80/89] buf.yaml cleanup --- buf.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/buf.yaml b/buf.yaml index 5c62be926b..d31ad1e385 100644 --- a/buf.yaml +++ b/buf.yaml @@ -62,12 +62,6 @@ modules: ignore_only: PACKAGE_VERSION_SUFFIX: - proto/protocolapis/astria_vendored/tendermint - FIELD_LOWER_SNAKE_CASE: - - proto/protocolapis/astria_vendored/connect - SERVICE_SUFFIX: - - proto/protocolapis/astria_vendored/connect - RPC_REQUEST_STANDARD_NAME: - - proto/protocolapis/astria_vendored/connect disallow_comment_ignores: true breaking: use: From fd69353b234b4d04291fe6ddc835004159ee61c8 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Wed, 30 Oct 2024 19:41:47 -0400 Subject: [PATCH 81/89] make connect optional in genesis --- crates/astria-core/src/protocol/genesis/v1.rs | 86 ++++++------------- .../src/connect/marketmap/component.rs | 21 +++-- .../src/connect/oracle/component.rs | 41 +++++---- 3 files changed, 63 insertions(+), 85 deletions(-) diff --git a/crates/astria-core/src/protocol/genesis/v1.rs b/crates/astria-core/src/protocol/genesis/v1.rs index 6e4ae301f2..7d1fd017b7 100644 --- a/crates/astria-core/src/protocol/genesis/v1.rs +++ b/crates/astria-core/src/protocol/genesis/v1.rs @@ -177,7 +177,7 @@ pub struct GenesisAppState { ibc_parameters: IBCParameters, allowed_fee_assets: Vec, fees: GenesisFees, - connect: ConnectGenesis, + connect: Option, } impl GenesisAppState { @@ -232,7 +232,7 @@ impl GenesisAppState { } #[must_use] - pub fn connect(&self) -> &ConnectGenesis { + pub fn connect(&self) -> &Option { &self.connect } @@ -267,23 +267,24 @@ impl GenesisAppState { self.ensure_address_has_base_prefix(address, &format!(".ibc_relayer_addresses[{i}]"))?; } - for (i, address) in self - .connect - .market_map - .params - .market_authorities - .iter() - .enumerate() - { + if let Some(connect) = &self.connect { + for (i, address) in connect + .market_map + .params + .market_authorities + .iter() + .enumerate() + { + self.ensure_address_has_base_prefix( + address, + &format!(".market_map.params.market_authorities[{i}]"), + )?; + } self.ensure_address_has_base_prefix( - address, - &format!(".market_map.params.market_authorities[{i}]"), + &connect.market_map.params.admin, + ".market_map.params.admin", )?; } - self.ensure_address_has_base_prefix( - &self.connect.market_map.params.admin, - ".market_map.params.admin", - )?; Ok(()) } @@ -297,7 +298,6 @@ impl Protobuf for GenesisAppState { #[expect( clippy::allow_attributes, clippy::allow_attributes_without_reason, - clippy::too_many_lines, reason = "false positive on `allowed_fee_assets` due to \"allow\" in the name" )] fn try_from_raw_ref(raw: &Self::Raw) -> Result { @@ -372,26 +372,11 @@ impl Protobuf for GenesisAppState { .ok_or_else(|| Self::Error::field_not_set("fees")) .and_then(|fees| GenesisFees::try_from_raw_ref(fees).map_err(Self::Error::fees))?; - let connect = connect - .as_ref() - .ok_or_else(|| Self::Error::field_not_set("connect"))?; - - let market_map = connect - .market_map - .as_ref() - .ok_or_else(|| Self::Error::field_not_set("market_map")) - .and_then(|market_map| { - market_map::v2::GenesisState::try_from_raw(market_map.clone()) - .map_err(Self::Error::market_map) - })?; - - let oracle = connect - .oracle - .as_ref() - .ok_or_else(|| Self::Error::field_not_set("oracle")) - .and_then(|oracle| { - oracle::v2::GenesisState::try_from_raw(oracle.clone()).map_err(Self::Error::oracle) - })?; + let connect = if let Some(connect) = connect { + Some(ConnectGenesis::try_from_raw_ref(connect).map_err(Self::Error::connect)?) + } else { + None + }; let this = Self { address_prefixes, @@ -404,10 +389,7 @@ impl Protobuf for GenesisAppState { ibc_parameters, allowed_fee_assets, fees, - connect: ConnectGenesis { - market_map, - oracle, - }, + connect, }; this.ensure_all_addresses_have_base_prefix() .map_err(Self::Error::address_does_not_match_base)?; @@ -441,7 +423,7 @@ impl Protobuf for GenesisAppState { ibc_parameters: Some(ibc_parameters.to_raw()), allowed_fee_assets: allowed_fee_assets.iter().map(ToString::to_string).collect(), fees: Some(fees.to_raw()), - connect: Some(connect.to_raw()), + connect: connect.as_ref().map(ConnectGenesis::to_raw), } } } @@ -525,14 +507,8 @@ impl GenesisAppStateError { }) } - fn market_map(source: market_map::v2::GenesisStateError) -> Self { - Self(GenesisAppStateErrorKind::MarketMap { - source, - }) - } - - fn oracle(source: oracle::v2::GenesisStateError) -> Self { - Self(GenesisAppStateErrorKind::Oracle { + fn connect(source: ConnectGenesisError) -> Self { + Self(GenesisAppStateErrorKind::Connect { source, }) } @@ -563,14 +539,8 @@ enum GenesisAppStateErrorKind { FieldNotSet { name: &'static str }, #[error("`native_asset_base_denomination` field was invalid")] NativeAssetBaseDenomination { source: ParseTracePrefixedError }, - #[error("`market_map` field was invalid")] - MarketMap { - source: market_map::v2::GenesisStateError, - }, - #[error("`oracle` field was invalid")] - Oracle { - source: oracle::v2::GenesisStateError, - }, + #[error("`connect` field was invalid")] + Connect { source: ConnectGenesisError }, } #[derive(Debug, thiserror::Error)] diff --git a/crates/astria-sequencer/src/connect/marketmap/component.rs b/crates/astria-sequencer/src/connect/marketmap/component.rs index a164bdd523..9f821e8aba 100644 --- a/crates/astria-sequencer/src/connect/marketmap/component.rs +++ b/crates/astria-sequencer/src/connect/marketmap/component.rs @@ -24,15 +24,18 @@ impl Component for MarketMapComponent { #[instrument(name = "MarketMapComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { - // TODO: put market map authorites and admin in state; - // only required for related actions however - - state - .put_market_map(app_state.connect().market_map().market_map.clone()) - .wrap_err("failed to put market map")?; - state - .put_params(app_state.connect().market_map().params.clone()) - .wrap_err("failed to put params")?; + if let Some(connect) = app_state.connect() { + // TODO: put market map authorites and admin in state; + // only required for related actions however + + state + .put_market_map(connect.market_map().market_map.clone()) + .wrap_err("failed to put market map")?; + state + .put_params(connect.market_map().params.clone()) + .wrap_err("failed to put params")?; + } + Ok(()) } diff --git a/crates/astria-sequencer/src/connect/oracle/component.rs b/crates/astria-sequencer/src/connect/oracle/component.rs index e5db437d63..ddb3a18a6e 100644 --- a/crates/astria-sequencer/src/connect/oracle/component.rs +++ b/crates/astria-sequencer/src/connect/oracle/component.rs @@ -26,26 +26,31 @@ impl Component for OracleComponent { #[instrument(name = "OracleComponent::init_chain", skip(state))] async fn init_chain(mut state: S, app_state: &Self::AppState) -> Result<()> { - for currency_pair in &app_state.connect().oracle().currency_pair_genesis { - let currency_pair_state = CurrencyPairState { - id: currency_pair.id(), - nonce: currency_pair.nonce(), - price: currency_pair.currency_pair_price().clone(), - }; + if let Some(connect) = app_state.connect() { + for currency_pair in &connect.oracle().currency_pair_genesis { + let currency_pair_state = CurrencyPairState { + id: currency_pair.id(), + nonce: currency_pair.nonce(), + price: currency_pair.currency_pair_price().clone(), + }; + state + .put_currency_pair_state( + currency_pair.currency_pair().clone(), + currency_pair_state, + ) + .wrap_err("failed to write currency pair to state")?; + } + + state + .put_next_currency_pair_id(connect.oracle().next_id) + .wrap_err("failed to put next currency pair id")?; state - .put_currency_pair_state(currency_pair.currency_pair().clone(), currency_pair_state) - .wrap_err("failed to write currency pair to state")?; + .put_num_currency_pairs(connect.oracle().currency_pair_genesis.len() as u64) + .wrap_err("failed to put number of currency pairs")?; + state + .put_num_removed_currency_pairs(0) + .wrap_err("failed to put number of removed currency pairs")?; } - - state - .put_next_currency_pair_id(app_state.connect().oracle().next_id) - .wrap_err("failed to put next currency pair id")?; - state - .put_num_currency_pairs(app_state.connect().oracle().currency_pair_genesis.len() as u64) - .wrap_err("failed to put number of currency pairs")?; - state - .put_num_removed_currency_pairs(0) - .wrap_err("failed to put number of removed currency pairs")?; Ok(()) } From fcf8f49c8487ebfd05fab18c683d2a020c0c86c2 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 1 Nov 2024 11:12:43 -0400 Subject: [PATCH 82/89] handle vote_extensions_enable_height and updates to it to handle number of injected txs --- .../src/app/benchmark_and_test_utils.rs | 1 + crates/astria-sequencer/src/app/mod.rs | 335 +++++++++++++----- crates/astria-sequencer/src/app/state_ext.rs | 24 ++ .../astria-sequencer/src/app/storage/keys.rs | 1 + .../astria-sequencer/src/service/consensus.rs | 20 +- .../src/service/mempool/tests.rs | 8 +- 6 files changed, 285 insertions(+), 104 deletions(-) diff --git a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs index 27d153e211..e947408011 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -206,6 +206,7 @@ pub(crate) async fn initialize_app_with_storage( genesis_state, genesis_validators, "test".to_string(), + 1, ) .await .unwrap(); diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 6671cd8cfc..f25aa94d4c 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -21,6 +21,7 @@ pub(crate) mod vote_extension; use std::{ collections::VecDeque, sync::Arc, + u64, }; use astria_core::{ @@ -148,13 +149,26 @@ const EXECUTION_RESULTS_KEY: &str = "execution_results"; // cleared at the end of the block. const POST_TRANSACTION_EXECUTION_RESULT_KEY: &str = "post_transaction_execution_result"; -// the number of non-external transactions expected at the start of the block. +// the number of non-external transactions expected at the start of the block +// before vote extensions are enabled. // -// currently consists of: +// consists of: +// 1. rollup data root +// 2. rollup IDs root +const INJECTED_TRANSACTIONS_COUNT_BEFORE_VOTE_EXTENSIONS_ENABLED: usize = 2; + +// the number of non-external transactions expected at the start of the block +// after vote extensions are enabled. +// +// consists of: // 1. encoded `ExtendedCommitInfo` for the previous block // 2. rollup data root // 3. rollup IDs root -const INHERENT_TRANSACTIONS_COUNT: usize = 3; +const INJECTED_TRANSACTIONS_COUNT_AFTER_VOTE_EXTENSIONS_ENABLED: usize = 3; + +// the height to set the `vote_extensions_enable_height` to in state if vote extensions are +// disabled. +const VOTE_EXTENSIONS_DISABLED_HEIGHT: u64 = u64::MAX; /// The inter-block state being written to by the application. type InterBlockState = Arc>; @@ -299,6 +313,7 @@ impl App { genesis_state: GenesisAppState, genesis_validators: Vec, chain_id: String, + vote_extensions_enable_height: u64, ) -> Result { let mut state_tx = self .state @@ -328,6 +343,17 @@ impl App { .put_block_height(0) .wrap_err("failed to write block height to state")?; + // if `vote_extensions_enable_height` is 0, vote extensions are disabled. + // we set it to `u64::MAX` in state so checking if our current height is past + // the vote extensions enabled height will always be false. + let vote_extensions_enable_height = match vote_extensions_enable_height { + 0 => VOTE_EXTENSIONS_DISABLED_HEIGHT, + _ => vote_extensions_enable_height, + }; + state_tx + .put_vote_extensions_enable_height(vote_extensions_enable_height) + .wrap_err("failed to write vote extensions enabled height to state")?; + // call init_chain on all components FeesComponent::init_chain(&mut state_tx, &genesis_state) .await @@ -394,55 +420,79 @@ impl App { self.executed_proposal_fingerprint = Some(prepare_proposal.clone().into()); self.update_state_for_new_round(&storage); - // create the extended commit info from the local last commit - let Some(last_commit) = prepare_proposal.local_last_commit else { - bail!("local last commit is empty; this should not occur") - }; + let vote_extensions_enable_height = self + .state + .get_vote_extensions_enable_height() + .await + .wrap_err("failed to get vote extensions enabled height")?; - // if this fails, we shouldn't return an error, but instead leave - // the vote extensions empty in this block for liveness. - // it's not a critical error if the oracle values are not updated for a block. - let round = last_commit.round; - let extended_commit_info = match ProposalHandler::prepare_proposal( - &self.state, - prepare_proposal.height.into(), - last_commit, - ) - .await + let (max_tx_bytes, encoded_extended_commit_info) = if vote_extensions_enable_height + <= prepare_proposal.height.value() { - Ok(info) => info.into_inner(), - Err(e) => { - warn!( - error = AsRef::::as_ref(&e), - "failed to generate extended commit info" - ); - tendermint::abci::types::ExtendedCommitInfo { - round, - votes: Vec::new(), + // create the extended commit info from the local last commit + let Some(last_commit) = prepare_proposal.local_last_commit else { + bail!("local last commit is empty; this should not occur") + }; + + // if this fails, we shouldn't return an error, but instead leave + // the vote extensions empty in this block for liveness. + // it's not a critical error if the oracle values are not updated for a block. + // + // note that at the height where vote extensions are enabled, the `extended_commit_info` + // will always be empty, as there were no vote extensions for the previous block. + let round = last_commit.round; + let extended_commit_info = match ProposalHandler::prepare_proposal( + &self.state, + prepare_proposal.height.into(), + last_commit, + ) + .await + { + Ok(info) => info.into_inner(), + Err(e) => { + warn!( + error = AsRef::::as_ref(&e), + "failed to generate extended commit info" + ); + tendermint::abci::types::ExtendedCommitInfo { + round, + votes: Vec::new(), + } } - } - }; + }; - let mut encoded_extended_commit_info = - ExtendedCommitInfo::from(extended_commit_info).encode_to_vec(); - let max_tx_bytes = usize::try_from(prepare_proposal.max_tx_bytes) - .wrap_err("failed to convert max_tx_bytes to usize")?; - - // adjust max block size to account for extended commit info - let adjusted_max_tx_bytes = max_tx_bytes - .checked_sub(encoded_extended_commit_info.len()) - .unwrap_or_else(|| { - // zero the commit info if it's too large to fit in the block - // for liveness. - warn!( - encoded_extended_commit_info_len = encoded_extended_commit_info.len(), - max_tx_bytes, - "extended commit info is too large to fit in block; not including in block" - ); - encoded_extended_commit_info.clear(); + let mut encoded_extended_commit_info = + ExtendedCommitInfo::from(extended_commit_info).encode_to_vec(); + let max_tx_bytes = usize::try_from(prepare_proposal.max_tx_bytes) + .wrap_err("failed to convert max_tx_bytes to usize")?; + + // adjust max block size to account for extended commit info + ( max_tx_bytes - }); - let mut block_size_constraints = BlockSizeConstraints::new(adjusted_max_tx_bytes) + .checked_sub(encoded_extended_commit_info.len()) + .unwrap_or_else(|| { + // zero the commit info if it's too large to fit in the block + // for liveness. + warn!( + encoded_extended_commit_info_len = encoded_extended_commit_info.len(), + max_tx_bytes, + "extended commit info is too large to fit in block; not including in \ + block" + ); + encoded_extended_commit_info.clear(); + max_tx_bytes + }), + Some(encoded_extended_commit_info), + ) + } else { + ( + usize::try_from(prepare_proposal.max_tx_bytes) + .wrap_err("failed to convert max_tx_bytes to usize")?, + None, + ) + }; + + let mut block_size_constraints = BlockSizeConstraints::new(max_tx_bytes) .wrap_err("failed to create block size constraints")?; let block_data = BlockData { @@ -472,10 +522,15 @@ impl App { // included in the block let res = generate_rollup_datas_commitment(&signed_txs_included, deposits); - // inject the extended commit info into the start of the block's txs - let txs = std::iter::once(encoded_extended_commit_info.into()) - .chain(res.into_iter().chain(included_tx_bytes)) - .collect(); + let txs = match encoded_extended_commit_info { + Some(encoded_extended_commit_info) => { + std::iter::once(encoded_extended_commit_info.into()) + .chain(res.into_iter().chain(included_tx_bytes)) + .collect() + } + None => res.into_iter().chain(included_tx_bytes).collect(), + }; + Ok(abci::response::PrepareProposal { txs, }) @@ -536,28 +591,38 @@ impl App { let mut txs = VecDeque::from(process_proposal.txs.clone()); - // the first transaction in the block should be the extended commit info - let extended_commit_info_bytes = txs - .pop_front() - .wrap_err("no extended commit info in proposal")?; - - // decode the extended commit info and validate it - let extended_commit_info = ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) - .wrap_err("failed to decode extended commit info")?; - let extended_commit_info = extended_commit_info - .try_into() - .wrap_err("failed to convert extended commit info from proto to native")?; - let Some(last_commit) = process_proposal.proposed_last_commit else { - bail!("proposed last commit is empty; this should not occur") - }; - ProposalHandler::validate_proposal( - &self.state, - process_proposal.height.value(), - &last_commit, - &extended_commit_info, - ) - .await - .wrap_err("failed to validate extended commit info")?; + let vote_extensions_enable_height = self + .state + .get_vote_extensions_enable_height() + .await + .wrap_err("failed to get vote extensions enabled height")?; + + if vote_extensions_enable_height <= process_proposal.height.value() { + // if vote extensions are enabled, the first transaction in the block should be the + // extended commit info + let extended_commit_info_bytes = txs + .pop_front() + .wrap_err("no extended commit info in proposal")?; + + // decode the extended commit info and validate it + let extended_commit_info = + ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) + .wrap_err("failed to decode extended commit info")?; + let extended_commit_info = extended_commit_info + .try_into() + .wrap_err("failed to convert extended commit info from proto to native")?; + let Some(last_commit) = process_proposal.proposed_last_commit else { + bail!("proposed last commit is empty; this should not occur") + }; + ProposalHandler::validate_proposal( + &self.state, + process_proposal.height.value(), + &last_commit, + &extended_commit_info, + ) + .await + .wrap_err("failed to validate extended commit info")?; + } let received_rollup_datas_root: [u8; 32] = txs .pop_front() @@ -1037,6 +1102,17 @@ impl App { .put_deposits(&block_hash, deposits_in_this_block.clone()) .wrap_err("failed to put deposits to state")?; + let vote_extensions_enable_height = self + .state + .get_vote_extensions_enable_height() + .await + .wrap_err("failed to get vote extensions enabled height")?; + let injected_txs_count = if vote_extensions_enable_height <= height.value() { + INJECTED_TRANSACTIONS_COUNT_AFTER_VOTE_EXTENSIONS_ENABLED + } else { + INJECTED_TRANSACTIONS_COUNT_BEFORE_VOTE_EXTENSIONS_ENABLED + }; + // cometbft expects a result for every tx in the block, so we need to return a // tx result for the commitments, even though they're not actually user txs. // @@ -1044,7 +1120,7 @@ impl App { // transaction, not the commitment, so its length is len(txs) - 3. let mut finalize_block_tx_results: Vec = Vec::with_capacity(txs.len()); finalize_block_tx_results - .extend(std::iter::repeat(ExecTxResult::default()).take(INHERENT_TRANSACTIONS_COUNT)); + .extend(std::iter::repeat(ExecTxResult::default()).take(injected_txs_count)); finalize_block_tx_results.extend(tx_results); let sequencer_block = SequencerBlock::try_from_block_info_and_data( @@ -1061,6 +1137,14 @@ impl App { .put_sequencer_block(sequencer_block) .wrap_err("failed to write sequencer block to state")?; + self.handle_consensus_param_updates( + &mut state_tx, + &end_block.consensus_param_updates, + vote_extensions_enable_height, + ) + .await + .wrap_err("failed to handle consensus param updates")?; + let result = PostTransactionExecutionResult { events: end_block.events, validator_updates: end_block.validator_updates, @@ -1077,6 +1161,42 @@ impl App { Ok(()) } + async fn handle_consensus_param_updates( + &mut self, + state_tx: &mut StateDelta>>, + consensus_param_updates: &Option, + current_vote_extensions_enable_height: u64, + ) -> Result<()> { + if let Some(consensus_param_updates) = &consensus_param_updates { + if let Some(new_vote_extensions_enable_height) = + consensus_param_updates.abci.vote_extensions_enable_height + { + // if vote extensions are already enabled, they cannot be disabled, + // and the `vote_extensions_enable_height` cannot be changed. + if current_vote_extensions_enable_height != VOTE_EXTENSIONS_DISABLED_HEIGHT { + warn!( + "vote extensions enabled height already set to {}; ignoring update", + current_vote_extensions_enable_height + ); + return Ok(()); + } + + // vote extensions are currently disabled, so updating the enabled height to + // 0 (which also means disabling them) is a no-op. + if new_vote_extensions_enable_height.value() == 0 { + warn!("ignoring update to set vote extensions enable height to 0"); + return Ok(()); + } + + state_tx + .put_vote_extensions_enable_height(new_vote_extensions_enable_height.value()) + .wrap_err("failed to put vote extensions enabled height")?; + } + } + + Ok(()) + } + /// Executes the given block, but does not write it to disk. /// /// `commit` must be called after this to write the block to disk. @@ -1095,28 +1215,48 @@ impl App { self.update_state_for_new_round(&storage); } - ensure!( - finalize_block.txs.len() >= INHERENT_TRANSACTIONS_COUNT, - "block must contain at least three transactions: the extended commit info, the rollup \ - transactions commitment and rollup IDs commitment" - ); + let vote_extensions_enable_height = self + .state + .get_vote_extensions_enable_height() + .await + .wrap_err("failed to get vote extensions enabled height")?; + let injected_transactions_count = + if vote_extensions_enable_height <= finalize_block.height.value() { + ensure!( + finalize_block.txs.len() + >= INJECTED_TRANSACTIONS_COUNT_AFTER_VOTE_EXTENSIONS_ENABLED, + "block must contain at least three transactions: the extended commit info, \ + the rollup transactions commitment and rollup IDs commitment" + ); - let extended_commit_info_bytes = finalize_block.txs.first().expect("asserted length above"); - let extended_commit_info = ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) - .wrap_err("failed to decode extended commit info")? - .try_into() - .context("failed to validate decoded extended commit info")?; - let mut state_tx: StateDelta>> = - StateDelta::new(self.state.clone()); - crate::app::vote_extension::apply_prices_from_vote_extensions( - &mut state_tx, - extended_commit_info, - finalize_block.time.into(), - finalize_block.height.value(), - ) - .await - .wrap_err("failed to apply prices from vote extensions")?; - let _ = self.apply(state_tx); + let extended_commit_info_bytes = + finalize_block.txs.first().expect("asserted length above"); + let extended_commit_info = + ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) + .wrap_err("failed to decode extended commit info")? + .try_into() + .context("failed to validate decoded extended commit info")?; + let mut state_tx: StateDelta>> = + StateDelta::new(self.state.clone()); + crate::app::vote_extension::apply_prices_from_vote_extensions( + &mut state_tx, + extended_commit_info, + finalize_block.time.into(), + finalize_block.height.value(), + ) + .await + .wrap_err("failed to apply prices from vote extensions")?; + let _ = self.apply(state_tx); + INJECTED_TRANSACTIONS_COUNT_AFTER_VOTE_EXTENSIONS_ENABLED + } else { + ensure!( + finalize_block.txs.len() + >= INJECTED_TRANSACTIONS_COUNT_BEFORE_VOTE_EXTENSIONS_ENABLED, + "block must contain at least two transactions: the rollup transactions \ + commitment and rollup IDs commitment" + ); + INJECTED_TRANSACTIONS_COUNT_BEFORE_VOTE_EXTENSIONS_ENABLED + }; // When the hash is not empty, we have already executed and cached the results if self.executed_proposal_hash.is_empty() { @@ -1140,8 +1280,9 @@ impl App { .wrap_err("failed to execute block")?; let mut tx_results = Vec::with_capacity(finalize_block.txs.len()); - // skip the first three transactions, as they are injected transactions - for tx in finalize_block.txs.iter().skip(INHERENT_TRANSACTIONS_COUNT) { + // skip the first `injected_transactions_count` transactions, as they are injected + // transactions + for tx in finalize_block.txs.iter().skip(injected_transactions_count) { let signed_tx = signed_transaction_from_bytes(tx) .wrap_err("protocol error; only valid txs should be finalized")?; diff --git a/crates/astria-sequencer/src/app/state_ext.rs b/crates/astria-sequencer/src/app/state_ext.rs index b9a2e3b78e..48d97848de 100644 --- a/crates/astria-sequencer/src/app/state_ext.rs +++ b/crates/astria-sequencer/src/app/state_ext.rs @@ -99,6 +99,21 @@ pub(crate) trait StateReadExt: StateRead { .and_then(|value| storage::StorageVersion::try_from(value).map(u64::from)) .context("invalid storage version bytes") } + + #[instrument(skip_all)] + async fn get_vote_extensions_enable_height(&self) -> Result { + let Some(bytes) = self + .nonverifiable_get_raw(keys::VOTE_EXTENSIONS_ENABLED_HEIGHT.as_bytes()) + .await + .map_err(anyhow_to_eyre) + .wrap_err("failed to read raw vote extensions enabled height from state")? + else { + bail!("vote extensions enabled height not found"); + }; + StoredValue::deserialize(&bytes) + .and_then(|value| storage::BlockHeight::try_from(value).map(u64::from)) + .context("invalid vote extensions enabled height bytes") + } } impl StateReadExt for T {} @@ -150,6 +165,15 @@ pub(crate) trait StateWriteExt: StateWrite { self.nonverifiable_put_raw(keys::storage_version_by_height(height).into_bytes(), bytes); Ok(()) } + + #[instrument(skip_all)] + fn put_vote_extensions_enable_height(&mut self, height: u64) -> Result<()> { + let bytes = StoredValue::from(storage::BlockHeight::from(height)) + .serialize() + .context("failed to serialize vote extensions enabled height")?; + self.nonverifiable_put_raw(keys::VOTE_EXTENSIONS_ENABLED_HEIGHT.into(), bytes); + Ok(()) + } } impl StateWriteExt for T {} diff --git a/crates/astria-sequencer/src/app/storage/keys.rs b/crates/astria-sequencer/src/app/storage/keys.rs index 564fab2cdf..e0c57f17b7 100644 --- a/crates/astria-sequencer/src/app/storage/keys.rs +++ b/crates/astria-sequencer/src/app/storage/keys.rs @@ -2,6 +2,7 @@ pub(in crate::app) const CHAIN_ID: &str = "app/chain_id"; pub(in crate::app) const REVISION_NUMBER: &str = "app/revision_number"; pub(in crate::app) const BLOCK_HEIGHT: &str = "app/block_height"; pub(in crate::app) const BLOCK_TIMESTAMP: &str = "app/block_timestamp"; +pub(in crate::app) const VOTE_EXTENSIONS_ENABLED_HEIGHT: &str = "app/vote_extensions_enable_height"; pub(in crate::app) fn storage_version_by_height(height: u64) -> String { format!("app/storage_version/{height}") diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index b98ea99449..f8329f57b3 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -154,6 +154,14 @@ impl Consensus { "failed converting cometbft genesis validators to astria validators", )?, init_chain.chain_id, + // if `vote_extensions_enable_height` is zero, vote extensions are disabled. + // see https://docs.cometbft.com/v1.0/spec/core/data_structures#abciparams + init_chain + .consensus_params + .abci + .vote_extensions_enable_height + .unwrap_or_default() + .value(), ) .await .wrap_err("failed to call init_chain")?; @@ -546,9 +554,15 @@ mod tests { ) .await .unwrap(); - app.init_chain(storage.clone(), genesis_state, vec![], "test".to_string()) - .await - .unwrap(); + app.init_chain( + storage.clone(), + genesis_state, + vec![], + "test".to_string(), + 1, + ) + .await + .unwrap(); app.commit(storage.clone()).await; let (_tx, rx) = mpsc::channel(1); diff --git a/crates/astria-sequencer/src/service/mempool/tests.rs b/crates/astria-sequencer/src/service/mempool/tests.rs index 416c1b60ef..2e78182036 100644 --- a/crates/astria-sequencer/src/service/mempool/tests.rs +++ b/crates/astria-sequencer/src/service/mempool/tests.rs @@ -36,7 +36,7 @@ async fn future_nonces_are_accepted() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string()) + app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) .await .unwrap(); app.commit(storage.clone()).await; @@ -68,7 +68,7 @@ async fn rechecks_pass() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string()) + app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) .await .unwrap(); app.commit(storage.clone()).await; @@ -108,7 +108,7 @@ async fn can_reinsert_after_recheck_fail() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string()) + app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) .await .unwrap(); app.commit(storage.clone()).await; @@ -158,7 +158,7 @@ async fn recheck_adds_non_tracked_tx() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string()) + app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) .await .unwrap(); app.commit(storage.clone()).await; From 8baa0c3aea43a0d3dd406a3fe61cf27995642f36 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 1 Nov 2024 11:14:36 -0400 Subject: [PATCH 83/89] clippy --- crates/astria-sequencer/src/app/mod.rs | 75 +++++++++---------- .../src/service/mempool/tests.rs | 48 +++++++++--- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index f25aa94d4c..ef6016bfa1 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -21,7 +21,6 @@ pub(crate) mod vote_extension; use std::{ collections::VecDeque, sync::Arc, - u64, }; use astria_core::{ @@ -1137,12 +1136,11 @@ impl App { .put_sequencer_block(sequencer_block) .wrap_err("failed to write sequencer block to state")?; - self.handle_consensus_param_updates( + handle_consensus_param_updates( &mut state_tx, &end_block.consensus_param_updates, vote_extensions_enable_height, ) - .await .wrap_err("failed to handle consensus param updates")?; let result = PostTransactionExecutionResult { @@ -1161,42 +1159,6 @@ impl App { Ok(()) } - async fn handle_consensus_param_updates( - &mut self, - state_tx: &mut StateDelta>>, - consensus_param_updates: &Option, - current_vote_extensions_enable_height: u64, - ) -> Result<()> { - if let Some(consensus_param_updates) = &consensus_param_updates { - if let Some(new_vote_extensions_enable_height) = - consensus_param_updates.abci.vote_extensions_enable_height - { - // if vote extensions are already enabled, they cannot be disabled, - // and the `vote_extensions_enable_height` cannot be changed. - if current_vote_extensions_enable_height != VOTE_EXTENSIONS_DISABLED_HEIGHT { - warn!( - "vote extensions enabled height already set to {}; ignoring update", - current_vote_extensions_enable_height - ); - return Ok(()); - } - - // vote extensions are currently disabled, so updating the enabled height to - // 0 (which also means disabling them) is a no-op. - if new_vote_extensions_enable_height.value() == 0 { - warn!("ignoring update to set vote extensions enable height to 0"); - return Ok(()); - } - - state_tx - .put_vote_extensions_enable_height(new_vote_extensions_enable_height.value()) - .wrap_err("failed to put vote extensions enabled height")?; - } - } - - Ok(()) - } - /// Executes the given block, but does not write it to disk. /// /// `commit` must be called after this to write the block to disk. @@ -1574,6 +1536,41 @@ impl App { } } +fn handle_consensus_param_updates( + state_tx: &mut StateDelta>>, + consensus_param_updates: &Option, + current_vote_extensions_enable_height: u64, +) -> Result<()> { + if let Some(consensus_param_updates) = &consensus_param_updates { + if let Some(new_vote_extensions_enable_height) = + consensus_param_updates.abci.vote_extensions_enable_height + { + // if vote extensions are already enabled, they cannot be disabled, + // and the `vote_extensions_enable_height` cannot be changed. + if current_vote_extensions_enable_height != VOTE_EXTENSIONS_DISABLED_HEIGHT { + warn!( + "vote extensions enable height already set to {}; ignoring update", + current_vote_extensions_enable_height + ); + return Ok(()); + } + + // vote extensions are currently disabled, so updating the enabled height to + // 0 (which also means disabling them) is a no-op. + if new_vote_extensions_enable_height.value() == 0 { + warn!("ignoring update to set vote extensions enable height to 0"); + return Ok(()); + } + + state_tx + .put_vote_extensions_enable_height(new_vote_extensions_enable_height.value()) + .wrap_err("failed to put vote extensions enable height")?; + } + } + + Ok(()) +} + // updates the mempool to reflect current state // // NOTE: this function locks the mempool until all accounts have been cleaned. diff --git a/crates/astria-sequencer/src/service/mempool/tests.rs b/crates/astria-sequencer/src/service/mempool/tests.rs index 2e78182036..b85f41df7c 100644 --- a/crates/astria-sequencer/src/service/mempool/tests.rs +++ b/crates/astria-sequencer/src/service/mempool/tests.rs @@ -36,9 +36,15 @@ async fn future_nonces_are_accepted() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) - .await - .unwrap(); + app.init_chain( + storage.clone(), + genesis_state(), + vec![], + "test".to_string(), + 0, + ) + .await + .unwrap(); app.commit(storage.clone()).await; let the_future_nonce = 10; @@ -68,9 +74,15 @@ async fn rechecks_pass() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) - .await - .unwrap(); + app.init_chain( + storage.clone(), + genesis_state(), + vec![], + "test".to_string(), + 0, + ) + .await + .unwrap(); app.commit(storage.clone()).await; let tx = MockTxBuilder::new().nonce(0).build(); @@ -108,9 +120,15 @@ async fn can_reinsert_after_recheck_fail() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) - .await - .unwrap(); + app.init_chain( + storage.clone(), + genesis_state(), + vec![], + "test".to_string(), + 0, + ) + .await + .unwrap(); app.commit(storage.clone()).await; let tx = MockTxBuilder::new().nonce(0).build(); @@ -158,9 +176,15 @@ async fn recheck_adds_non_tracked_tx() { .await .unwrap(); - app.init_chain(storage.clone(), genesis_state(), vec![], "test".to_string(), 0) - .await - .unwrap(); + app.init_chain( + storage.clone(), + genesis_state(), + vec![], + "test".to_string(), + 0, + ) + .await + .unwrap(); app.commit(storage.clone()).await; let tx = MockTxBuilder::new().nonce(0).build(); From fbdb0153550deec05e4efbaf15cb5184a5a3e0cb Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 4 Nov 2024 14:23:57 -0500 Subject: [PATCH 84/89] rename ASTRIA_SEQUENCER_CONNECT_GRPC_ADDR to ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR, increment next_currency_pair_id in put_price_for_currency_pair --- charts/sequencer/templates/configmaps.yaml | 2 +- crates/astria-core/src/connect/types.rs | 5 +++++ crates/astria-sequencer/local.env.example | 4 ++-- crates/astria-sequencer/src/app/mod.rs | 2 ++ crates/astria-sequencer/src/config.rs | 4 ++-- crates/astria-sequencer/src/connect/oracle/state_ext.rs | 3 +++ crates/astria-sequencer/src/sequencer.rs | 2 +- 7 files changed, 16 insertions(+), 6 deletions(-) diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index 036ba3e273..263ab977dd 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,7 +73,7 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" - ASTRIA_SEQUENCER_CONNECT_GRPC_ADDR: "http://127.0.0.1:8081" + ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8081" ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" {{- if not .Values.global.dev }} {{- else }} diff --git a/crates/astria-core/src/connect/types.rs b/crates/astria-core/src/connect/types.rs index 2009a73c38..4dfc191a75 100644 --- a/crates/astria-core/src/connect/types.rs +++ b/crates/astria-core/src/connect/types.rs @@ -322,6 +322,11 @@ pub mod v2 { pub fn get(self) -> u64 { self.0 } + + pub fn increment(self) -> Self { + let new_id = self.0.wrapping_add(1); + Self::new(new_id) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/astria-sequencer/local.env.example b/crates/astria-sequencer/local.env.example index b219d1e3f2..30830e98a2 100644 --- a/crates/astria-sequencer/local.env.example +++ b/crates/astria-sequencer/local.env.example @@ -33,12 +33,12 @@ ASTRIA_SEQUENCER_METRICS_HTTP_LISTENER_ADDR="127.0.0.1:9000" # `ASTRIA_SEQUENCER_FORCE_STDOUT` is set to `true`. ASTRIA_SEQUENCER_PRETTY_PRINT=false -# If the oracle is disabled. If false, the connect_grpc_addr must be set. +# If the oracle is disabled. If false, the oracle_grpc_addr must be set. # Should be false for validator nodes and true for non-validator nodes. ASTRIA_SEQUENCER_NO_ORACLE=true # The gRPC endpoint for the oracle sidecar. -ASTRIA_SEQUENCER_CONNECT_GRPC_ADDR="http://127.0.0.1:8081" +ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR="http://127.0.0.1:8081" # The timeout for the responses from the oracle sidecar in milliseconds. ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS=1000 diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index ef6016bfa1..8c7656db76 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -372,6 +372,8 @@ impl App { IbcComponent::init_chain(&mut state_tx, &genesis_state) .await .wrap_err("init_chain failed on IbcComponent")?; + + // TODO: handle vote_extensions_enable_height in MarketMapComponent MarketMapComponent::init_chain(&mut state_tx, &genesis_state) .await .wrap_err("init_chain failed on MarketMapComponent")?; diff --git a/crates/astria-sequencer/src/config.rs b/crates/astria-sequencer/src/config.rs index 5c8b48efe3..f5103c27bf 100644 --- a/crates/astria-sequencer/src/config.rs +++ b/crates/astria-sequencer/src/config.rs @@ -30,11 +30,11 @@ pub struct Config { pub metrics_http_listener_addr: String, /// Writes a human readable format to stdout instead of JSON formatted OTEL trace data. pub pretty_print: bool, - /// If the oracle is disabled. If false, the `connect_grpc_addr` must be set. + /// If the oracle is disabled. If false, the `oracle_grpc_addr` must be set. /// Should be false for validator nodes and true for non-validator nodes. pub no_oracle: bool, /// The gRPC endpoint for the oracle sidecar. - pub connect_grpc_addr: String, + pub oracle_grpc_addr: String, /// The timeout for the responses from the oracle sidecar in milliseconds. pub oracle_client_timeout_milliseconds: u64, /// The maximum number of transactions that can be parked in the mempool. diff --git a/crates/astria-sequencer/src/connect/oracle/state_ext.rs b/crates/astria-sequencer/src/connect/oracle/state_ext.rs index 21d98fe827..3e7b4fb333 100644 --- a/crates/astria-sequencer/src/connect/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/connect/oracle/state_ext.rs @@ -532,6 +532,9 @@ pub(crate) trait StateWriteExt: StateWrite { .get_next_currency_pair_id() .await .wrap_err("failed to read next currency pair ID")?; + let next_id = id.increment(); + self.put_next_currency_pair_id(next_id) + .wrap_err("failed to put next currency pair ID")?; CurrencyPairState { price, nonce: CurrencyPairNonce::new(0), diff --git a/crates/astria-sequencer/src/sequencer.rs b/crates/astria-sequencer/src/sequencer.rs index 59e479309a..a9b9e0f76d 100644 --- a/crates/astria-sequencer/src/sequencer.rs +++ b/crates/astria-sequencer/src/sequencer.rs @@ -121,7 +121,7 @@ impl Sequencer { None } else { let uri: Uri = config - .connect_grpc_addr + .oracle_grpc_addr .parse() .context("failed parsing oracle grpc address as Uri")?; let endpoint = Endpoint::from(uri.clone()).timeout(std::time::Duration::from_millis( From d291a0b26a7ba23a816408fe28a789b630c805f4 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 4 Nov 2024 14:44:39 -0500 Subject: [PATCH 85/89] only init MarketMap and Oracle components if vote extensions are enabled --- crates/astria-sequencer/src/app/mod.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 8c7656db76..d659dd93bf 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -373,13 +373,14 @@ impl App { .await .wrap_err("init_chain failed on IbcComponent")?; - // TODO: handle vote_extensions_enable_height in MarketMapComponent - MarketMapComponent::init_chain(&mut state_tx, &genesis_state) - .await - .wrap_err("init_chain failed on MarketMapComponent")?; - OracleComponent::init_chain(&mut state_tx, &genesis_state) - .await - .wrap_err("init_chain failed on OracleComponent")?; + if vote_extensions_enable_height != VOTE_EXTENSIONS_DISABLED_HEIGHT { + MarketMapComponent::init_chain(&mut state_tx, &genesis_state) + .await + .wrap_err("init_chain failed on MarketMapComponent")?; + OracleComponent::init_chain(&mut state_tx, &genesis_state) + .await + .wrap_err("init_chain failed on OracleComponent")?; + } state_tx.apply(); @@ -1564,6 +1565,9 @@ fn handle_consensus_param_updates( return Ok(()); } + // TODO: when we implement an action to activate vote extensions, + // we must ensure that the action *also* writes the necessary state + // as done in `MarketMapComponent::init_chain` and `OracleComponent::init_chain`. state_tx .put_vote_extensions_enable_height(new_vote_extensions_enable_height.value()) .wrap_err("failed to put vote extensions enable height")?; From 19c81f1c9487c167395103d16b93d9733f2dd5bb Mon Sep 17 00:00:00 2001 From: elizabeth Date: Mon, 4 Nov 2024 14:47:20 -0500 Subject: [PATCH 86/89] update CurrencyPairId::increment --- crates/astria-core/src/connect/types.rs | 7 ++++--- crates/astria-sequencer/src/connect/oracle/state_ext.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/astria-core/src/connect/types.rs b/crates/astria-core/src/connect/types.rs index 4dfc191a75..306495875a 100644 --- a/crates/astria-core/src/connect/types.rs +++ b/crates/astria-core/src/connect/types.rs @@ -323,9 +323,10 @@ pub mod v2 { self.0 } - pub fn increment(self) -> Self { - let new_id = self.0.wrapping_add(1); - Self::new(new_id) + #[must_use] + pub fn increment(self) -> Option { + let new_id = self.get().checked_add(1)?; + Some(Self::new(new_id)) } } diff --git a/crates/astria-sequencer/src/connect/oracle/state_ext.rs b/crates/astria-sequencer/src/connect/oracle/state_ext.rs index 3e7b4fb333..6096c8189f 100644 --- a/crates/astria-sequencer/src/connect/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/connect/oracle/state_ext.rs @@ -532,7 +532,7 @@ pub(crate) trait StateWriteExt: StateWrite { .get_next_currency_pair_id() .await .wrap_err("failed to read next currency pair ID")?; - let next_id = id.increment(); + let next_id = id.increment().wrap_err("increment ID overflowed")?; self.put_next_currency_pair_id(next_id) .wrap_err("failed to put next currency pair ID")?; CurrencyPairState { From ca02d51a3a9925384d22ff0d82322e502a16928d Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 5 Nov 2024 12:22:41 -0500 Subject: [PATCH 87/89] update charts to have configurable values and mark breaking changes --- charts/sequencer/files/cometbft/config/genesis.json | 4 ++-- charts/sequencer/templates/configmaps.yaml | 4 ++-- charts/sequencer/values.yaml | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index df4e9a72c4..6533e99f39 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -120,6 +120,8 @@ {{ include "sequencer.address" $value }} {{- end }} ], + {{- if not .Values.global.dev }} + {{- else }} "connect": { "marketMap": { "marketMap": { @@ -135,8 +137,6 @@ "nextId": "0" } } - {{- if not .Values.global.dev }} - {{- else }} {{- end}} }, "chain_id": "{{ .Values.genesis.chainId }}", diff --git a/charts/sequencer/templates/configmaps.yaml b/charts/sequencer/templates/configmaps.yaml index 263ab977dd..f666257a1d 100644 --- a/charts/sequencer/templates/configmaps.yaml +++ b/charts/sequencer/templates/configmaps.yaml @@ -73,8 +73,8 @@ data: OTEL_EXPORTER_OTLP_HEADERS: "{{ .Values.sequencer.otel.otlpHeaders }}" OTEL_EXPORTER_OTLP_TRACE_HEADERS: "{{ .Values.sequencer.otel.traceHeaders }}" OTEL_SERVICE_NAME: "{{ tpl .Values.sequencer.otel.serviceName . }}" - ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:8081" - ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "1000" + ASTRIA_SEQUENCER_ORACLE_GRPC_ADDR: "http://127.0.0.1:{{ .Values.ports.oracleGrpc }}" + ASTRIA_SEQUENCER_ORACLE_CLIENT_TIMEOUT_MILLISECONDS: "{{ .Values.sequencer.oracle.clientTimeout }}" {{- if not .Values.global.dev }} {{- else }} ASTRIA_SEQUENCER_NO_ORACLE: "true" diff --git a/charts/sequencer/values.yaml b/charts/sequencer/values.yaml index 552bd98200..8d01e06b7d 100644 --- a/charts/sequencer/values.yaml +++ b/charts/sequencer/values.yaml @@ -109,6 +109,8 @@ sequencer: mempool: parked: maxTxCount: 200 + oracle: + clientTimeout: 1000 metrics: enabled: false otel: @@ -276,6 +278,7 @@ ports: sequencerGrpc: 8080 relayerRpc: 2450 sequencerMetrics: 9000 + oracleGrpc: 8081 # ServiceMonitor configuration serviceMonitor: From 3722a2fa55078891fab709484f2819edccc070c8 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 5 Nov 2024 14:38:10 -0500 Subject: [PATCH 88/89] update vote_extensions_enable_height in chart genesis --- .../sequencer/files/cometbft/config/genesis.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 6533e99f39..09b0b8dbdc 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -141,9 +141,6 @@ }, "chain_id": "{{ .Values.genesis.chainId }}", "consensus_params": { - "abci": { - "vote_extensions_enable_height": "1" - }, "block": { "max_bytes": " {{ .Values.genesis.consensusParams.blockMaxBytes }}", "max_gas": "{{ .Values.genesis.consensusParams.blockMaxGas }}" @@ -160,7 +157,16 @@ }, "version": { "app": "0" - } + }, + {{- if not .Values.global.dev }} + "abci": { + "vote_extensions_enable_height": "0" + }, + {{- else }} + "abci": { + "vote_extensions_enable_height": "1" + }, + {{- end}} }, "genesis_time": "{{ .Values.genesis.genesisTime }}", "initial_height": "0", From cdcbaec8333970c620fc398e0cd966bc7ddc9fb6 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Tue, 5 Nov 2024 14:49:54 -0500 Subject: [PATCH 89/89] maybe fix genesis --- charts/sequencer/files/cometbft/config/genesis.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/sequencer/files/cometbft/config/genesis.json b/charts/sequencer/files/cometbft/config/genesis.json index 09b0b8dbdc..f76d4f3f2d 100644 --- a/charts/sequencer/files/cometbft/config/genesis.json +++ b/charts/sequencer/files/cometbft/config/genesis.json @@ -161,11 +161,11 @@ {{- if not .Values.global.dev }} "abci": { "vote_extensions_enable_height": "0" - }, + } {{- else }} "abci": { "vote_extensions_enable_height": "1" - }, + } {{- end}} }, "genesis_time": "{{ .Values.genesis.genesisTime }}",