From a3ed038a43764cfe88ff128be157bba789f8b02b Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 6 Dec 2024 15:26:45 -0500 Subject: [PATCH 1/7] add AddCurrencyPairs and RemoveCurrencyPairs action types --- crates/astria-core/src/connect/types.rs | 8 + .../astria.protocol.transaction.v1.rs | 37 ++- .../astria.protocol.transaction.v1.serde.rs | 210 ++++++++++++++++++ .../transaction/v1/action/group/mod.rs | 7 + .../src/protocol/transaction/v1/action/mod.rs | 164 ++++++++++++++ .../impls/add_currency_pairs.rs | 23 ++ .../src/action_handler/impls/mod.rs | 1 + .../impls/remove_currency_pairs.rs | 31 +++ .../src/action_handler/impls/transaction.rs | 14 ++ .../src/transaction/checks.rs | 4 +- .../protocol/transaction/v1/action.proto | 13 ++ 11 files changed, 510 insertions(+), 2 deletions(-) create mode 100644 crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs create mode 100644 crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs diff --git a/crates/astria-core/src/connect/types.rs b/crates/astria-core/src/connect/types.rs index d97f4b9896..13a110fc53 100644 --- a/crates/astria-core/src/connect/types.rs +++ b/crates/astria-core/src/connect/types.rs @@ -261,6 +261,14 @@ pub mod v2 { quote: self.quote.0, } } + + #[must_use] + pub fn to_raw(&self) -> raw::CurrencyPair { + raw::CurrencyPair { + base: self.base.0.clone(), + quote: self.quote.0.clone(), + } + } } impl TryFrom for CurrencyPair { diff --git a/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs b/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs index 0b38628394..dd06cee096 100644 --- a/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs +++ b/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs @@ -3,7 +3,7 @@ pub struct Action { #[prost( oneof = "action::Value", - tags = "1, 2, 11, 12, 13, 14, 21, 22, 50, 51, 52, 53, 55, 56" + tags = "1, 2, 11, 12, 13, 14, 21, 22, 50, 51, 52, 53, 55, 56, 61, 62" )] pub value: ::core::option::Option, } @@ -46,6 +46,11 @@ pub mod action { FeeChange(super::FeeChange), #[prost(message, tag = "56")] IbcSudoChange(super::IbcSudoChange), + /// Oracle actions are defined on 61-70 + #[prost(message, tag = "61")] + AddCurrencyPairs(super::AddCurrencyPairs), + #[prost(message, tag = "62")] + RemoveCurrencyPairs(super::RemoveCurrencyPairs), } } impl ::prost::Name for Action { @@ -464,6 +469,36 @@ impl ::prost::Name for IbcSudoChange { ::prost::alloc::format!("astria.protocol.transaction.v1.{}", Self::NAME) } } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AddCurrencyPairs { + #[prost(message, repeated, tag = "1")] + pub pairs: ::prost::alloc::vec::Vec< + super::super::super::super::connect::types::v2::CurrencyPair, + >, +} +impl ::prost::Name for AddCurrencyPairs { + const NAME: &'static str = "AddCurrencyPairs"; + const PACKAGE: &'static str = "astria.protocol.transaction.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.protocol.transaction.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RemoveCurrencyPairs { + #[prost(message, repeated, tag = "1")] + pub pairs: ::prost::alloc::vec::Vec< + super::super::super::super::connect::types::v2::CurrencyPair, + >, +} +impl ::prost::Name for RemoveCurrencyPairs { + const NAME: &'static str = "RemoveCurrencyPairs"; + const PACKAGE: &'static str = "astria.protocol.transaction.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.protocol.transaction.v1.{}", Self::NAME) + } +} /// `Transaction` is a transaction `TransactionBody` together with a public /// ket and a signature. #[allow(clippy::derive_partial_eq_without_eq)] diff --git a/crates/astria-core/src/generated/astria.protocol.transaction.v1.serde.rs b/crates/astria-core/src/generated/astria.protocol.transaction.v1.serde.rs index 4c77666d12..702f507d81 100644 --- a/crates/astria-core/src/generated/astria.protocol.transaction.v1.serde.rs +++ b/crates/astria-core/src/generated/astria.protocol.transaction.v1.serde.rs @@ -54,6 +54,12 @@ impl serde::Serialize for Action { action::Value::IbcSudoChange(v) => { struct_ser.serialize_field("ibcSudoChange", v)?; } + action::Value::AddCurrencyPairs(v) => { + struct_ser.serialize_field("addCurrencyPairs", v)?; + } + action::Value::RemoveCurrencyPairs(v) => { + struct_ser.serialize_field("removeCurrencyPairs", v)?; + } } } struct_ser.end() @@ -92,6 +98,10 @@ impl<'de> serde::Deserialize<'de> for Action { "feeChange", "ibc_sudo_change", "ibcSudoChange", + "add_currency_pairs", + "addCurrencyPairs", + "remove_currency_pairs", + "removeCurrencyPairs", ]; #[allow(clippy::enum_variant_names)] @@ -110,6 +120,8 @@ impl<'de> serde::Deserialize<'de> for Action { FeeAssetChange, FeeChange, IbcSudoChange, + AddCurrencyPairs, + RemoveCurrencyPairs, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -145,6 +157,8 @@ impl<'de> serde::Deserialize<'de> for Action { "feeAssetChange" | "fee_asset_change" => Ok(GeneratedField::FeeAssetChange), "feeChange" | "fee_change" => Ok(GeneratedField::FeeChange), "ibcSudoChange" | "ibc_sudo_change" => Ok(GeneratedField::IbcSudoChange), + "addCurrencyPairs" | "add_currency_pairs" => Ok(GeneratedField::AddCurrencyPairs), + "removeCurrencyPairs" | "remove_currency_pairs" => Ok(GeneratedField::RemoveCurrencyPairs), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -263,6 +277,20 @@ impl<'de> serde::Deserialize<'de> for Action { return Err(serde::de::Error::duplicate_field("ibcSudoChange")); } value__ = map_.next_value::<::std::option::Option<_>>()?.map(action::Value::IbcSudoChange) +; + } + GeneratedField::AddCurrencyPairs => { + if value__.is_some() { + return Err(serde::de::Error::duplicate_field("addCurrencyPairs")); + } + value__ = map_.next_value::<::std::option::Option<_>>()?.map(action::Value::AddCurrencyPairs) +; + } + GeneratedField::RemoveCurrencyPairs => { + if value__.is_some() { + return Err(serde::de::Error::duplicate_field("removeCurrencyPairs")); + } + value__ = map_.next_value::<::std::option::Option<_>>()?.map(action::Value::RemoveCurrencyPairs) ; } } @@ -275,6 +303,97 @@ impl<'de> serde::Deserialize<'de> for Action { deserializer.deserialize_struct("astria.protocol.transaction.v1.Action", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for AddCurrencyPairs { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.pairs.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.protocol.transaction.v1.AddCurrencyPairs", len)?; + if !self.pairs.is_empty() { + struct_ser.serialize_field("pairs", &self.pairs)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AddCurrencyPairs { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "pairs", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Pairs, + } + 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 { + "pairs" => Ok(GeneratedField::Pairs), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AddCurrencyPairs; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.protocol.transaction.v1.AddCurrencyPairs") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut pairs__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Pairs => { + if pairs__.is_some() { + return Err(serde::de::Error::duplicate_field("pairs")); + } + pairs__ = Some(map_.next_value()?); + } + } + } + Ok(AddCurrencyPairs { + pairs: pairs__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.protocol.transaction.v1.AddCurrencyPairs", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for BridgeLock { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -1919,6 +2038,97 @@ impl<'de> serde::Deserialize<'de> for InitBridgeAccount { deserializer.deserialize_struct("astria.protocol.transaction.v1.InitBridgeAccount", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for RemoveCurrencyPairs { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.pairs.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.protocol.transaction.v1.RemoveCurrencyPairs", len)?; + if !self.pairs.is_empty() { + struct_ser.serialize_field("pairs", &self.pairs)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for RemoveCurrencyPairs { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "pairs", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Pairs, + } + 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 { + "pairs" => Ok(GeneratedField::Pairs), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = RemoveCurrencyPairs; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.protocol.transaction.v1.RemoveCurrencyPairs") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut pairs__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Pairs => { + if pairs__.is_some() { + return Err(serde::de::Error::duplicate_field("pairs")); + } + pairs__ = Some(map_.next_value()?); + } + } + } + Ok(RemoveCurrencyPairs { + pairs: pairs__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.protocol.transaction.v1.RemoveCurrencyPairs", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for RollupDataSubmission { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/astria-core/src/protocol/transaction/v1/action/group/mod.rs b/crates/astria-core/src/protocol/transaction/v1/action/group/mod.rs index c145176803..efcb2508d7 100644 --- a/crates/astria-core/src/protocol/transaction/v1/action/group/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1/action/group/mod.rs @@ -11,6 +11,7 @@ use penumbra_ibc::IbcRelay; use super::{ Action, ActionName, + AddCurrencyPairs, BridgeLock, BridgeSudoChange, BridgeUnlock, @@ -20,6 +21,7 @@ use super::{ IbcSudoChange, Ics20Withdrawal, InitBridgeAccount, + RemoveCurrencyPairs, RollupDataSubmission, SudoAddressChange, Transfer, @@ -55,6 +57,9 @@ impl_belong_to_group!( (FeeAssetChange, Group::BundleableSudo), (IbcRelay, Group::BundleableGeneral), (IbcSudoChange, Group::UnbundleableSudo), + // TODO: should these have a different group? + (AddCurrencyPairs, Group::BundleableGeneral), + (RemoveCurrencyPairs, Group::BundleableGeneral), ); impl Action { @@ -74,6 +79,8 @@ impl Action { Action::FeeAssetChange(_) => FeeAssetChange::GROUP, Action::Ibc(_) => IbcRelay::GROUP, Action::IbcSudoChange(_) => IbcSudoChange::GROUP, + Action::AddCurrencyPairs(_) => AddCurrencyPairs::GROUP, + Action::RemoveCurrencyPairs(_) => RemoveCurrencyPairs::GROUP, } } } diff --git a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs index 80ba7d2326..1de5a9bf75 100644 --- a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs @@ -11,6 +11,10 @@ use prost::Name as _; use super::raw; use crate::{ + connect::types::v2::{ + CurrencyPair, + CurrencyPairError, + }, primitive::v1::{ asset::{ self, @@ -64,6 +68,8 @@ pub enum Action { BridgeUnlock(BridgeUnlock), BridgeSudoChange(BridgeSudoChange), FeeChange(FeeChange), + AddCurrencyPairs(AddCurrencyPairs), + RemoveCurrencyPairs(RemoveCurrencyPairs), } impl Protobuf for Action { @@ -88,6 +94,8 @@ impl Protobuf for Action { Action::BridgeUnlock(act) => Value::BridgeUnlock(act.to_raw()), Action::BridgeSudoChange(act) => Value::BridgeSudoChange(act.to_raw()), Action::FeeChange(act) => Value::FeeChange(act.to_raw()), + Action::AddCurrencyPairs(act) => Value::AddCurrencyPairs(act.to_raw()), + Action::RemoveCurrencyPairs(act) => Value::RemoveCurrencyPairs(act.to_raw()), }; raw::Action { value: Some(kind), @@ -161,6 +169,12 @@ impl Protobuf for Action { Value::FeeChange(act) => { Self::FeeChange(FeeChange::try_from_raw_ref(&act).map_err(Error::fee_change)?) } + Value::AddCurrencyPairs(act) => Self::AddCurrencyPairs( + AddCurrencyPairs::try_from_raw(act).map_err(Error::add_currency_pairs)?, + ), + Value::RemoveCurrencyPairs(act) => Self::RemoveCurrencyPairs( + RemoveCurrencyPairs::try_from_raw(act).map_err(Error::remove_currency_pairs)?, + ), }; Ok(action) } @@ -308,6 +322,8 @@ impl ActionName for Action { Action::BridgeUnlock(_) => "BridgeUnlock", Action::BridgeSudoChange(_) => "BridgeSudoChange", Action::FeeChange(_) => "FeeChange", + Action::AddCurrencyPairs(_) => "AddCurrencyPairs", + Action::RemoveCurrencyPairs(_) => "RemoveCurrencyPairs", } } } @@ -376,6 +392,14 @@ impl Error { fn fee_change(inner: FeeChangeError) -> Self { Self(ActionErrorKind::FeeChange(inner)) } + + fn add_currency_pairs(inner: AddCurrencyPairsError) -> Self { + Self(ActionErrorKind::AddCurrencyPairs(inner)) + } + + fn remove_currency_pairs(inner: RemoveCurrencyPairsError) -> Self { + Self(ActionErrorKind::RemoveCurrencyPairs(inner)) + } } #[derive(Debug, thiserror::Error)] @@ -410,6 +434,10 @@ enum ActionErrorKind { BridgeSudoChange(#[source] BridgeSudoChangeError), #[error("fee change action was not valid")] FeeChange(#[source] FeeChangeError), + #[error("add currency pairs action was not valid")] + AddCurrencyPairs(#[source] AddCurrencyPairsError), + #[error("remove currency pairs action was not valid")] + RemoveCurrencyPairs(#[source] RemoveCurrencyPairsError), } #[derive(Debug, thiserror::Error)] @@ -2072,3 +2100,139 @@ impl Protobuf for FeeChange { }) } } + +#[derive(Debug, Clone)] +pub struct AddCurrencyPairs { + pub pairs: Vec, +} + +impl Protobuf for AddCurrencyPairs { + type Error = AddCurrencyPairsError; + type Raw = raw::AddCurrencyPairs; + + #[must_use] + fn into_raw(self) -> raw::AddCurrencyPairs { + raw::AddCurrencyPairs { + pairs: self.pairs.into_iter().map(CurrencyPair::into_raw).collect(), + } + } + + #[must_use] + fn to_raw(&self) -> raw::AddCurrencyPairs { + raw::AddCurrencyPairs { + pairs: self.pairs.iter().map(CurrencyPair::to_raw).collect(), + } + } + + /// Convert from a raw, unchecked protobuf [`raw::AddCurrencyPairsAction`]. + /// + /// # Errors + /// + /// - if any of the `pairs` field is invalid + fn try_from_raw(proto: raw::AddCurrencyPairs) -> Result { + let pairs = proto + .pairs + .into_iter() + .map(CurrencyPair::try_from_raw) + .collect::>() + .map_err(AddCurrencyPairsError::invalid_currency_pair)?; + Ok(Self { + pairs, + }) + } + + /// Convert from a reference to a raw, unchecked protobuf [`raw::AddCurrencyPairsAction`]. + /// + /// # Errors + /// + /// - if any of the `pairs` field is invalid + fn try_from_raw_ref(proto: &raw::AddCurrencyPairs) -> Result { + Self::try_from_raw(proto.clone()) + } +} + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct AddCurrencyPairsError(AddCurrencyPairsErrorKind); + +impl AddCurrencyPairsError { + #[must_use] + fn invalid_currency_pair(err: CurrencyPairError) -> Self { + Self(AddCurrencyPairsErrorKind::InvalidCurrencyPair(err)) + } +} + +#[derive(Debug, thiserror::Error)] +enum AddCurrencyPairsErrorKind { + #[error("a currency pair was invalid")] + InvalidCurrencyPair(#[from] CurrencyPairError), +} + +#[derive(Debug, Clone)] +pub struct RemoveCurrencyPairs { + pub pairs: Vec, +} + +impl Protobuf for RemoveCurrencyPairs { + type Error = RemoveCurrencyPairsError; + type Raw = raw::RemoveCurrencyPairs; + + #[must_use] + fn into_raw(self) -> raw::RemoveCurrencyPairs { + raw::RemoveCurrencyPairs { + pairs: self.pairs.into_iter().map(CurrencyPair::into_raw).collect(), + } + } + + #[must_use] + fn to_raw(&self) -> raw::RemoveCurrencyPairs { + raw::RemoveCurrencyPairs { + pairs: self.pairs.iter().map(CurrencyPair::to_raw).collect(), + } + } + + /// Convert from a raw, unchecked protobuf [`raw::RemoveCurrencyPairsAction`]. + /// + /// # Errors + /// + /// - if any of the `pairs` field is invalid + fn try_from_raw(proto: raw::RemoveCurrencyPairs) -> Result { + let pairs = proto + .pairs + .into_iter() + .map(CurrencyPair::try_from_raw) + .collect::>() + .map_err(RemoveCurrencyPairsError::invalid_currency_pair)?; + Ok(Self { + pairs, + }) + } + + /// Convert from a reference to a raw, unchecked protobuf [`raw::RemoveCurrencyPairsAction`]. + /// + /// # Errors + /// + /// - if any of the `pairs` field is invalid + fn try_from_raw_ref( + proto: &raw::RemoveCurrencyPairs, + ) -> Result { + Self::try_from_raw(proto.clone()) + } +} + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct RemoveCurrencyPairsError(RemoveCurrencyPairsErrorKind); + +impl RemoveCurrencyPairsError { + #[must_use] + fn invalid_currency_pair(err: CurrencyPairError) -> Self { + Self(RemoveCurrencyPairsErrorKind::InvalidCurrencyPair(err)) + } +} + +#[derive(Debug, thiserror::Error)] +enum RemoveCurrencyPairsErrorKind { + #[error("a currency pair was invalid")] + InvalidCurrencyPair(#[from] CurrencyPairError), +} diff --git a/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs new file mode 100644 index 0000000000..617b39b535 --- /dev/null +++ b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs @@ -0,0 +1,23 @@ +use astria_core::protocol::transaction::v1::action::AddCurrencyPairs; +use astria_eyre::eyre::{ + ensure, + OptionExt as _, + Result, + WrapErr as _, +}; +use async_trait::async_trait; +use cnidarium::StateWrite; + +use crate::action_handler::ActionHandler; + +#[async_trait] +impl ActionHandler for AddCurrencyPairs { + async fn check_stateless(&self) -> Result<()> { + Ok(()) + } + + async fn check_and_execute(&self, mut state: S) -> Result<()> { + todo!(); + Ok(()) + } +} diff --git a/crates/astria-sequencer/src/action_handler/impls/mod.rs b/crates/astria-sequencer/src/action_handler/impls/mod.rs index c273cc26ff..b9fd6f599f 100644 --- a/crates/astria-sequencer/src/action_handler/impls/mod.rs +++ b/crates/astria-sequencer/src/action_handler/impls/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod add_currency_pairs; pub(crate) mod bridge_lock; pub(crate) mod bridge_sudo_change; pub(crate) mod bridge_unlock; diff --git a/crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs b/crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs new file mode 100644 index 0000000000..ba197c6880 --- /dev/null +++ b/crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs @@ -0,0 +1,31 @@ +use astria_core::{ + protocol::transaction::v1::action::{ + RemoveCurrencyPairs, + }, +}; +use astria_eyre::eyre::{ + ensure, + OptionExt as _, + Result, + WrapErr as _, +}; +use async_trait::async_trait; +use cnidarium::StateWrite; + +use crate::{ + action_handler::{ + ActionHandler, + }, +}; + +#[async_trait] +impl ActionHandler for RemoveCurrencyPairs { + async fn check_stateless(&self) -> Result<()> { + Ok(()) + } + + async fn check_and_execute(&self, mut state: S) -> Result<()> { + todo!(); + Ok(()) + } +} diff --git a/crates/astria-sequencer/src/action_handler/impls/transaction.rs b/crates/astria-sequencer/src/action_handler/impls/transaction.rs index 29df7cc10a..5ca7ee1c86 100644 --- a/crates/astria-sequencer/src/action_handler/impls/transaction.rs +++ b/crates/astria-sequencer/src/action_handler/impls/transaction.rs @@ -136,6 +136,14 @@ impl ActionHandler for Transaction { .check_stateless() .await .wrap_err("stateless check failed for BridgeSudoChange action")?, + Action::AddCurrencyPairs(act) => act + .check_stateless() + .await + .wrap_err("stateless check failed for AddCurrencyPairs action")?, + Action::RemoveCurrencyPairs(act) => act + .check_stateless() + .await + .wrap_err("stateless check failed for RemoveCurrencyPairs action")?, } } Ok(()) @@ -262,6 +270,12 @@ impl ActionHandler for Transaction { Action::BridgeSudoChange(act) => check_execute_and_pay_fees(act, &mut state) .await .wrap_err("failed executing bridge sudo change")?, + Action::AddCurrencyPairs(act) => check_execute_and_pay_fees(act, &mut state) + .await + .wrap_err("failed executing add currency pairs")?, + Action::RemoveCurrencyPairs(act) => check_execute_and_pay_fees(act, &mut state) + .await + .wrap_err("failed executing remove currency pairs")?, } } diff --git a/crates/astria-sequencer/src/transaction/checks.rs b/crates/astria-sequencer/src/transaction/checks.rs index 3e00c7b0f4..f85029d89c 100644 --- a/crates/astria-sequencer/src/transaction/checks.rs +++ b/crates/astria-sequencer/src/transaction/checks.rs @@ -114,7 +114,9 @@ pub(crate) async fn get_total_transaction_cost( | Action::Ibc(_) | Action::IbcRelayerChange(_) | Action::FeeAssetChange(_) - | Action::FeeChange(_) => { + | Action::FeeChange(_) + | Action::AddCurrencyPairs(_) + | Action::RemoveCurrencyPairs(_) => { continue; } } diff --git a/proto/protocolapis/astria/protocol/transaction/v1/action.proto b/proto/protocolapis/astria/protocol/transaction/v1/action.proto index 26cab7579d..9b7ec7db05 100644 --- a/proto/protocolapis/astria/protocol/transaction/v1/action.proto +++ b/proto/protocolapis/astria/protocol/transaction/v1/action.proto @@ -6,6 +6,7 @@ import "astria/primitive/v1/types.proto"; import "astria/protocol/fees/v1/types.proto"; import "astria_vendored/penumbra/core/component/ibc/v1/ibc.proto"; import "astria_vendored/tendermint/abci/types.proto"; +import "connect/types/v2/currency_pair.proto"; message Action { oneof value { @@ -30,6 +31,10 @@ message Action { FeeAssetChange fee_asset_change = 53; FeeChange fee_change = 55; IbcSudoChange ibc_sudo_change = 56; + + // Oracle actions are defined on 61-70 + AddCurrencyPairs add_currency_pairs = 61; + RemoveCurrencyPairs remove_currency_pairs = 62; } } @@ -234,3 +239,11 @@ message FeeChange { message IbcSudoChange { astria.primitive.v1.Address new_address = 1; } + +message AddCurrencyPairs { + repeated connect.types.v2.CurrencyPair pairs = 1; +} + +message RemoveCurrencyPairs { + repeated connect.types.v2.CurrencyPair pairs = 1; +} From ebe0340df45e97f07cf37cb1f786b34879480f2c Mon Sep 17 00:00:00 2001 From: elizabeth Date: Thu, 12 Dec 2024 19:14:07 -0500 Subject: [PATCH 2/7] impl Add/RemoveCurrencyPairs --- .../impls/add_currency_pairs.rs | 93 +++++++++++++++- .../impls/remove_currency_pairs.rs | 51 ++++++++- .../src/app/vote_extension.rs | 21 ++-- .../src/connect/oracle/component.rs | 3 - .../connect/oracle/currency_pair_strategy.rs | 23 ++-- .../src/connect/oracle/state_ext.rs | 101 ++++++------------ .../src/connect/oracle/storage/keys.rs | 4 - 7 files changed, 187 insertions(+), 109 deletions(-) diff --git a/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs index 617b39b535..3c03660760 100644 --- a/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs +++ b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs @@ -1,4 +1,18 @@ -use astria_core::protocol::transaction::v1::action::AddCurrencyPairs; +use astria_core::{ + connect::{ + oracle::v2::{ + CurrencyPairState, + QuotePrice, + }, + types::v2::{ + CurrencyPairId, + CurrencyPairNonce, + Price, + }, + }, + protocol::transaction::v1::action::AddCurrencyPairs, + Timestamp, +}; use astria_eyre::eyre::{ ensure, OptionExt as _, @@ -8,7 +22,18 @@ use astria_eyre::eyre::{ use async_trait::async_trait; use cnidarium::StateWrite; -use crate::action_handler::ActionHandler; +use crate::{ + action_handler::ActionHandler, + app::StateReadExt as _, + connect::{ + market_map::state_ext::StateReadExt as _, + oracle::state_ext::{ + StateReadExt as _, + StateWriteExt, + }, + }, + transaction::StateReadExt as _, +}; #[async_trait] impl ActionHandler for AddCurrencyPairs { @@ -17,7 +42,69 @@ impl ActionHandler for AddCurrencyPairs { } async fn check_and_execute(&self, mut state: S) -> Result<()> { - todo!(); + // TODO: should we use the market map admin here, or a different admin? + let admin = state + .get_params() + .await? + .ok_or_eyre("market map params not set")? + .admin; + let from = state + .get_transaction_context() + .expect("transaction source must be present in state when executing an action") + .address_bytes(); + ensure!( + from == admin.bytes(), + "only the market map admin can add currency pairs" + ); + + let next_currency_pair_id = state + .get_next_currency_pair_id() + .await + .wrap_err("failed to get next currency pair id")?; + let num_currency_pairs = state + .get_num_currency_pairs() + .await + .wrap_err("failed to get number of currency pairs")?; + let timestamp: tendermint_proto::google::protobuf::Timestamp = state + .get_block_timestamp() + .await + .wrap_err("failed to get block timestamp")? + .into(); + + for pair in &self.pairs { + let currency_pair_state = CurrencyPairState { + price: QuotePrice { + price: Price::new(0), + block_timestamp: Timestamp { + seconds: timestamp.seconds, + nanos: timestamp.nanos, + }, + block_height: state.get_block_height().await?, + }, + nonce: CurrencyPairNonce::new(0), + id: next_currency_pair_id, + }; + state + .put_currency_pair_state(pair.clone(), currency_pair_state) + .wrap_err("failed to put currency pair state")?; + num_currency_pairs + .checked_add(1) + .ok_or_eyre("overflow when incrementing number of currency pairs")?; + } + + state + .put_next_currency_pair_id(CurrencyPairId::new(num_currency_pairs)) + .wrap_err("failed to put next currency pair id")?; + state + .put_num_currency_pairs( + num_currency_pairs.saturating_add( + self.pairs + .len() + .try_into() + .expect("number of pairs cannot exceed u64::MAX"), + ), + ) + .wrap_err("failed to put number of currency pairs")?; Ok(()) } } diff --git a/crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs b/crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs index 6caea48d53..73a1267922 100644 --- a/crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs +++ b/crates/astria-sequencer/src/action_handler/impls/remove_currency_pairs.rs @@ -8,7 +8,17 @@ use astria_eyre::eyre::{ use async_trait::async_trait; use cnidarium::StateWrite; -use crate::action_handler::ActionHandler; +use crate::{ + action_handler::ActionHandler, + connect::{ + market_map::state_ext::StateReadExt as _, + oracle::state_ext::{ + StateReadExt as _, + StateWriteExt, + }, + }, + transaction::StateReadExt as _, +}; #[async_trait] impl ActionHandler for RemoveCurrencyPairs { @@ -17,7 +27,44 @@ impl ActionHandler for RemoveCurrencyPairs { } async fn check_and_execute(&self, mut state: S) -> Result<()> { - todo!(); + // TODO: should we use the market map admin here, or a different admin? + let admin = state + .get_params() + .await? + .ok_or_eyre("market map params not set")? + .admin; + let from = state + .get_transaction_context() + .expect("transaction source must be present in state when executing an action") + .address_bytes(); + ensure!( + from == admin.bytes(), + "only the market map admin can add currency pairs" + ); + + let num_currency_pairs = state + .get_num_currency_pairs() + .await + .wrap_err("failed to get number of currency pairs")?; + ensure!( + num_currency_pairs >= self.pairs.len() as u64, + "cannot remove more currency pairs than exist", + ); + + for pair in &self.pairs { + state + .delete_currency_pair(pair) + .await + .wrap_err("failed to delete currency pair")?; + num_currency_pairs + .checked_sub(1) + .ok_or_eyre("failed to decrement number of currency pairs")?; + } + + state + .put_num_currency_pairs(num_currency_pairs) + .wrap_err("failed to put number of currency pairs")?; + Ok(()) } } diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index 1e5d4b2a8f..dc7794b937 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -123,10 +123,9 @@ impl Handler { return Ok(abci::response::VerifyVoteExtension::Accept); } - 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 max_num_currency_pairs = DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state) + .await + .wrap_err("failed to get max number of currency pairs")?; let response = match verify_vote_extension(vote.vote_extension, max_num_currency_pairs) { Ok(_) => abci::response::VerifyVoteExtension::Accept, @@ -225,10 +224,9 @@ impl ProposalHandler { )); } - 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 max_num_currency_pairs = DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state) + .await + .wrap_err("failed to get max number of currency pairs")?; let mut all_currency_pair_ids = HashSet::new(); for vote in &mut extended_commit_info.votes { @@ -310,10 +308,9 @@ 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 max_num_currency_pairs = DefaultCurrencyPairStrategy::get_max_num_currency_pairs(state) + .await + .wrap_err("failed to get max number of currency pairs")?; let mut all_currency_pair_ids = HashSet::new(); for vote in &extended_commit_info.votes { diff --git a/crates/astria-sequencer/src/connect/oracle/component.rs b/crates/astria-sequencer/src/connect/oracle/component.rs index 8cfc1824f1..a9c9b6c2fe 100644 --- a/crates/astria-sequencer/src/connect/oracle/component.rs +++ b/crates/astria-sequencer/src/connect/oracle/component.rs @@ -47,9 +47,6 @@ impl Component for OracleComponent { 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")?; } Ok(()) } diff --git a/crates/astria-sequencer/src/connect/oracle/currency_pair_strategy.rs b/crates/astria-sequencer/src/connect/oracle/currency_pair_strategy.rs index 3e21daddc8..27bc777ce3 100644 --- a/crates/astria-sequencer/src/connect/oracle/currency_pair_strategy.rs +++ b/crates/astria-sequencer/src/connect/oracle/currency_pair_strategy.rs @@ -27,23 +27,14 @@ impl DefaultCurrencyPairStrategy { state.get_currency_pair(id).await } - pub(crate) async fn get_max_num_currency_pairs( - state: &S, - is_proposal_phase: bool, - ) -> Result { - let current = state + pub(crate) async fn get_max_num_currency_pairs(state: &S) -> Result { + // unlike the skip implementation, we don't need to track removed currency pairs + // from the previous block as we execute our transactions during the proposal phase, + // before vote extensions are broadcast. thus by the time we're making our VE, we + // already have the updated state for that block. + state .get_num_currency_pairs() .await - .wrap_err("failed to get number of currency pairs")?; - - if is_proposal_phase { - let removed = state - .get_num_removed_currency_pairs() - .await - .wrap_err("failed to get number of removed currency pairs")?; - Ok(current.saturating_add(removed)) - } else { - Ok(current) - } + .wrap_err("failed to get number of currency pairs") } } diff --git a/crates/astria-sequencer/src/connect/oracle/state_ext.rs b/crates/astria-sequencer/src/connect/oracle/state_ext.rs index 19b16f543a..a9ab7b858a 100644 --- a/crates/astria-sequencer/src/connect/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/connect/oracle/state_ext.rs @@ -195,21 +195,6 @@ pub(crate) trait StateReadExt: StateRead { .wrap_err("invalid number of currency pairs bytes") } - #[instrument(skip_all)] - async fn get_num_removed_currency_pairs(&self) -> Result { - let Some(bytes) = self - .get_raw(keys::NUM_REMOVED_CURRENCY_PAIRS) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading number of removed currency pairs from state")? - else { - return Ok(0); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| storage::Count::try_from(value).map(u64::from)) - .wrap_err("invalid number of removed currency pairs bytes") - } - #[instrument(skip_all)] async fn get_currency_pair_state( &self, @@ -230,6 +215,21 @@ pub(crate) trait StateReadExt: StateRead { }) .wrap_err("invalid currency pair state bytes") } + + #[instrument(skip_all)] + async fn get_next_currency_pair_id(&self) -> Result { + let Some(bytes) = self + .get_raw(keys::NEXT_CURRENCY_PAIR_ID) + .await + .map_err(anyhow_to_eyre) + .wrap_err("failed reading next currency pair id from state")? + else { + return Ok(CurrencyPairId::new(0)); + }; + StoredValue::deserialize(&bytes) + .and_then(|value| storage::CurrencyPairId::try_from(value).map(CurrencyPairId::from)) + .wrap_err("invalid next currency pair id bytes") + } } impl StateReadExt for T {} @@ -245,15 +245,6 @@ pub(crate) trait StateWriteExt: StateWrite { Ok(()) } - #[instrument(skip_all)] - fn put_num_removed_currency_pairs(&mut self, num_removed_currency_pairs: u64) -> Result<()> { - let bytes = StoredValue::from(storage::Count::from(num_removed_currency_pairs)) - .serialize() - .wrap_err("failed to serialize number of removed currency pairs")?; - self.put_raw(keys::NUM_REMOVED_CURRENCY_PAIRS.to_string(), bytes); - Ok(()) - } - #[instrument(skip_all)] fn put_currency_pair_state( &mut self, @@ -299,7 +290,8 @@ pub(crate) trait StateWriteExt: StateWrite { .ok_or_eyre("increment nonce overflowed")?; state } else { - let id = get_next_currency_pair_id(self) + let id = self + .get_next_currency_pair_id() .await .wrap_err("failed to read next currency pair ID")?; let next_id = id.increment().wrap_err("increment ID overflowed")?; @@ -314,25 +306,25 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_currency_pair_state(currency_pair, state) .wrap_err("failed to put currency pair state") } + + #[instrument(skip_all)] + async fn delete_currency_pair(&mut self, currency_pair: &CurrencyPair) -> Result<()> { + let Some(id) = self + .get_currency_pair_id(currency_pair) + .await + .wrap_err("failed to get currency pair ID")? + else { + return Ok(()); + }; + self.delete(keys::currency_pair_to_id(currency_pair)); + self.delete(keys::id_to_currency_pair(id)); + self.delete(keys::currency_pair_state(currency_pair)); + Ok(()) + } } impl StateWriteExt for T {} -#[instrument(skip_all)] -async fn get_next_currency_pair_id(state: &T) -> Result { - let Some(bytes) = state - .get_raw(keys::NEXT_CURRENCY_PAIR_ID) - .await - .map_err(anyhow_to_eyre) - .wrap_err("failed reading next currency pair id from state")? - else { - return Ok(CurrencyPairId::new(0)); - }; - StoredValue::deserialize(&bytes) - .and_then(|value| storage::CurrencyPairId::try_from(value).map(CurrencyPairId::from)) - .wrap_err("invalid next currency pair id bytes") -} - #[instrument(skip_all)] fn put_currency_pair_id( state: &mut T, @@ -557,33 +549,4 @@ mod tests { .expect("should not error"); assert_eq!(2, retrieved_count); } - - #[tokio::test] - async fn should_put_and_get_num_removed_currency_pairs() { - let storage = cnidarium::TempStorage::new().await.unwrap(); - let snapshot = storage.latest_snapshot(); - let mut state = StateDelta::new(snapshot); - - // Getting should return `0` when no count is stored. - assert_eq!(state.get_num_removed_currency_pairs().await.unwrap(), 0); - - // Putting a count should succeed. - state.put_num_removed_currency_pairs(1).unwrap(); - - // Getting the stored count should succeed. - let retrieved_count = state - .get_num_removed_currency_pairs() - .await - .expect("should not error"); - assert_eq!(1, retrieved_count); - - // Putting a new count should overwrite the first. - state.put_num_removed_currency_pairs(2).unwrap(); - - let retrieved_count = state - .get_num_removed_currency_pairs() - .await - .expect("should not error"); - assert_eq!(2, retrieved_count); - } } diff --git a/crates/astria-sequencer/src/connect/oracle/storage/keys.rs b/crates/astria-sequencer/src/connect/oracle/storage/keys.rs index b37e2493ac..bb81fdf22c 100644 --- a/crates/astria-sequencer/src/connect/oracle/storage/keys.rs +++ b/crates/astria-sequencer/src/connect/oracle/storage/keys.rs @@ -16,8 +16,6 @@ pub(in crate::connect::oracle) const CURRENCY_PAIR_STATE_PREFIX: &str = "connect/oracle/currency_pair_state/"; pub(in crate::connect::oracle) const NUM_CURRENCY_PAIRS: &str = "connect/oracle/num_currency_pairs"; -pub(in crate::connect::oracle) const NUM_REMOVED_CURRENCY_PAIRS: &str = - "connect/oracle/num_removed_currency_pairs"; pub(in crate::connect::oracle) const NEXT_CURRENCY_PAIR_ID: &str = "connect/oracle/next_currency_pair_id"; @@ -83,7 +81,6 @@ mod tests { #[test] fn keys_should_not_change() { insta::assert_snapshot!("num_currency_pairs_key", NUM_CURRENCY_PAIRS); - insta::assert_snapshot!("num_removed_currency_pairs_key", NUM_REMOVED_CURRENCY_PAIRS); insta::assert_snapshot!("next_currency_pair_id_key", NEXT_CURRENCY_PAIR_ID); insta::assert_snapshot!( "currency_pair_to_id_key", @@ -102,7 +99,6 @@ mod tests { #[test] fn keys_should_have_component_prefix() { assert!(NUM_CURRENCY_PAIRS.starts_with(COMPONENT_PREFIX)); - assert!(NUM_REMOVED_CURRENCY_PAIRS.starts_with(COMPONENT_PREFIX)); assert!(NEXT_CURRENCY_PAIR_ID.starts_with(COMPONENT_PREFIX)); assert!(currency_pair_to_id(¤cy_pair()).starts_with(COMPONENT_PREFIX)); assert!(id_to_currency_pair(CurrencyPairId::new(9)).starts_with(COMPONENT_PREFIX)); From 3cc37df2a50e131cb3ef547aa602d90a0b4b014e Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 13 Dec 2024 10:17:13 -0500 Subject: [PATCH 3/7] remove num_removed_currency_pairs as we don't need it --- crates/astria-core/src/connect/oracle.rs | 32 +++++-------------- crates/astria-core/src/protocol/genesis/v1.rs | 4 +-- .../impls/add_currency_pairs.rs | 20 +++++++----- .../src/app/vote_extension.rs | 4 +-- .../src/connect/oracle/state_ext.rs | 8 ++--- .../storage/values/currency_pair_state.rs | 6 ++-- 6 files changed, 31 insertions(+), 43 deletions(-) diff --git a/crates/astria-core/src/connect/oracle.rs b/crates/astria-core/src/connect/oracle.rs index bfed0016dd..617ef50fd9 100644 --- a/crates/astria-core/src/connect/oracle.rs +++ b/crates/astria-core/src/connect/oracle.rs @@ -94,7 +94,7 @@ pub mod v2 { #[derive(Debug, Clone, PartialEq)] pub struct CurrencyPairState { - pub price: QuotePrice, + pub price: Option, pub nonce: CurrencyPairNonce, pub id: CurrencyPairId, } @@ -121,14 +121,11 @@ pub mod v2 { /// - 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 + let price = raw .price .map(QuotePrice::try_from_raw) .transpose() - .map_err(CurrencyPairStateError::quote_price_parse_error)? - else { - return Err(CurrencyPairStateError::missing_price()); - }; + .map_err(CurrencyPairStateError::quote_price_parse_error)?; let nonce = CurrencyPairNonce::new(raw.nonce); let id = CurrencyPairId::new(raw.id); Ok(Self { @@ -141,7 +138,7 @@ pub mod v2 { #[must_use] pub fn into_raw(self) -> raw::CurrencyPairState { raw::CurrencyPairState { - price: Some(self.price.into_raw()), + price: self.price.map(QuotePrice::into_raw), nonce: self.nonce.get(), id: self.id.get(), } @@ -153,11 +150,6 @@ pub mod v2 { pub struct CurrencyPairStateError(CurrencyPairStateErrorKind); impl CurrencyPairStateError { - #[must_use] - fn missing_price() -> Self { - Self(CurrencyPairStateErrorKind::MissingPrice) - } - #[must_use] fn quote_price_parse_error(err: QuotePriceError) -> Self { Self(CurrencyPairStateErrorKind::QuotePriceParseError(err)) @@ -166,8 +158,6 @@ pub mod v2 { #[derive(Debug, thiserror::Error)] enum CurrencyPairStateErrorKind { - #[error("missing price")] - MissingPrice, #[error("failed to parse quote price")] QuotePriceParseError(#[source] QuotePriceError), } @@ -175,7 +165,7 @@ pub mod v2 { #[derive(Debug, Clone)] pub struct CurrencyPairGenesis { pub currency_pair: CurrencyPair, - pub currency_pair_price: QuotePrice, + pub currency_pair_price: Option, pub id: CurrencyPairId, pub nonce: CurrencyPairNonce, } @@ -201,7 +191,7 @@ pub mod v2 { } #[must_use] - pub fn currency_pair_price(&self) -> &QuotePrice { + pub fn currency_pair_price(&self) -> &Option { &self.currency_pair_price } @@ -232,13 +222,7 @@ pub mod v2 { .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 currency_pair_price = raw.currency_pair_price.map(QuotePrice::try_from_raw).transpose().map_err(CurrencyPairGenesisError::currency_pair_price)?; let id = CurrencyPairId::new(raw.id); let nonce = CurrencyPairNonce::new(raw.nonce); @@ -254,7 +238,7 @@ pub mod v2 { 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()), + currency_pair_price: self.currency_pair_price.map(QuotePrice::into_raw), id: self.id.get(), nonce: self.nonce.get(), } diff --git a/crates/astria-core/src/protocol/genesis/v1.rs b/crates/astria-core/src/protocol/genesis/v1.rs index 3ab1c0fc7b..283bbcb10d 100644 --- a/crates/astria-core/src/protocol/genesis/v1.rs +++ b/crates/astria-core/src/protocol/genesis/v1.rs @@ -1165,14 +1165,14 @@ mod tests { currency_pair_genesis: vec![CurrencyPairGenesis { id: CurrencyPairId::new(1), nonce: CurrencyPairNonce::new(0), - currency_pair_price: QuotePrice { + currency_pair_price: Some(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(), diff --git a/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs index 3c03660760..481405334e 100644 --- a/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs +++ b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs @@ -21,6 +21,7 @@ use astria_eyre::eyre::{ }; use async_trait::async_trait; use cnidarium::StateWrite; +use tracing::debug; use crate::{ action_handler::ActionHandler, @@ -72,15 +73,18 @@ impl ActionHandler for AddCurrencyPairs { .into(); for pair in &self.pairs { + if state + .get_currency_pair_state(pair) + .await + .wrap_err("failed to get currency pair state")? + .is_some() + { + debug!("currency pair {} already exists, skipping", pair); + continue; + } + let currency_pair_state = CurrencyPairState { - price: QuotePrice { - price: Price::new(0), - block_timestamp: Timestamp { - seconds: timestamp.seconds, - nanos: timestamp.nanos, - }, - block_height: state.get_block_height().await?, - }, + price: None, nonce: CurrencyPairNonce::new(0), id: next_currency_pair_id, }; diff --git a/crates/astria-sequencer/src/app/vote_extension.rs b/crates/astria-sequencer/src/app/vote_extension.rs index dc7794b937..3b7160cc35 100644 --- a/crates/astria-sequencer/src/app/vote_extension.rs +++ b/crates/astria-sequencer/src/app/vote_extension.rs @@ -846,14 +846,14 @@ mod test { for (pair, pair_id) in [pair_0(), pair_1(), pair_2()] { let pair_state = CurrencyPairState { - price: QuotePrice { + price: Some(QuotePrice { price: Price::new(123), block_timestamp: Timestamp { seconds: 4, nanos: 5, }, block_height: 1, - }, + }), nonce: CurrencyPairNonce::new(1), id: pair_id, }; diff --git a/crates/astria-sequencer/src/connect/oracle/state_ext.rs b/crates/astria-sequencer/src/connect/oracle/state_ext.rs index a9ab7b858a..c7f1f98c65 100644 --- a/crates/astria-sequencer/src/connect/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/connect/oracle/state_ext.rs @@ -283,7 +283,7 @@ pub(crate) trait StateWriteExt: StateWrite { .await .wrap_err("failed to get currency pair state")? { - state.price = price; + state.price = Some(price); state.nonce = state .nonce .increment() @@ -298,7 +298,7 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_next_currency_pair_id(next_id) .wrap_err("failed to put next currency pair ID")?; CurrencyPairState { - price, + Some(price), nonce: CurrencyPairNonce::new(0), id, } @@ -383,14 +383,14 @@ mod tests { fn currency_pair_state(id: u64, nonce: u64) -> CurrencyPairState { CurrencyPairState { - price: QuotePrice { + price: Some(QuotePrice { price: Price::new(123), block_timestamp: Timestamp { seconds: 4, nanos: 5, }, block_height: nonce.checked_add(10).unwrap(), - }, + }), nonce: CurrencyPairNonce::new(nonce), id: CurrencyPairId::new(id), } diff --git a/crates/astria-sequencer/src/connect/oracle/storage/values/currency_pair_state.rs b/crates/astria-sequencer/src/connect/oracle/storage/values/currency_pair_state.rs index 98b3cd7f6f..6dc19ad75f 100644 --- a/crates/astria-sequencer/src/connect/oracle/storage/values/currency_pair_state.rs +++ b/crates/astria-sequencer/src/connect/oracle/storage/values/currency_pair_state.rs @@ -77,7 +77,7 @@ impl From for DomainQuotePrice { #[derive(Debug, BorshSerialize, BorshDeserialize)] pub(in crate::connect::oracle) struct CurrencyPairState { - price: QuotePrice, + price: Option, nonce: u64, id: CurrencyPairId, } @@ -85,7 +85,7 @@ pub(in crate::connect::oracle) struct CurrencyPairState { impl From for CurrencyPairState { fn from(state: DomainCurrencyPairState) -> Self { CurrencyPairState { - price: QuotePrice::from(state.price), + price: state.price.map(QuotePrice::from), nonce: state.nonce.get(), id: CurrencyPairId::from(state.id), } @@ -95,7 +95,7 @@ impl From for CurrencyPairState { impl From for DomainCurrencyPairState { fn from(state: CurrencyPairState) -> Self { Self { - price: DomainQuotePrice::from(state.price), + price: state.price.map(DomainQuotePrice::from), nonce: CurrencyPairNonce::new(state.nonce), id: DomainCurrencyPairId::from(state.id), } From 533c83855d86aa13b4f8d564e835c34f79abb79c Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 13 Dec 2024 11:55:53 -0500 Subject: [PATCH 4/7] fix clippy --- crates/astria-core/src/connect/oracle.rs | 6 +++++- .../action_handler/impls/add_currency_pairs.rs | 15 ++------------- .../src/app/benchmark_and_test_utils.rs | 1 + .../src/connect/oracle/state_ext.rs | 2 +- crates/astria-sequencer/src/grpc/connect.rs | 8 ++++++-- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/crates/astria-core/src/connect/oracle.rs b/crates/astria-core/src/connect/oracle.rs index 617ef50fd9..70f1bea115 100644 --- a/crates/astria-core/src/connect/oracle.rs +++ b/crates/astria-core/src/connect/oracle.rs @@ -222,7 +222,11 @@ pub mod v2 { .ok_or_else(|| CurrencyPairGenesisError::field_not_set("currency_pair"))? .try_into() .map_err(CurrencyPairGenesisError::currency_pair)?; - let currency_pair_price = raw.currency_pair_price.map(QuotePrice::try_from_raw).transpose().map_err(CurrencyPairGenesisError::currency_pair_price)?; + let currency_pair_price = raw + .currency_pair_price + .map(QuotePrice::try_from_raw) + .transpose() + .map_err(CurrencyPairGenesisError::currency_pair_price)?; let id = CurrencyPairId::new(raw.id); let nonce = CurrencyPairNonce::new(raw.nonce); diff --git a/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs index 481405334e..2173c0988f 100644 --- a/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs +++ b/crates/astria-sequencer/src/action_handler/impls/add_currency_pairs.rs @@ -1,17 +1,12 @@ use astria_core::{ connect::{ - oracle::v2::{ - CurrencyPairState, - QuotePrice, - }, + oracle::v2::CurrencyPairState, types::v2::{ CurrencyPairId, CurrencyPairNonce, - Price, }, }, protocol::transaction::v1::action::AddCurrencyPairs, - Timestamp, }; use astria_eyre::eyre::{ ensure, @@ -25,12 +20,11 @@ use tracing::debug; use crate::{ action_handler::ActionHandler, - app::StateReadExt as _, connect::{ market_map::state_ext::StateReadExt as _, oracle::state_ext::{ StateReadExt as _, - StateWriteExt, + StateWriteExt as _, }, }, transaction::StateReadExt as _, @@ -66,11 +60,6 @@ impl ActionHandler for AddCurrencyPairs { .get_num_currency_pairs() .await .wrap_err("failed to get number of currency pairs")?; - let timestamp: tendermint_proto::google::protobuf::Timestamp = state - .get_block_timestamp() - .await - .wrap_err("failed to get block timestamp")? - .into(); for pair in &self.pairs { if state 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 8a7ad0648f..c0475c6d14 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -279,6 +279,7 @@ pub(crate) fn mock_state_put_account_nonce( state.put_account_nonce(address, nonce).unwrap(); } +#[expect(clippy::too_many_lines, reason = "this is needed for test set up")] pub(crate) async fn mock_state_getter() -> StateDelta { let storage = cnidarium::TempStorage::new().await.unwrap(); let snapshot = storage.latest_snapshot(); diff --git a/crates/astria-sequencer/src/connect/oracle/state_ext.rs b/crates/astria-sequencer/src/connect/oracle/state_ext.rs index c7f1f98c65..529b3dc462 100644 --- a/crates/astria-sequencer/src/connect/oracle/state_ext.rs +++ b/crates/astria-sequencer/src/connect/oracle/state_ext.rs @@ -298,7 +298,7 @@ pub(crate) trait StateWriteExt: StateWrite { self.put_next_currency_pair_id(next_id) .wrap_err("failed to put next currency pair ID")?; CurrencyPairState { - Some(price), + price: Some(price), nonce: CurrencyPairNonce::new(0), id, } diff --git a/crates/astria-sequencer/src/grpc/connect.rs b/crates/astria-sequencer/src/grpc/connect.rs index 2ebb6c77ac..ea4efa7a8e 100644 --- a/crates/astria-sequencer/src/grpc/connect.rs +++ b/crates/astria-sequencer/src/grpc/connect.rs @@ -206,7 +206,9 @@ impl OracleService for SequencerServer { }; Ok(Response::new(GetPriceResponse { - price: Some(state.price.into_raw()), + price: state + .price + .map(astria_core::connect::oracle::v2::QuotePrice::into_raw), nonce: state.nonce.get(), id: state.id.get(), decimals: market.ticker.decimals, @@ -260,7 +262,9 @@ impl OracleService for SequencerServer { }; prices.push(GetPriceResponse { - price: Some(state.price.into_raw()), + price: state + .price + .map(astria_core::connect::oracle::v2::QuotePrice::into_raw), nonce: state.nonce.get(), id: state.id.get(), decimals: market.ticker.decimals, From 1d909ec322402e37d985ab6ae984e279e83a303b Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 13 Dec 2024 12:33:48 -0500 Subject: [PATCH 5/7] unit tests --- .../src/protocol/transaction/v1/action/mod.rs | 12 ++++ .../src/app/benchmark_and_test_utils.rs | 4 +- ...breaking_changes__app_hash_at_genesis.snap | 63 +++++++++--------- ...hanges__app_hash_execute_every_action.snap | 63 +++++++++--------- ...king_changes__app_hash_finalize_block.snap | 61 +++++++++-------- .../src/app/tests_execute_transaction.rs | 65 ++++++++++++++++++- crates/astria-sequencer/src/fees/component.rs | 14 ++++ .../src/service/mempool/tests.rs | 10 +-- 8 files changed, 189 insertions(+), 103 deletions(-) diff --git a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs index b579c63b64..95d6a007d6 100644 --- a/crates/astria-core/src/protocol/transaction/v1/action/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1/action/mod.rs @@ -272,6 +272,18 @@ impl From for Action { } } +impl From for Action { + fn from(value: AddCurrencyPairs) -> Self { + Self::AddCurrencyPairs(value) + } +} + +impl From for Action { + fn from(value: RemoveCurrencyPairs) -> Self { + Self::RemoveCurrencyPairs(value) + } +} + impl From for raw::Action { fn from(value: Action) -> Self { value.into_raw() 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 c0475c6d14..ceeeb57486 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -144,7 +144,7 @@ pub(crate) fn proto_genesis_state() } } -pub(crate) fn genesis_state() -> GenesisAppState { +pub(crate) fn get_test_genesis_state() -> GenesisAppState { proto_genesis_state().try_into().unwrap() } @@ -163,7 +163,7 @@ pub(crate) async fn initialize_app_with_storage( .await .unwrap(); - let genesis_state = genesis_state.unwrap_or_else(self::genesis_state); + let genesis_state = genesis_state.unwrap_or_else(get_test_genesis_state); app.init_chain( storage.clone(), diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap index f6c9833e83..902b74b9a3 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap @@ -1,39 +1,38 @@ --- source: crates/astria-sequencer/src/app/tests_breaking_changes.rs -assertion_line: 78 expression: app.app_hash.as_bytes() --- [ - 53, - 138, - 44, - 156, - 111, - 243, - 242, - 225, - 251, - 114, - 33, - 182, - 11, - 173, - 107, - 58, + 157, + 13, + 132, + 3, + 217, + 162, + 227, + 248, + 73, + 118, + 68, + 236, + 34, + 159, + 87, + 5, + 85, + 41, + 161, 204, - 168, - 230, - 127, - 25, - 143, - 211, - 168, - 91, - 18, - 167, - 229, - 221, - 230, - 107, - 85 + 145, + 154, + 238, + 97, + 139, + 220, + 41, + 164, + 135, + 194, + 179, + 87 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap index b8bbde8d49..ef11b69906 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap @@ -1,39 +1,38 @@ --- source: crates/astria-sequencer/src/app/tests_breaking_changes.rs -assertion_line: 353 expression: app.app_hash.as_bytes() --- [ - 36, - 17, - 125, - 110, - 25, - 11, - 235, - 211, - 94, - 235, - 13, - 113, - 63, - 150, - 42, - 51, - 123, - 133, - 22, - 198, - 180, - 136, - 152, - 182, + 65, + 196, + 122, + 216, + 249, + 102, + 27, + 156, + 222, + 203, + 66, + 178, + 168, + 59, + 1, + 214, + 143, + 185, + 29, + 138, + 161, + 109, + 71, + 78, + 249, + 62, + 16, + 107, + 192, + 151, 124, - 193, - 188, - 48, - 9, - 215, - 118, - 202 + 20 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap index 051cc94ebd..f3bff68358 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap @@ -1,39 +1,38 @@ --- source: crates/astria-sequencer/src/app/tests_breaking_changes.rs -assertion_line: 160 expression: app.app_hash.as_bytes() --- [ - 16, - 234, - 26, - 31, - 30, - 140, - 161, - 180, - 247, - 182, - 185, - 153, - 253, + 150, + 81, + 50, + 231, + 107, 236, - 69, - 32, - 60, - 164, - 33, - 20, - 135, - 188, - 98, + 27, + 6, + 41, + 178, 107, - 198, - 164, - 95, - 223, - 50, - 124, - 146, - 122 + 36, + 215, + 193, + 86, + 129, + 159, + 216, + 205, + 186, + 209, + 114, + 210, + 24, + 78, + 74, + 36, + 151, + 35, + 133, + 176, + 209 ] diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 7f5d778e36..90de4e8871 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -1,6 +1,10 @@ -use std::sync::Arc; +use std::{ + str::FromStr, + sync::Arc, +}; use astria_core::{ + connect::types::v2::CurrencyPair, crypto::SigningKey, primitive::v1::{ asset, @@ -12,10 +16,12 @@ use astria_core::{ genesis::v1::GenesisAppState, transaction::v1::{ action::{ + AddCurrencyPairs, BridgeLock, BridgeUnlock, IbcRelayerChange, IbcSudoChange, + RemoveCurrencyPairs, RollupDataSubmission, SudoAddressChange, Transfer, @@ -33,6 +39,7 @@ use cnidarium::{ ArcStateDeltaExt as _, StateDelta, }; +use futures::StreamExt as _; use super::test_utils::get_alice_signing_key; use crate::{ @@ -64,6 +71,7 @@ use crate::{ StateReadExt as _, StateWriteExt as _, }, + connect::oracle::state_ext::StateReadExt, fees::{ StateReadExt as _, StateWriteExt as _, @@ -1325,3 +1333,58 @@ async fn ensure_all_event_attributes_are_indexed() { ); }); } + +#[tokio::test] +async fn test_app_execute_transaction_add_and_remove_currency_pairs() { + let alice = get_alice_signing_key(); + let alice_address = astria_address(&alice.address_bytes()); + + let mut app = initialize_app(Some(genesis_state()), vec![]).await; + + let currency_pair = CurrencyPair::from_str("TIA/USD").unwrap(); + + let tx = TransactionBody::builder() + .actions(vec![ + AddCurrencyPairs { + pairs: vec![currency_pair.clone()], + } + .into(), + ]) + .chain_id("test") + .try_build() + .unwrap(); + + let signed_tx = Arc::new(tx.sign(&alice)); + app.execute_transaction(signed_tx).await.unwrap(); + assert_eq!( + app.state.get_account_nonce(&alice_address).await.unwrap(), + 1 + ); + + let currency_pairs: Vec> = + app.state.currency_pairs().collect().await; + assert_eq!(currency_pairs.len(), 1); + assert_eq!(currency_pairs[0].as_ref().unwrap(), ¤cy_pair); + + let tx = TransactionBody::builder() + .actions(vec![ + RemoveCurrencyPairs { + pairs: vec![currency_pair.clone()], + } + .into(), + ]) + .chain_id("test") + .try_build() + .unwrap(); + + let signed_tx = Arc::new(tx.sign(&alice)); + app.execute_transaction(signed_tx).await.unwrap(); + assert_eq!( + app.state.get_account_nonce(&alice_address).await.unwrap(), + 2 + ); + + let currency_pairs: Vec> = + app.state.currency_pairs().collect().await; + assert_eq!(currency_pairs.len(), 0); +} diff --git a/crates/astria-sequencer/src/fees/component.rs b/crates/astria-sequencer/src/fees/component.rs index e22e4480d7..9b9120e051 100644 --- a/crates/astria-sequencer/src/fees/component.rs +++ b/crates/astria-sequencer/src/fees/component.rs @@ -130,6 +130,20 @@ impl Component for FeesComponent { .wrap_err("failed to store ibc sudo change fee components")?; } + let add_currency_pairs_fees = app_state.fees().add_currency_pairs; + if let Some(add_currency_pairs_fees) = add_currency_pairs_fees { + state + .put_fees(add_currency_pairs_fees) + .wrap_err("failed to store add currency pairs fee components")?; + } + + let remove_currency_pairs_fees = app_state.fees().remove_currency_pairs; + if let Some(remove_currency_pairs_fees) = remove_currency_pairs_fees { + state + .put_fees(remove_currency_pairs_fees) + .wrap_err("failed to store remove currency pairs fee components")?; + } + Ok(()) } diff --git a/crates/astria-sequencer/src/service/mempool/tests.rs b/crates/astria-sequencer/src/service/mempool/tests.rs index b85f41df7c..631bfc827e 100644 --- a/crates/astria-sequencer/src/service/mempool/tests.rs +++ b/crates/astria-sequencer/src/service/mempool/tests.rs @@ -13,7 +13,7 @@ use tendermint::{ use crate::{ app::{ - benchmark_and_test_utils::genesis_state, + benchmark_and_test_utils::get_test_genesis_state, test_utils::MockTxBuilder, App, }, @@ -38,7 +38,7 @@ async fn future_nonces_are_accepted() { app.init_chain( storage.clone(), - genesis_state(), + get_test_genesis_state(), vec![], "test".to_string(), 0, @@ -76,7 +76,7 @@ async fn rechecks_pass() { app.init_chain( storage.clone(), - genesis_state(), + get_test_genesis_state(), vec![], "test".to_string(), 0, @@ -122,7 +122,7 @@ async fn can_reinsert_after_recheck_fail() { app.init_chain( storage.clone(), - genesis_state(), + get_test_genesis_state(), vec![], "test".to_string(), 0, @@ -178,7 +178,7 @@ async fn recheck_adds_non_tracked_tx() { app.init_chain( storage.clone(), - genesis_state(), + get_test_genesis_state(), vec![], "test".to_string(), 0, From 436f57d90055c6cf8b8405ad9f1c001d8ea0180c Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 13 Dec 2024 12:34:14 -0500 Subject: [PATCH 6/7] update snapshots --- ...core__protocol__genesis__v1__tests__genesis_state.snap | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state.snap b/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state.snap index 21acc3a521..004306d1e7 100644 --- a/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state.snap +++ b/crates/astria-core/src/protocol/genesis/snapshots/astria_core__protocol__genesis__v1__tests__genesis_state.snap @@ -131,6 +131,14 @@ expression: genesis_state() "validatorUpdate": { "base": {}, "multiplier": {} + }, + "addCurrencyPairs": { + "base": {}, + "multiplier": {} + }, + "removeCurrencyPairs": { + "base": {}, + "multiplier": {} } }, "connect": { From 2da5b5ee8519ed894ea5271536e968a12772670e Mon Sep 17 00:00:00 2001 From: elizabeth Date: Fri, 13 Dec 2024 14:48:13 -0500 Subject: [PATCH 7/7] fix tests --- ...breaking_changes__app_hash_at_genesis.snap | 60 +++++++++---------- ...hanges__app_hash_execute_every_action.snap | 60 +++++++++---------- ...king_changes__app_hash_finalize_block.snap | 60 +++++++++---------- .../src/app/tests_breaking_changes.rs | 34 +++++++++++ .../src/app/tests_execute_transaction.rs | 1 + 5 files changed, 125 insertions(+), 90 deletions(-) diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap index 902b74b9a3..94ae75bee4 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_at_genesis.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 157, - 13, - 132, - 3, - 217, - 162, - 227, - 248, - 73, - 118, - 68, - 236, - 34, - 159, - 87, - 5, - 85, - 41, - 161, - 204, - 145, + 131, + 167, + 88, + 101, + 218, + 254, + 19, + 102, + 147, + 63, + 205, + 234, + 31, + 196, + 47, + 222, + 20, + 84, + 53, + 174, + 172, + 189, + 174, 154, - 238, - 97, 139, - 220, - 41, - 164, - 135, - 194, - 179, - 87 + 244, + 239, + 59, + 70, + 251, + 95, + 70 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap index ef11b69906..57bc28686d 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_execute_every_action.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 65, - 196, - 122, - 216, - 249, - 102, - 27, - 156, - 222, - 203, - 66, + 255, + 11, 178, - 168, - 59, + 217, + 172, + 122, + 116, + 231, 1, - 214, - 143, - 185, - 29, - 138, - 161, - 109, - 71, - 78, - 249, - 62, - 16, - 107, - 192, - 151, - 124, - 20 + 74, + 167, + 201, + 134, + 26, + 35, + 157, + 44, + 206, + 101, + 210, + 2, + 96, + 162, + 213, + 165, + 49, + 82, + 19, + 134, + 82, + 247, + 72 ] diff --git a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap index f3bff68358..24b0fb555c 100644 --- a/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap +++ b/crates/astria-sequencer/src/app/snapshots/astria_sequencer__app__tests_breaking_changes__app_hash_finalize_block.snap @@ -3,36 +3,36 @@ source: crates/astria-sequencer/src/app/tests_breaking_changes.rs expression: app.app_hash.as_bytes() --- [ - 150, - 81, - 50, - 231, - 107, - 236, - 27, - 6, - 41, - 178, - 107, - 36, - 215, + 247, + 161, + 143, + 121, + 170, + 69, + 5, + 151, 193, - 86, - 129, - 159, - 216, - 205, - 186, - 209, - 114, + 59, + 28, + 146, + 221, 210, - 24, - 78, - 74, - 36, - 151, - 35, - 133, - 176, - 209 + 102, + 243, + 221, + 51, + 5, + 90, + 88, + 208, + 47, + 18, + 92, + 248, + 211, + 103, + 156, + 26, + 127, + 58 ] diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 1e2b186712..709bcfc437 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -11,10 +11,12 @@ use std::{ collections::HashMap, + str::FromStr as _, sync::Arc, }; use astria_core::{ + connect::types::v2::CurrencyPair, primitive::v1::{ Address, RollupId, @@ -23,11 +25,13 @@ use astria_core::{ genesis::v1::Account, transaction::v1::{ action::{ + AddCurrencyPairs, BridgeLock, BridgeSudoChange, BridgeUnlock, IbcRelayerChange, IbcSudoChange, + RemoveCurrencyPairs, RollupDataSubmission, Transfer, ValidatorUpdate, @@ -361,6 +365,36 @@ async fn app_execute_transaction_with_every_action_snapshot() { let signed_tx = Arc::new(tx_bridge.sign(&bridge)); app.execute_transaction(signed_tx).await.unwrap(); + let currency_pair_tia = CurrencyPair::from_str("TIA/USD").unwrap(); + let currency_pair_eth = CurrencyPair::from_str("ETH/USD").unwrap(); + let tx = TransactionBody::builder() + .actions(vec![ + AddCurrencyPairs { + pairs: vec![currency_pair_tia.clone(), currency_pair_eth.clone()], + } + .into(), + ]) + .chain_id("test") + .nonce(4) + .try_build() + .unwrap(); + let signed_tx = Arc::new(tx.sign(&alice)); + app.execute_transaction(signed_tx).await.unwrap(); + + let tx = TransactionBody::builder() + .actions(vec![ + RemoveCurrencyPairs { + pairs: vec![currency_pair_tia.clone()], + } + .into(), + ]) + .chain_id("test") + .nonce(5) + .try_build() + .unwrap(); + let signed_tx = Arc::new(tx.sign(&alice)); + app.execute_transaction(signed_tx).await.unwrap(); + let sudo_address = app.state.get_sudo_address().await.unwrap(); app.end_block(1, &sudo_address).await.unwrap(); diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 90de4e8871..c4790b130e 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -1374,6 +1374,7 @@ async fn test_app_execute_transaction_add_and_remove_currency_pairs() { .into(), ]) .chain_id("test") + .nonce(1) .try_build() .unwrap();