From 4193cbc90f98b4b2cc15976668e531648f5bed6a Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 21 Sep 2023 14:01:51 -0400 Subject: [PATCH 1/8] refactor: merge state.rs and types.rs (#111) --- ampd/src/evm/mod.rs | 34 +- ampd/src/handlers/evm_verify_msg.rs | 6 +- ampd/src/handlers/evm_verify_worker_set.rs | 4 +- contracts/aggregate-verifier/tests/test.rs | 4 +- contracts/connection-router/src/contract.rs | 7 +- contracts/connection-router/src/error.rs | 7 +- contracts/connection-router/src/events.rs | 3 +- contracts/connection-router/src/lib.rs | 1 - contracts/connection-router/src/msg.rs | 4 +- contracts/connection-router/src/state.rs | 310 +++++++++++++++++- contracts/connection-router/src/types.rs | 295 ----------------- contracts/connection-router/tests/mock.rs | 3 +- contracts/connection-router/tests/test.rs | 3 +- contracts/gateway/src/contract.rs | 2 +- contracts/gateway/tests/tests.rs | 2 +- contracts/multisig-prover/src/contract.rs | 2 +- contracts/multisig-prover/src/execute.rs | 2 +- contracts/multisig-prover/src/state.rs | 2 +- .../src/test/mocks/voting_verifier.rs | 4 +- contracts/voting-verifier/src/error.rs | 4 +- contracts/voting-verifier/src/events.rs | 9 +- contracts/voting-verifier/src/execute.rs | 5 +- contracts/voting-verifier/src/msg.rs | 8 +- contracts/voting-verifier/src/state.rs | 5 +- contracts/voting-verifier/tests/tests.rs | 16 +- 25 files changed, 355 insertions(+), 387 deletions(-) delete mode 100644 contracts/connection-router/src/types.rs diff --git a/ampd/src/evm/mod.rs b/ampd/src/evm/mod.rs index d7c2ad40d..53db6dfe4 100644 --- a/ampd/src/evm/mod.rs +++ b/ampd/src/evm/mod.rs @@ -16,8 +16,8 @@ pub enum ChainName { Other(String), } -impl PartialEq for ChainName { - fn eq(&self, other: &connection_router::types::ChainName) -> bool { +impl PartialEq for ChainName { + fn eq(&self, other: &connection_router::state::ChainName) -> bool { self.to_string().eq_ignore_ascii_case(&other.to_string()) } } @@ -44,28 +44,26 @@ impl ChainName { #[cfg(test)] mod tests { + use crate::evm; + use connection_router::state::ChainName; use std::str::FromStr; - use crate::evm::ChainName; - #[test] fn chain_name_partial_eq() { - let chain_name = ChainName::Ethereum; + let chain_name = evm::ChainName::Ethereum; - assert!(chain_name == connection_router::types::ChainName::from_str("Ethereum").unwrap()); - assert!(chain_name == connection_router::types::ChainName::from_str("ETHEREUM").unwrap()); - assert!(chain_name == connection_router::types::ChainName::from_str("ethereum").unwrap()); - assert!(chain_name == connection_router::types::ChainName::from_str("ethEReum").unwrap()); - assert!(chain_name != connection_router::types::ChainName::from_str("Ethereum-1").unwrap()); + assert_eq!(chain_name, ChainName::from_str("Ethereum").unwrap()); + assert_eq!(chain_name, ChainName::from_str("ETHEREUM").unwrap()); + assert_eq!(chain_name, ChainName::from_str("ethereum").unwrap()); + assert_eq!(chain_name, ChainName::from_str("ethEReum").unwrap()); + assert_ne!(chain_name, ChainName::from_str("Ethereum-1").unwrap()); - let chain_name = ChainName::Other("avalanche".into()); + let chain_name = evm::ChainName::Other("avalanche".into()); - assert!(chain_name == connection_router::types::ChainName::from_str("Avalanche").unwrap()); - assert!(chain_name == connection_router::types::ChainName::from_str("AVALANCHE").unwrap()); - assert!(chain_name == connection_router::types::ChainName::from_str("avalanche").unwrap()); - assert!(chain_name == connection_router::types::ChainName::from_str("avaLAnche").unwrap()); - assert!( - chain_name != connection_router::types::ChainName::from_str("Avalanche-2").unwrap() - ); + assert_eq!(chain_name, ChainName::from_str("Avalanche").unwrap()); + assert_eq!(chain_name, ChainName::from_str("AVALANCHE").unwrap()); + assert_eq!(chain_name, ChainName::from_str("avalanche").unwrap()); + assert_eq!(chain_name, ChainName::from_str("avaLAnche").unwrap()); + assert_ne!(chain_name, ChainName::from_str("Avalanche-2").unwrap()); } } diff --git a/ampd/src/handlers/evm_verify_msg.rs b/ampd/src/handlers/evm_verify_msg.rs index 38af2569c..0addb782b 100644 --- a/ampd/src/handlers/evm_verify_msg.rs +++ b/ampd/src/handlers/evm_verify_msg.rs @@ -11,7 +11,7 @@ use tracing::{info, info_span}; use valuable::Valuable; use axelar_wasm_std::voting::PollID; -use connection_router::types::ID_SEPARATOR; +use connection_router::state::ID_SEPARATOR; use events::Error::EventTypeMismatch; use events_derive::try_from; use voting_verifier::msg::ExecuteMsg; @@ -32,7 +32,7 @@ pub struct Message { pub tx_id: Hash, pub event_index: u64, pub destination_address: String, - pub destination_chain: connection_router::types::ChainName, + pub destination_chain: connection_router::state::ChainName, pub source_address: EVMAddress, pub payload_hash: Hash, } @@ -43,7 +43,7 @@ struct PollStartedEvent { #[serde(rename = "_contract_address")] contract_address: TMAddress, poll_id: PollID, - source_chain: connection_router::types::ChainName, + source_chain: connection_router::state::ChainName, source_gateway_address: EVMAddress, confirmation_height: u64, messages: Vec, diff --git a/ampd/src/handlers/evm_verify_worker_set.rs b/ampd/src/handlers/evm_verify_worker_set.rs index 9ec91bcdf..099efc502 100644 --- a/ampd/src/handlers/evm_verify_worker_set.rs +++ b/ampd/src/handlers/evm_verify_worker_set.rs @@ -11,7 +11,7 @@ use events::Error::EventTypeMismatch; use events_derive::try_from; use axelar_wasm_std::voting::PollID; -use connection_router::types::ID_SEPARATOR; +use connection_router::state::ID_SEPARATOR; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; @@ -43,7 +43,7 @@ struct PollStartedEvent { contract_address: TMAddress, worker_set: WorkerSetConfirmation, poll_id: PollID, - source_chain: connection_router::types::ChainName, + source_chain: connection_router::state::ChainName, source_gateway_address: EVMAddress, confirmation_height: u64, participants: Vec, diff --git a/contracts/aggregate-verifier/tests/test.rs b/contracts/aggregate-verifier/tests/test.rs index b6340fa01..090f34097 100644 --- a/contracts/aggregate-verifier/tests/test.rs +++ b/contracts/aggregate-verifier/tests/test.rs @@ -2,7 +2,7 @@ use aggregate_verifier::contract::*; use aggregate_verifier::msg::{ExecuteMsg, InstantiateMsg}; use axelar_wasm_std::ContractError; use connection_router::msg::Message; -use connection_router::types::ID_SEPARATOR; +use connection_router::state::ID_SEPARATOR; use cosmwasm_std::from_binary; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; @@ -68,7 +68,7 @@ fn bad_message_id() { .unwrap_err(); assert_eq!( res.downcast::().unwrap().to_string(), - ContractError::from(connection_router::error::ContractError::InvalidMessageID).to_string() + ContractError::from(connection_router::error::ContractError::InvalidMessageId).to_string() ) } diff --git a/contracts/connection-router/src/contract.rs b/contracts/connection-router/src/contract.rs index d7de7475f..23be7ee8b 100644 --- a/contracts/connection-router/src/contract.rs +++ b/contracts/connection-router/src/contract.rs @@ -71,11 +71,8 @@ pub mod execute { use axelar_wasm_std::flagset::FlagSet; - use crate::state::NewMessage; - use crate::{ - events::{ChainFrozen, GatewayInfo, GatewayUpgraded, MessageRouted}, - types::{ChainEndpoint, ChainName, Gateway, GatewayDirection}, - }; + use crate::events::{ChainFrozen, GatewayInfo, GatewayUpgraded, MessageRouted}; + use crate::state::{ChainEndpoint, ChainName, Gateway, GatewayDirection, NewMessage}; use super::*; diff --git a/contracts/connection-router/src/error.rs b/contracts/connection-router/src/error.rs index 84914bf29..25e59631d 100644 --- a/contracts/connection-router/src/error.rs +++ b/contracts/connection-router/src/error.rs @@ -1,8 +1,9 @@ -use axelar_wasm_std_derive::IntoContractError; use cosmwasm_std::StdError; use thiserror::Error; -use crate::types::ChainName; +use axelar_wasm_std_derive::IntoContractError; + +use crate::state::ChainName; #[derive(Error, Debug, PartialEq, IntoContractError)] pub enum ContractError { @@ -19,7 +20,7 @@ pub enum ContractError { InvalidChainName, #[error("message ID is invalid")] - InvalidMessageID, + InvalidMessageId, #[error("chain is not found")] ChainNotFound, diff --git a/contracts/connection-router/src/events.rs b/contracts/connection-router/src/events.rs index fa582c475..3244ae6dc 100644 --- a/contracts/connection-router/src/events.rs +++ b/contracts/connection-router/src/events.rs @@ -1,8 +1,7 @@ use cosmwasm_std::{Addr, Attribute, Event}; use std::ops::Deref; -use crate::state::Message; -use crate::{state::NewMessage, types::ChainName}; +use crate::state::{ChainName, Message, NewMessage}; pub struct RouterInstantiated { pub admin: Addr, diff --git a/contracts/connection-router/src/lib.rs b/contracts/connection-router/src/lib.rs index 15daef8fd..41f83fef5 100644 --- a/contracts/connection-router/src/lib.rs +++ b/contracts/connection-router/src/lib.rs @@ -3,6 +3,5 @@ pub mod error; pub mod events; pub mod msg; pub mod state; -pub mod types; pub use crate::error::ContractError; diff --git a/contracts/connection-router/src/msg.rs b/contracts/connection-router/src/msg.rs index 1cc9fdafc..f34627d1a 100644 --- a/contracts/connection-router/src/msg.rs +++ b/contracts/connection-router/src/msg.rs @@ -1,9 +1,7 @@ -use crate::state::NewMessage; +use crate::state::{GatewayDirection, NewMessage}; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::HexBinary; -use crate::types::GatewayDirection; - // Message is a type meant to be used in interfaces where the data can be provided by the user. // The fields have not necessarily been validated, and should be checked prior to further processing. #[cw_serde] diff --git a/contracts/connection-router/src/state.rs b/contracts/connection-router/src/state.rs index 6c8a4dc8b..a1536e963 100644 --- a/contracts/connection-router/src/state.rs +++ b/contracts/connection-router/src/state.rs @@ -1,22 +1,27 @@ #![allow(deprecated)] use core::panic; +use std::fmt; +use std::fmt::{Display, Formatter}; use std::ops::Deref; use std::str::FromStr; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, DepsMut, HexBinary, Order, StdResult}; -use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; +use cosmwasm_std::{Addr, DepsMut, HexBinary, Order, StdError, StdResult}; +use cw_storage_plus::{ + Index, IndexList, IndexedMap, Item, Key, KeyDeserialize, MultiIndex, Prefixer, PrimaryKey, +}; use error_stack::{bail, Report, ResultExt}; +use flagset::flags; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; -use axelar_wasm_std::nonempty; +use axelar_wasm_std::flagset::FlagSet; +use axelar_wasm_std::{nonempty, FnExt}; -use crate::types::CrossChainUid; -use crate::{ - msg, - types::{ChainEndpoint, ChainName, MessageID, ID_SEPARATOR}, - ContractError, -}; +use crate::{msg, ContractError}; + +pub const ID_SEPARATOR: char = ':'; #[cw_serde] pub struct Config { @@ -115,10 +120,10 @@ impl TryFrom for NewMessage { let (chain, id) = msg .id .split_once(ID_SEPARATOR) - .ok_or(ContractError::InvalidMessageID)?; + .ok_or(ContractError::InvalidMessageId)?; if chain.parse::()? != msg.source_chain { - bail!(ContractError::InvalidMessageID); + bail!(ContractError::InvalidMessageId); } Ok(NewMessage { @@ -139,7 +144,7 @@ impl TryFrom for NewMessage { #[cw_serde] #[deprecated(note = "use NewMessage instead")] pub struct Message { - pub id: MessageID, // globally unique + pub id: MessageId, // globally unique pub destination_address: String, pub destination_chain: ChainName, pub source_chain: ChainName, @@ -149,7 +154,7 @@ pub struct Message { impl Message { pub fn new( - id: MessageID, + id: MessageId, destination_address: String, destination_chain: ChainName, source_chain: ChainName, @@ -182,7 +187,7 @@ impl TryFrom for Message { .id .starts_with(&format!("{}{}", value.source_chain, ID_SEPARATOR)) { - return Err(ContractError::InvalidMessageID); + return Err(ContractError::InvalidMessageId); } Ok(Message::new( @@ -240,14 +245,205 @@ impl TryFrom for Address { } } +#[cw_serde] +#[serde(try_from = "String")] +pub struct MessageId(String); + +impl FromStr for MessageId { + type Err = ContractError; + + fn from_str(s: &str) -> Result { + // todo: should split in exactly 2 parts when migrated to state::NewMessage + let split: Vec<_> = s.split(ID_SEPARATOR).filter(|s| !s.is_empty()).collect(); + if split.len() < 2 { + return Err(ContractError::InvalidMessageId); + } + Ok(MessageId(s.to_lowercase())) + } +} + +impl TryFrom for MessageId { + type Error = ContractError; + fn try_from(value: String) -> Result { + value.as_str().parse() + } +} + +impl From for String { + fn from(d: MessageId) -> Self { + d.0 + } +} + +impl Deref for MessageId { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for MessageId { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl<'a> PrimaryKey<'a> for MessageId { + type Prefix = (); + type SubPrefix = (); + type Suffix = Self; + type SuperSuffix = Self; + + fn key(&self) -> Vec { + vec![Key::Ref(self.0.as_bytes())] + } +} + +impl KeyDeserialize for MessageId { + type Output = Self; + + fn from_vec(value: Vec) -> StdResult { + let value = String::from_utf8(value).map_err(StdError::invalid_utf8)?; + Ok(Self(value)) + } +} + +#[cw_serde] +pub struct CrossChainUid { + pub chain: ChainName, + pub id: MessageId, +} + +impl Display for CrossChainUid { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}{}{}", &self.chain, ID_SEPARATOR, &self.id) + } +} + +impl PrimaryKey<'_> for CrossChainUid { + type Prefix = ChainName; + type SubPrefix = (); + type Suffix = MessageId; + type SuperSuffix = (ChainName, MessageId); + + fn key(&self) -> Vec { + let mut keys = self.chain.key(); + keys.extend(self.id.key()); + keys + } +} + +impl KeyDeserialize for CrossChainUid { + type Output = Self; + + fn from_vec(value: Vec) -> StdResult { + let (chain, id) = <(ChainName, MessageId)>::from_vec(value)?; + Ok(CrossChainUid { chain, id }) + } +} + +#[cw_serde] +#[serde(try_from = "String")] +pub struct ChainName(String); + +impl FromStr for ChainName { + type Err = ContractError; + + fn from_str(s: &str) -> Result { + if s.contains(ID_SEPARATOR) || s.is_empty() { + return Err(ContractError::InvalidChainName); + } + + Ok(ChainName(s.to_lowercase())) + } +} + +impl From for String { + fn from(d: ChainName) -> Self { + d.0 + } +} + +impl TryFrom for ChainName { + type Error = ContractError; + + fn try_from(value: String) -> Result { + value.parse() + } +} + +impl Display for ChainName { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl<'a> PrimaryKey<'a> for ChainName { + type Prefix = (); + type SubPrefix = (); + type Suffix = Self; + type SuperSuffix = Self; + + fn key(&self) -> Vec { + vec![Key::Ref(self.0.as_bytes())] + } +} + +impl<'a> Prefixer<'a> for ChainName { + fn prefix(&self) -> Vec { + vec![Key::Ref(self.0.as_bytes())] + } +} + +impl KeyDeserialize for ChainName { + type Output = Self; + + #[inline(always)] + fn from_vec(value: Vec) -> StdResult { + String::from_utf8(value) + .map_err(StdError::invalid_utf8)? + .then(ChainName::try_from) + .map_err(StdError::invalid_utf8) + } +} + +#[cw_serde] +pub struct Gateway { + pub address: Addr, +} + +#[cw_serde] +pub struct ChainEndpoint { + pub name: ChainName, + pub gateway: Gateway, + pub frozen_status: FlagSet, +} + +flags! { + #[repr(u8)] + #[derive(Deserialize, Serialize, Hash, JsonSchema)] + pub enum GatewayDirection: u8 { + None = 0, + Incoming = 1, + Outgoing = 2, + Bidirectional = (GatewayDirection::Incoming | GatewayDirection::Outgoing).bits(), + } +} + #[cfg(test)] mod tests { - use crate::state::NewMessage; - use crate::types::CrossChainUid; + use std::str::FromStr; + use cosmwasm_std::to_vec; use hex; + use rand::distributions::Alphanumeric; + use rand::{thread_rng, Rng}; use sha3::{Digest, Sha3_256}; + use crate::state::{ChainName, CrossChainUid, MessageId, NewMessage, ID_SEPARATOR}; + use crate::ContractError; + #[test] fn create_correct_global_message_id() { let msg = dummy_message(); @@ -270,6 +466,88 @@ mod tests { ); } + #[test] + fn should_fail_to_parse_invalid_chain_name() { + // empty + assert_eq!( + "".parse::().unwrap_err(), + ContractError::InvalidChainName + ); + + // name contains id separator + assert_eq!( + format!("chain {ID_SEPARATOR}") + .parse::() + .unwrap_err(), + ContractError::InvalidChainName + ); + } + + #[test] + fn should_parse_to_case_insensitive_chain_name() { + let rand_str: String = thread_rng() + .sample_iter(&Alphanumeric) + .take(10) + .map(char::from) + .collect(); + + let chain_name: ChainName = rand_str.parse().unwrap(); + + assert_eq!( + chain_name, + rand_str.to_lowercase().parse::().unwrap() + ); + assert_eq!( + chain_name, + rand_str.to_uppercase().parse::().unwrap() + ); + } + + #[test] + fn should_not_deserialize_invalid_chain_name() { + assert_eq!( + "chain name is invalid", + serde_json::from_str::(format!("\"\"").as_str()) + .unwrap_err() + .to_string() + ); + + assert_eq!( + "chain name is invalid", + serde_json::from_str::(format!("\"chain{ID_SEPARATOR}\"").as_str()) + .unwrap_err() + .to_string() + ); + } + + #[test] + fn message_id_must_have_at_least_one_separator() { + assert!(MessageId::from_str("source_chain:hash:id").is_ok()); + assert!(serde_json::from_str::("\"source_chain:hash:id\"").is_ok()); + + assert!(MessageId::from_str("invalid_hash").is_err()); + assert!(serde_json::from_str::("\"invalid_hash\"").is_err()); + + assert!(MessageId::from_str("invalid_hash:").is_err()); + } + + #[test] + fn message_id_is_lower_case() { + let msg_id = "HaSH:iD".parse::().unwrap(); + assert_eq!(msg_id.to_string(), "hash:id"); + } + + #[test] + fn serialize_global_message_id() { + let id = CrossChainUid { + chain: "ethereum".parse().unwrap(), + id: "hash:id".parse().unwrap(), + }; + + let serialized = serde_json::to_string(&id).unwrap(); + assert_eq!(id, serde_json::from_str(&serialized).unwrap()); + } + fn dummy_message() -> NewMessage { NewMessage { uid: CrossChainUid { diff --git a/contracts/connection-router/src/types.rs b/contracts/connection-router/src/types.rs deleted file mode 100644 index 403b92376..000000000 --- a/contracts/connection-router/src/types.rs +++ /dev/null @@ -1,295 +0,0 @@ -use std::fmt; -use std::fmt::{Display, Formatter}; -use std::ops::Deref; -use std::str::FromStr; - -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, StdError, StdResult}; -use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey}; -use flagset::flags; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use axelar_wasm_std::flagset::FlagSet; -use axelar_wasm_std::FnExt; - -use crate::ContractError; - -pub const ID_SEPARATOR: char = ':'; - -#[cw_serde] -#[serde(try_from = "String")] -pub struct MessageID(String); - -impl FromStr for MessageID { - type Err = ContractError; - - fn from_str(s: &str) -> Result { - // todo: should split in exactly 2 parts when migrated to state::NewMessage - let split: Vec<_> = s.split(ID_SEPARATOR).filter(|s| !s.is_empty()).collect(); - if split.len() < 2 { - return Err(ContractError::InvalidMessageID); - } - Ok(MessageID(s.to_lowercase())) - } -} - -impl TryFrom for MessageID { - type Error = ContractError; - fn try_from(value: String) -> Result { - value.as_str().parse() - } -} - -impl From for String { - fn from(d: MessageID) -> Self { - d.0 - } -} - -impl Deref for MessageID { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Display for MessageID { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl<'a> PrimaryKey<'a> for MessageID { - type Prefix = (); - type SubPrefix = (); - type Suffix = Self; - type SuperSuffix = Self; - - fn key(&self) -> Vec { - vec![Key::Ref(self.0.as_bytes())] - } -} - -impl KeyDeserialize for MessageID { - type Output = Self; - - fn from_vec(value: Vec) -> StdResult { - let value = String::from_utf8(value).map_err(StdError::invalid_utf8)?; - Ok(Self(value)) - } -} - -/// cosmwasm cannot serialize tuples, so we need to convert [CrossChainUid] into a struct -#[cw_serde] -pub struct CrossChainUid { - pub chain: ChainName, - pub id: MessageID, -} - -impl Display for CrossChainUid { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}{}{}", &self.chain, ID_SEPARATOR, &self.id) - } -} - -impl PrimaryKey<'_> for CrossChainUid { - type Prefix = ChainName; - type SubPrefix = (); - type Suffix = MessageID; - type SuperSuffix = (ChainName, MessageID); - - fn key(&self) -> Vec { - let mut keys = self.chain.key(); - keys.extend(self.id.key()); - keys - } -} - -impl KeyDeserialize for CrossChainUid { - type Output = Self; - - fn from_vec(value: Vec) -> StdResult { - let (chain, id) = <(ChainName, MessageID)>::from_vec(value)?; - Ok(CrossChainUid { chain, id }) - } -} - -#[cw_serde] -#[serde(try_from = "String")] -pub struct ChainName(String); - -impl FromStr for ChainName { - type Err = ContractError; - - fn from_str(s: &str) -> Result { - if s.contains(ID_SEPARATOR) || s.is_empty() { - return Err(ContractError::InvalidChainName); - } - - Ok(ChainName(s.to_lowercase())) - } -} - -impl From for String { - fn from(d: ChainName) -> Self { - d.0 - } -} - -impl TryFrom for ChainName { - type Error = ContractError; - - fn try_from(value: String) -> Result { - value.parse() - } -} - -impl Display for ChainName { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl<'a> PrimaryKey<'a> for ChainName { - type Prefix = (); - type SubPrefix = (); - type Suffix = Self; - type SuperSuffix = Self; - - fn key(&self) -> Vec { - vec![Key::Ref(self.0.as_bytes())] - } -} - -impl<'a> Prefixer<'a> for ChainName { - fn prefix(&self) -> Vec { - vec![Key::Ref(self.0.as_bytes())] - } -} - -impl KeyDeserialize for ChainName { - type Output = Self; - - #[inline(always)] - fn from_vec(value: Vec) -> StdResult { - String::from_utf8(value) - .map_err(StdError::invalid_utf8)? - .then(ChainName::try_from) - .map_err(StdError::invalid_utf8) - } -} - -#[cw_serde] -pub struct Gateway { - pub address: Addr, -} - -#[cw_serde] -pub struct ChainEndpoint { - pub name: ChainName, - pub gateway: Gateway, - pub frozen_status: FlagSet, -} - -flags! { - #[repr(u8)] - #[derive(Deserialize, Serialize, Hash, JsonSchema)] - pub enum GatewayDirection: u8 { - None = 0, - Incoming = 1, - Outgoing = 2, - Bidirectional = (GatewayDirection::Incoming | GatewayDirection::Outgoing).bits(), - } -} - -#[cfg(test)] -mod tests { - use rand::distributions::Alphanumeric; - use rand::{thread_rng, Rng}; - - use super::*; - - #[test] - fn should_fail_to_parse_invalid_chain_name() { - // empty - assert_eq!( - "".parse::().unwrap_err(), - ContractError::InvalidChainName - ); - - // name contains id separator - assert_eq!( - format!("chain {ID_SEPARATOR}") - .parse::() - .unwrap_err(), - ContractError::InvalidChainName - ); - } - - #[test] - fn should_parse_to_case_insensitive_chain_name() { - let rand_str: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(10) - .map(char::from) - .collect(); - - let chain_name: ChainName = rand_str.parse().unwrap(); - - assert_eq!( - chain_name, - rand_str.to_lowercase().parse::().unwrap() - ); - assert_eq!( - chain_name, - rand_str.to_uppercase().parse::().unwrap() - ); - } - - #[test] - fn should_not_deserialize_invalid_chain_name() { - assert_eq!( - "chain name is invalid", - serde_json::from_str::(format!("\"\"").as_str()) - .unwrap_err() - .to_string() - ); - - assert_eq!( - "chain name is invalid", - serde_json::from_str::(format!("\"chain{ID_SEPARATOR}\"").as_str()) - .unwrap_err() - .to_string() - ); - } - - #[test] - fn message_id_must_have_at_least_one_separator() { - assert!(MessageID::from_str("source_chain:hash:id").is_ok()); - assert!(serde_json::from_str::("\"source_chain:hash:id\"").is_ok()); - - assert!(MessageID::from_str("invalid_hash").is_err()); - assert!(serde_json::from_str::("\"invalid_hash\"").is_err()); - - assert!(MessageID::from_str("invalid_hash:").is_err()); - } - - #[test] - fn message_id_is_lower_case() { - let msg_id = "HaSH:iD".parse::().unwrap(); - assert_eq!(msg_id.to_string(), "hash:id"); - } - - #[test] - fn serialize_global_message_id() { - let id = CrossChainUid { - chain: "ethereum".parse().unwrap(), - id: "hash:id".parse().unwrap(), - }; - - let serialized = serde_json::to_string(&id).unwrap(); - assert_eq!(id, serde_json::from_str(&serialized).unwrap()); - } -} diff --git a/contracts/connection-router/tests/mock.rs b/contracts/connection-router/tests/mock.rs index 9567a938e..1e8564c2f 100644 --- a/contracts/connection-router/tests/mock.rs +++ b/contracts/connection-router/tests/mock.rs @@ -1,5 +1,4 @@ -use connection_router::state::NewMessage; -use connection_router::types::CrossChainUid; +use connection_router::state::{CrossChainUid, NewMessage}; use connection_router::ContractError; use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; diff --git a/contracts/connection-router/tests/test.rs b/contracts/connection-router/tests/test.rs index a97cde82d..e7341ab5e 100644 --- a/contracts/connection-router/tests/test.rs +++ b/contracts/connection-router/tests/test.rs @@ -7,8 +7,7 @@ use cw_multi_test::{App, ContractWrapper, Executor}; use connection_router::contract::*; use connection_router::error::ContractError; use connection_router::msg::{ExecuteMsg, InstantiateMsg}; -use connection_router::state::NewMessage; -use connection_router::types::{ChainName, CrossChainUid, GatewayDirection}; +use connection_router::state::{ChainName, CrossChainUid, GatewayDirection, NewMessage}; pub mod mock; diff --git a/contracts/gateway/src/contract.rs b/contracts/gateway/src/contract.rs index 1c20767cf..b40e52057 100644 --- a/contracts/gateway/src/contract.rs +++ b/contracts/gateway/src/contract.rs @@ -143,7 +143,7 @@ pub mod execute { .into_iter() .map(connection_router::state::NewMessage::try_from) .collect::, _>>() - .map_err(|_| connection_router::error::ContractError::InvalidMessageID)?, //todo: remove when integrating error stack into the gateway + .map_err(|_| connection_router::error::ContractError::InvalidMessageId)?, //todo: remove when integrating error stack into the gateway ))?, funds: vec![], }) diff --git a/contracts/gateway/tests/tests.rs b/contracts/gateway/tests/tests.rs index 5c9e08ba0..b9d8448bf 100644 --- a/contracts/gateway/tests/tests.rs +++ b/contracts/gateway/tests/tests.rs @@ -1,5 +1,5 @@ use connection_router::msg::Message; -use connection_router::types::ID_SEPARATOR; +use connection_router::state::ID_SEPARATOR; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; use gateway::contract::*; diff --git a/contracts/multisig-prover/src/contract.rs b/contracts/multisig-prover/src/contract.rs index f4d15173a..fc23aaa25 100644 --- a/contracts/multisig-prover/src/contract.rs +++ b/contracts/multisig-prover/src/contract.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{ use std::str::FromStr; -use connection_router::types::ChainName; +use connection_router::state::ChainName; use crate::{ error::ContractError, diff --git a/contracts/multisig-prover/src/execute.rs b/contracts/multisig-prover/src/execute.rs index 4248db1f7..10a412634 100644 --- a/contracts/multisig-prover/src/execute.rs +++ b/contracts/multisig-prover/src/execute.rs @@ -7,7 +7,7 @@ use multisig::key::{KeyType, PublicKey}; use std::str::FromStr; use axelar_wasm_std::snapshot; -use connection_router::{msg::Message, types::ChainName}; +use connection_router::{msg::Message, state::ChainName}; use service_registry::state::Worker; use crate::{ diff --git a/contracts/multisig-prover/src/state.rs b/contracts/multisig-prover/src/state.rs index 706f87696..b85b0361d 100644 --- a/contracts/multisig-prover/src/state.rs +++ b/contracts/multisig-prover/src/state.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use axelar_wasm_std::{Participant, Snapshot, Threshold}; -use connection_router::types::ChainName; +use connection_router::state::ChainName; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, HexBinary, Uint256}; use cw_storage_plus::{Item, Map}; diff --git a/contracts/multisig-prover/src/test/mocks/voting_verifier.rs b/contracts/multisig-prover/src/test/mocks/voting_verifier.rs index 67fc8b539..18a34890d 100644 --- a/contracts/multisig-prover/src/test/mocks/voting_verifier.rs +++ b/contracts/multisig-prover/src/test/mocks/voting_verifier.rs @@ -1,5 +1,5 @@ use axelar_wasm_std::operators::Operators; -use connection_router::types::MessageID; +use connection_router::state::MessageId; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ to_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdError, @@ -57,7 +57,7 @@ pub fn confirm_worker_set( Addr::unchecked("relayer"), voting_verifier_address.clone(), &ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from("ethereum:00".to_string()).unwrap(), + message_id: MessageId::try_from("ethereum:00".to_string()).unwrap(), new_operators: Operators { weights_by_addresses: new_operators, threshold, diff --git a/contracts/voting-verifier/src/error.rs b/contracts/voting-verifier/src/error.rs index 294724568..da7f81a19 100644 --- a/contracts/voting-verifier/src/error.rs +++ b/contracts/voting-verifier/src/error.rs @@ -1,7 +1,7 @@ use axelar_wasm_std::{nonempty, voting}; use axelar_wasm_std_derive::IntoContractError; use connection_router; -use connection_router::types::{ChainName, MessageID}; +use connection_router::state::{ChainName, MessageId}; use cosmwasm_std::StdError; use service_registry; use thiserror::Error; @@ -30,7 +30,7 @@ pub enum ContractError { MessageMismatch(String), #[error("invalid message id {0}")] - InvalidMessageID(MessageID), + InvalidMessageID(MessageId), #[error("poll not found")] PollNotFound, diff --git a/contracts/voting-verifier/src/events.rs b/contracts/voting-verifier/src/events.rs index 85d394c90..8e53a3158 100644 --- a/contracts/voting-verifier/src/events.rs +++ b/contracts/voting-verifier/src/events.rs @@ -1,14 +1,13 @@ use std::str::FromStr; use std::vec::Vec; -use axelar_wasm_std::operators::Operators; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Attribute, Event, HexBinary}; use serde_json::to_string; +use axelar_wasm_std::operators::Operators; use axelar_wasm_std::voting::PollID; -use connection_router::state::Message; -use connection_router::types::{ChainName, MessageID, ID_SEPARATOR}; +use connection_router::state::{ChainName, Message, MessageId, ID_SEPARATOR}; use crate::error::ContractError; use crate::state::Config; @@ -103,7 +102,7 @@ pub struct WorkerSetConfirmation { } impl WorkerSetConfirmation { - pub fn new(message_id: MessageID, operators: Operators) -> Result { + pub fn new(message_id: MessageId, operators: Operators) -> Result { let (tx_id, event_index) = parse_message_id(&message_id)?; Ok(Self { tx_id, @@ -148,7 +147,7 @@ impl FromStr for MessageByTxEvent { } } -fn parse_message_id(message_id: &MessageID) -> Result<(String, u64), ContractError> { +fn parse_message_id(message_id: &MessageId) -> Result<(String, u64), ContractError> { // expected format: :: let components = message_id.as_str().split(ID_SEPARATOR).collect::>(); diff --git a/contracts/voting-verifier/src/execute.rs b/contracts/voting-verifier/src/execute.rs index ef9779a41..85eab4fb2 100644 --- a/contracts/voting-verifier/src/execute.rs +++ b/contracts/voting-verifier/src/execute.rs @@ -1,5 +1,4 @@ use axelar_wasm_std::operators::Operators; -use connection_router::types::{ChainName, MessageID}; use cosmwasm_std::{ to_binary, Deps, DepsMut, Env, MessageInfo, QueryRequest, Response, Storage, WasmQuery, }; @@ -9,7 +8,7 @@ use axelar_wasm_std::{ snapshot, voting::{Poll, WeightedPoll}, }; -use connection_router::state::Message; +use connection_router::state::{ChainName, Message, MessageId}; use service_registry::msg::QueryMsg; use service_registry::state::Worker; @@ -34,7 +33,7 @@ enum VerificationStatus { pub fn confirm_worker_set( deps: DepsMut, env: Env, - message_id: MessageID, + message_id: MessageId, new_operators: Operators, ) -> Result { if CONFIRMED_WORKER_SETS diff --git a/contracts/voting-verifier/src/msg.rs b/contracts/voting-verifier/src/msg.rs index b0d9c7f9c..0c92c2efb 100644 --- a/contracts/voting-verifier/src/msg.rs +++ b/contracts/voting-verifier/src/msg.rs @@ -5,10 +5,8 @@ use axelar_wasm_std::{ voting::{PollID, PollResult}, Threshold, }; -use connection_router::{ - msg::Message, - types::{ChainName, MessageID}, -}; +use connection_router::msg::Message; +use connection_router::state::{ChainName, MessageId}; #[cw_serde] pub struct InstantiateMsg { @@ -45,7 +43,7 @@ pub enum ExecuteMsg { // Starts a poll to confirm a worker set update on the external evm gateway ConfirmWorkerSet { - message_id: MessageID, + message_id: MessageId, new_operators: Operators, }, } diff --git a/contracts/voting-verifier/src/state.rs b/contracts/voting-verifier/src/state.rs index 7347604b8..fb3521b36 100644 --- a/contracts/voting-verifier/src/state.rs +++ b/contracts/voting-verifier/src/state.rs @@ -8,8 +8,7 @@ use axelar_wasm_std::{ voting::{PollID, WeightedPoll}, Threshold, }; -use connection_router::types::MessageID; -use connection_router::{state::Message, types::ChainName}; +use connection_router::state::{ChainName, Message, MessageId}; #[cw_serde] pub struct Config { @@ -34,7 +33,7 @@ pub const POLLS: Map = Map::new("polls"); pub const PENDING_MESSAGES: Map> = Map::new("pending_messages"); -pub const VERIFIED_MESSAGES: Map<&MessageID, Message> = Map::new("verified_messages"); +pub const VERIFIED_MESSAGES: Map<&MessageId, Message> = Map::new("verified_messages"); pub const CONFIG: Item = Item::new("config"); diff --git a/contracts/voting-verifier/tests/tests.rs b/contracts/voting-verifier/tests/tests.rs index a87d48349..cff2f3ec8 100644 --- a/contracts/voting-verifier/tests/tests.rs +++ b/contracts/voting-verifier/tests/tests.rs @@ -4,7 +4,7 @@ use cw_multi_test::{App, ContractWrapper, Executor}; use axelar_wasm_std::operators::Operators; use axelar_wasm_std::Threshold; use connection_router::msg::Message; -use connection_router::types::{MessageID, ID_SEPARATOR}; +use connection_router::state::{MessageId, ID_SEPARATOR}; use service_registry::state::Worker; use voting_verifier::{contract, error::ContractError, msg}; @@ -248,7 +248,7 @@ fn should_start_worker_set_confirmation() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -287,7 +287,7 @@ fn should_confirm_worker_set() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -341,7 +341,7 @@ fn should_not_confirm_worker_set() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -395,7 +395,7 @@ fn should_confirm_worker_set_after_failed() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -427,7 +427,7 @@ fn should_confirm_worker_set_after_failed() { // try again, and this time vote true let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -481,7 +481,7 @@ fn should_not_confirm_twice() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -504,7 +504,7 @@ fn should_not_confirm_twice() { // try again, should fail let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageID::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); From 5d8e29292faeabc1437c04ad49b732c4b307bcb4 Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 21 Sep 2023 14:17:54 -0400 Subject: [PATCH 2/8] refactor: migrate voting verifier to use NewMessage (#112) --- ampd/src/handlers/evm_verify_msg.rs | 30 ++-- ampd/src/handlers/evm_verify_worker_set.rs | 6 +- contracts/connection-router/src/contract.rs | 2 +- contracts/connection-router/src/events.rs | 4 +- contracts/connection-router/src/state.rs | 28 ++-- contracts/connection-router/tests/mock.rs | 10 +- contracts/connection-router/tests/test.rs | 4 +- contracts/voting-verifier/src/contract.rs | 40 +---- contracts/voting-verifier/src/events.rs | 101 ++++++------ contracts/voting-verifier/src/execute.rs | 49 +++--- contracts/voting-verifier/src/msg.rs | 18 +-- contracts/voting-verifier/src/query.rs | 16 +- contracts/voting-verifier/src/state.rs | 18 +-- contracts/voting-verifier/tests/tests.rs | 164 ++++++++++---------- 14 files changed, 245 insertions(+), 245 deletions(-) diff --git a/ampd/src/handlers/evm_verify_msg.rs b/ampd/src/handlers/evm_verify_msg.rs index 0addb782b..80acb70e8 100644 --- a/ampd/src/handlers/evm_verify_msg.rs +++ b/ampd/src/handlers/evm_verify_msg.rs @@ -233,7 +233,7 @@ mod tests { use events::Error::{DeserializationFailed, EventTypeMismatch}; use events::Event; - use voting_verifier::events::{MessageByTxEvent, PollMetadata, PollStarted}; + use voting_verifier::events::{PollMetadata, PollStarted, TxEventConfirmation}; use crate::types::{EVMAddress, Hash}; @@ -244,7 +244,9 @@ mod tests { metadata: PollMetadata { poll_id: "100".parse().unwrap(), source_chain: "ethereum".parse().unwrap(), - source_gateway_address: "0x4f4495243837681061c4743b74eedf548d5686a5".into(), + source_gateway_address: "0x4f4495243837681061c4743b74eedf548d5686a5" + .parse() + .unwrap(), confirmation_height: 15, expires_at: 100, participants: vec![ @@ -260,28 +262,28 @@ mod tests { ], }, messages: vec![ - MessageByTxEvent { - tx_id: format!("0x{:x}", Hash::random()), + TxEventConfirmation { + tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), event_index: 0, - source_address: format!("0x{:x}", EVMAddress::random()), + source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), - destination_address: format!("0x{:x}", EVMAddress::random()), + destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), payload_hash: HexBinary::from(Hash::random().as_bytes()), }, - MessageByTxEvent { - tx_id: format!("0x{:x}", Hash::random()), + TxEventConfirmation { + tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), event_index: 1, - source_address: format!("0x{:x}", EVMAddress::random()), + source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), - destination_address: format!("0x{:x}", EVMAddress::random()), + destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), payload_hash: HexBinary::from(Hash::random().as_bytes()), }, - MessageByTxEvent { - tx_id: format!("0x{:x}", Hash::random()), + TxEventConfirmation { + tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), event_index: 10, - source_address: format!("0x{:x}", EVMAddress::random()), + source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), - destination_address: format!("0x{:x}", EVMAddress::random()), + destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), payload_hash: HexBinary::from(Hash::random().as_bytes()), }, ], diff --git a/ampd/src/handlers/evm_verify_worker_set.rs b/ampd/src/handlers/evm_verify_worker_set.rs index 099efc502..ff835a96d 100644 --- a/ampd/src/handlers/evm_verify_worker_set.rs +++ b/ampd/src/handlers/evm_verify_worker_set.rs @@ -216,7 +216,7 @@ mod tests { fn get_poll_started_event() -> Event { let poll_started = PollStarted::WorkerSet { worker_set: WorkerSetConfirmation { - tx_id: format!("0x{:x}", Hash::random()), + tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), event_index: 100, operators: Operators { threshold: 40u64.into(), @@ -239,7 +239,9 @@ mod tests { metadata: PollMetadata { poll_id: "100".parse().unwrap(), source_chain: "ethereum".parse().unwrap(), - source_gateway_address: "0x4f4495243837681061c4743b74eedf548d5686a5".into(), + source_gateway_address: "0x4f4495243837681061c4743b74eedf548d5686a5" + .parse() + .unwrap(), confirmation_height: 15, expires_at: 100, participants: vec![ diff --git a/contracts/connection-router/src/contract.rs b/contracts/connection-router/src/contract.rs index 23be7ee8b..d2a843f1a 100644 --- a/contracts/connection-router/src/contract.rs +++ b/contracts/connection-router/src/contract.rs @@ -184,7 +184,7 @@ pub mod execute { }); } - if msgs.iter().any(|msg| msg.uid.chain != source_chain.name) { + if msgs.iter().any(|msg| msg.cc_id.chain != source_chain.name) { return Err(ContractError::WrongSourceChain); } diff --git a/contracts/connection-router/src/events.rs b/contracts/connection-router/src/events.rs index 3244ae6dc..56098e6b0 100644 --- a/contracts/connection-router/src/events.rs +++ b/contracts/connection-router/src/events.rs @@ -103,8 +103,8 @@ impl From for Event { impl From for Vec { fn from(other: NewMessage) -> Self { vec![ - ("id", other.uid.id).into(), - ("source_chain", other.uid.chain).into(), + ("id", other.cc_id.id).into(), + ("source_chain", other.cc_id.chain).into(), ("source_addresses", other.source_address.deref()).into(), ("destination_chain", other.destination_chain).into(), ("destination_addresses", other.destination_address.deref()).into(), diff --git a/contracts/connection-router/src/state.rs b/contracts/connection-router/src/state.rs index a1536e963..3f06e0cff 100644 --- a/contracts/connection-router/src/state.rs +++ b/contracts/connection-router/src/state.rs @@ -89,7 +89,7 @@ impl<'a> IndexList for ChainEndpointIndexes<'a> { #[cw_serde] pub struct NewMessage { - pub uid: CrossChainUid, + pub cc_id: CrossChainId, pub destination_address: Address, pub destination_chain: ChainName, pub source_address: Address, @@ -102,10 +102,10 @@ impl TryFrom for Message { fn try_from(msg: NewMessage) -> Result { Ok(Message { - id: format!("{}", msg.uid).parse()?, + id: format!("{}", msg.cc_id).parse()?, destination_address: msg.destination_address.to_string(), destination_chain: msg.destination_chain, - source_chain: msg.uid.chain, + source_chain: msg.cc_id.chain, source_address: msg.source_address.to_string(), payload_hash: msg.payload_hash, }) @@ -127,7 +127,7 @@ impl TryFrom for NewMessage { } Ok(NewMessage { - uid: CrossChainUid { + cc_id: CrossChainId { id: id.parse()?, chain: msg.source_chain, }, @@ -310,18 +310,18 @@ impl KeyDeserialize for MessageId { } #[cw_serde] -pub struct CrossChainUid { +pub struct CrossChainId { pub chain: ChainName, pub id: MessageId, } -impl Display for CrossChainUid { +impl Display for CrossChainId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}{}{}", &self.chain, ID_SEPARATOR, &self.id) } } -impl PrimaryKey<'_> for CrossChainUid { +impl PrimaryKey<'_> for CrossChainId { type Prefix = ChainName; type SubPrefix = (); type Suffix = MessageId; @@ -334,12 +334,12 @@ impl PrimaryKey<'_> for CrossChainUid { } } -impl KeyDeserialize for CrossChainUid { +impl KeyDeserialize for CrossChainId { type Output = Self; fn from_vec(value: Vec) -> StdResult { let (chain, id) = <(ChainName, MessageId)>::from_vec(value)?; - Ok(CrossChainUid { chain, id }) + Ok(CrossChainId { chain, id }) } } @@ -441,14 +441,14 @@ mod tests { use rand::{thread_rng, Rng}; use sha3::{Digest, Sha3_256}; - use crate::state::{ChainName, CrossChainUid, MessageId, NewMessage, ID_SEPARATOR}; + use crate::state::{ChainName, CrossChainId, MessageId, NewMessage, ID_SEPARATOR}; use crate::ContractError; #[test] fn create_correct_global_message_id() { let msg = dummy_message(); - assert_eq!(msg.uid.to_string(), "chain:hash:index".to_string()); + assert_eq!(msg.cc_id.to_string(), "chain:hash:index".to_string()); } #[test] @@ -456,7 +456,7 @@ mod tests { // will cause this test to fail, indicating that a migration is needed. fn test_message_struct_unchanged() { let expected_message_hash = - "cf6a6e654af0d60891b91cb014b1c12c7d2d95edd5f3cca54125d8a9917b240e"; + "252e44129132a3bac9b26ee4d7f247453bd80b2aa0513050c274d5c5cf2f7153"; let msg = dummy_message(); @@ -539,7 +539,7 @@ mod tests { #[test] fn serialize_global_message_id() { - let id = CrossChainUid { + let id = CrossChainId { chain: "ethereum".parse().unwrap(), id: "hash:id".parse().unwrap(), }; @@ -550,7 +550,7 @@ mod tests { fn dummy_message() -> NewMessage { NewMessage { - uid: CrossChainUid { + cc_id: CrossChainId { id: "hash:index".parse().unwrap(), chain: "chain".parse().unwrap(), }, diff --git a/contracts/connection-router/tests/mock.rs b/contracts/connection-router/tests/mock.rs index 1e8564c2f..c8cf1ccaf 100644 --- a/contracts/connection-router/tests/mock.rs +++ b/contracts/connection-router/tests/mock.rs @@ -1,11 +1,11 @@ -use connection_router::state::{CrossChainUid, NewMessage}; +use connection_router::state::{CrossChainId, NewMessage}; use connection_router::ContractError; use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw_multi_test::{App, ContractWrapper, Executor}; use cw_storage_plus::Map; -const MOCK_GATEWAY_MESSAGES: Map = Map::new("gateway_messages"); +const MOCK_GATEWAY_MESSAGES: Map = Map::new("gateway_messages"); #[cw_serde] pub enum MockGatewayExecuteMsg { @@ -21,7 +21,7 @@ pub fn mock_gateway_execute( match msg { MockGatewayExecuteMsg::RouteMessages(messages) => { for m in messages { - MOCK_GATEWAY_MESSAGES.save(deps.storage, m.uid.clone(), &m)?; + MOCK_GATEWAY_MESSAGES.save(deps.storage, m.cc_id.clone(), &m)?; } Ok(Response::new()) } @@ -30,7 +30,7 @@ pub fn mock_gateway_execute( #[cw_serde] pub enum MockGatewayQueryMsg { - GetMessages { ids: Vec }, + GetMessages { ids: Vec }, } pub fn mock_gateway_query(deps: Deps, _env: Env, msg: MockGatewayQueryMsg) -> StdResult { let mut msgs = vec![]; @@ -57,7 +57,7 @@ pub fn get_gateway_messages( .query_wasm_smart( gateway_address, &MockGatewayQueryMsg::GetMessages { - ids: msgs.iter().map(|m| m.uid.clone()).collect(), + ids: msgs.iter().map(|m| m.cc_id.clone()).collect(), }, ) .unwrap() diff --git a/contracts/connection-router/tests/test.rs b/contracts/connection-router/tests/test.rs index e7341ab5e..d75c7974d 100644 --- a/contracts/connection-router/tests/test.rs +++ b/contracts/connection-router/tests/test.rs @@ -7,7 +7,7 @@ use cw_multi_test::{App, ContractWrapper, Executor}; use connection_router::contract::*; use connection_router::error::ContractError; use connection_router::msg::{ExecuteMsg, InstantiateMsg}; -use connection_router::state::{ChainName, CrossChainUid, GatewayDirection, NewMessage}; +use connection_router::state::{ChainName, CrossChainId, GatewayDirection, NewMessage}; pub mod mock; @@ -85,7 +85,7 @@ fn generate_messages( *nonce = *nonce + 1; let id = format!("tx_id:{}", nonce); msgs.push(NewMessage { - uid: CrossChainUid { + cc_id: CrossChainId { id: id.parse().unwrap(), chain: src_chain.chain_name.clone(), }, diff --git a/contracts/voting-verifier/src/contract.rs b/contracts/voting-verifier/src/contract.rs index 88707d8ed..b471b33fc 100644 --- a/contracts/voting-verifier/src/contract.rs +++ b/contracts/voting-verifier/src/contract.rs @@ -1,10 +1,9 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{ + to_binary, Attribute, Binary, Deps, DepsMut, Env, Event, MessageInfo, Response, StdResult, +}; -use connection_router::state; - -use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{Config, CONFIG, CONFIRMED_WORKER_SETS}; use crate::{execute, query}; @@ -18,7 +17,7 @@ pub fn instantiate( ) -> Result { let config = Config { service_name: msg.service_name, - service_registry: deps.api.addr_validate(&msg.service_registry_address)?, + service_registry_contract: deps.api.addr_validate(&msg.service_registry_address)?, source_gateway_address: msg.source_gateway_address, voting_threshold: msg.voting_threshold, block_expiry: msg.block_expiry, @@ -27,7 +26,8 @@ pub fn instantiate( }; CONFIG.save(deps.storage, &config)?; - Ok(Response::new().add_event(config.into())) + Ok(Response::new() + .add_event(Event::new("instantiated").add_attributes(>::from(config)))) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -38,28 +38,7 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::VerifyMessages { messages } => { - if messages.is_empty() { - Err(ContractError::EmptyMessages)?; - } - - // todo: extract to conversion function - let messages = messages - .into_iter() - .map(state::Message::try_from) - .collect::, _>>()?; - - let source_chain = CONFIG.load(deps.storage)?.source_chain; - - if messages - .iter() - .any(|message| !message.source_chain.eq(&source_chain)) - { - Err(ContractError::SourceChainMismatch(source_chain))?; - } - - execute::verify_messages(deps, env, messages) - } + ExecuteMsg::VerifyMessages { messages } => execute::verify_messages(deps, env, messages), ExecuteMsg::Vote { poll_id, votes } => execute::vote(deps, env, info, poll_id, votes), ExecuteMsg::EndPoll { poll_id } => execute::end_poll(deps, env, poll_id), ExecuteMsg::ConfirmWorkerSet { @@ -73,11 +52,6 @@ pub fn execute( pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::IsVerified { messages } => { - let messages = messages - .into_iter() - .map(state::Message::try_from) - .collect::, _>>()?; - to_binary(&query::verification_statuses(deps, messages)?) } diff --git a/contracts/voting-verifier/src/events.rs b/contracts/voting-verifier/src/events.rs index 8e53a3158..d649aedea 100644 --- a/contracts/voting-verifier/src/events.rs +++ b/contracts/voting-verifier/src/events.rs @@ -1,33 +1,42 @@ -use std::str::FromStr; use std::vec::Vec; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Attribute, Event, HexBinary}; -use serde_json::to_string; +use axelar_wasm_std::nonempty; use axelar_wasm_std::operators::Operators; use axelar_wasm_std::voting::PollID; -use connection_router::state::{ChainName, Message, MessageId, ID_SEPARATOR}; +use connection_router::state::{Address, ChainName, MessageId, NewMessage, ID_SEPARATOR}; use crate::error::ContractError; use crate::state::Config; -impl From for Event { +impl From for Vec { fn from(other: Config) -> Self { - Event::new("instantiated") - .add_attribute("service_name", other.service_name) - .add_attribute("service_registry_contract", other.service_registry) - .add_attribute("source_gateway_address", other.source_gateway_address) - .add_attribute("voting_threshold", other.voting_threshold.to_string()) - .add_attribute("block_expiry", other.block_expiry.to_string()) - .add_attribute("confirmation_height", other.confirmation_height.to_string()) + vec![ + ("service_name", other.service_name.to_string()), + ( + "service_registry_contract", + other.service_registry_contract.to_string(), + ), + ( + "source_gateway_address", + other.source_gateway_address.to_string(), + ), + ("voting_threshold", other.voting_threshold.to_string()), + ("block_expiry", other.block_expiry.to_string()), + ("confirmation_height", other.confirmation_height.to_string()), + ] + .into_iter() + .map(Attribute::from) + .collect() } } pub struct PollMetadata { pub poll_id: PollID, pub source_chain: ChainName, - pub source_gateway_address: String, + pub source_gateway_address: nonempty::String, pub confirmation_height: u64, pub expires_at: u64, pub participants: Vec, @@ -35,7 +44,7 @@ pub struct PollMetadata { pub enum PollStarted { Messages { - messages: Vec, + messages: Vec, metadata: PollMetadata, }, WorkerSet { @@ -49,22 +58,23 @@ impl From for Vec { vec![ ( "poll_id", - to_string(&value.poll_id).expect("failed to serialize poll_id"), + &serde_json::to_string(&value.poll_id).expect("failed to serialize poll_id"), ), + ("source_chain", &value.source_chain.to_string()), + ("source_gateway_address", &value.source_gateway_address), ( - "source_chain", - to_string(&value.source_chain).expect("failed to serialize source_chain"), + "confirmation_height", + &value.confirmation_height.to_string(), ), - ("source_gateway_address", value.source_gateway_address), - ("confirmation_height", value.confirmation_height.to_string()), - ("expires_at", value.expires_at.to_string()), + ("expires_at", &value.expires_at.to_string()), ( "participants", - to_string(&value.participants).expect("failed to serialize participants"), + &serde_json::to_string(&value.participants) + .expect("failed to serialize participants"), ), ] .into_iter() - .map(Into::into) + .map(Attribute::from) .collect() } } @@ -78,7 +88,7 @@ impl From for Event { } => Event::new("messages_poll_started") .add_attribute( "messages", - to_string(&data).expect("failed to serialize messages"), + serde_json::to_string(&data).expect("failed to serialize messages"), ) .add_attributes(Vec::<_>::from(metadata)), PollStarted::WorkerSet { @@ -87,7 +97,8 @@ impl From for Event { } => Event::new("worker_set_poll_started") .add_attribute( "worker_set", - to_string(&data).expect("failed to serialize worker set confirmation"), + serde_json::to_string(&data) + .expect("failed to serialize worker set confirmation"), ) .add_attributes(Vec::<_>::from(metadata)), } @@ -96,7 +107,7 @@ impl From for Event { #[cw_serde] pub struct WorkerSetConfirmation { - pub tx_id: String, + pub tx_id: nonempty::String, pub event_index: u64, pub operators: Operators, } @@ -113,22 +124,22 @@ impl WorkerSetConfirmation { } #[cw_serde] -pub struct MessageByTxEvent { - pub tx_id: String, +pub struct TxEventConfirmation { + pub tx_id: nonempty::String, pub event_index: u64, - pub destination_address: String, + pub destination_address: Address, pub destination_chain: ChainName, - pub source_address: String, + pub source_address: Address, pub payload_hash: HexBinary, } -impl TryFrom for MessageByTxEvent { +impl TryFrom for TxEventConfirmation { type Error = ContractError; - fn try_from(other: Message) -> Result { - let (tx_id, event_index) = parse_message_id(&other.id)?; + fn try_from(other: NewMessage) -> Result { + let (tx_id, event_index) = parse_message_id(&other.cc_id.id)?; - Ok(MessageByTxEvent { + Ok(TxEventConfirmation { tx_id, event_index, destination_address: other.destination_address, @@ -139,25 +150,17 @@ impl TryFrom for MessageByTxEvent { } } -impl FromStr for MessageByTxEvent { - type Err = serde_json::Error; - - fn from_str(s: &str) -> Result { - serde_json::from_str(s) - } -} - -fn parse_message_id(message_id: &MessageId) -> Result<(String, u64), ContractError> { - // expected format: :: - let components = message_id.as_str().split(ID_SEPARATOR).collect::>(); +fn parse_message_id(message_id: &MessageId) -> Result<(nonempty::String, u64), ContractError> { + // expected format: : + let components = message_id.split(ID_SEPARATOR).collect::>(); - if components.len() != 3 { + if components.len() != 2 { return Err(ContractError::InvalidMessageID(message_id.clone())); } Ok(( - components[1].to_string(), - components[2] + components[0].try_into()?, + components[1] .parse::() .map_err(|_| ContractError::InvalidMessageID(message_id.clone()))?, )) @@ -173,7 +176,7 @@ impl From for Event { Event::new("voted") .add_attribute( "poll_id", - to_string(&other.poll_id).expect("failed to serialize poll_id"), + serde_json::to_string(&other.poll_id).expect("failed to serialize poll_id"), ) .add_attribute("voter", other.voter) } @@ -189,11 +192,11 @@ impl From for Event { Event::new("poll_ended") .add_attribute( "poll_id", - to_string(&other.poll_id).expect("failed to serialize poll_id"), + serde_json::to_string(&other.poll_id).expect("failed to serialize poll_id"), ) .add_attribute( "results", - to_string(&other.results).expect("failed to serialize results"), + serde_json::to_string(&other.results).expect("failed to serialize results"), ) } } diff --git a/contracts/voting-verifier/src/execute.rs b/contracts/voting-verifier/src/execute.rs index 85eab4fb2..e264d4ddc 100644 --- a/contracts/voting-verifier/src/execute.rs +++ b/contracts/voting-verifier/src/execute.rs @@ -8,13 +8,13 @@ use axelar_wasm_std::{ snapshot, voting::{Poll, WeightedPoll}, }; -use connection_router::state::{ChainName, Message, MessageId}; +use connection_router::state::{ChainName, MessageId, NewMessage}; use service_registry::msg::QueryMsg; use service_registry::state::Worker; use crate::error::ContractError; use crate::events::{ - MessageByTxEvent, PollEnded, PollMetadata, PollStarted, Voted, WorkerSetConfirmation, + PollEnded, PollMetadata, PollStarted, TxEventConfirmation, Voted, WorkerSetConfirmation, }; use crate::execute::VerificationStatus::{Pending, Verified}; use crate::msg::{EndPollResponse, VerifyMessagesResponse}; @@ -26,8 +26,8 @@ use crate::state::{ }; enum VerificationStatus { - Verified(Message), - Pending(Message), + Verified(NewMessage), + Pending(NewMessage), } pub fn confirm_worker_set( @@ -47,20 +47,20 @@ pub fn confirm_worker_set( let snapshot = take_snapshot(deps.as_ref(), &env, &config.source_chain)?; let participants = snapshot.get_participants(); - let id = create_worker_set_poll( + let poll_id = create_worker_set_poll( deps.storage, env.block.height, config.block_expiry, snapshot, )?; - PENDING_WORKER_SETS.save(deps.storage, id, &new_operators)?; + PENDING_WORKER_SETS.save(deps.storage, poll_id, &new_operators)?; Ok(Response::new().add_event( PollStarted::WorkerSet { worker_set: WorkerSetConfirmation::new(message_id, new_operators)?, metadata: PollMetadata { - poll_id: id, + poll_id, source_chain: config.source_chain, source_gateway_address: config.source_gateway_address, confirmation_height: config.confirmation_height, @@ -75,8 +75,21 @@ pub fn confirm_worker_set( pub fn verify_messages( deps: DepsMut, env: Env, - messages: Vec, + messages: Vec, ) -> Result { + if messages.is_empty() { + Err(ContractError::EmptyMessages)?; + } + + let source_chain = CONFIG.load(deps.storage)?.source_chain; + + if messages + .iter() + .any(|message| message.cc_id.chain.ne(&source_chain)) + { + Err(ContractError::SourceChainMismatch(source_chain))?; + } + let config = CONFIG.load(deps.storage)?; let messages = messages @@ -96,13 +109,13 @@ pub fn verify_messages( verification_statuses: messages .iter() .map(|status| match status { - Verified(message) => (message.id.to_string(), true), - Pending(message) => (message.id.to_string(), false), + Verified(message) => (message.cc_id.clone(), true), + Pending(message) => (message.cc_id.clone(), false), }) .collect(), })?); - let pending_messages: Vec = messages + let pending_messages: Vec = messages .into_iter() .filter_map(|status| match status { Pending(message) => Some(message), @@ -114,7 +127,7 @@ pub fn verify_messages( return Ok(response); } - let snapshot = take_snapshot(deps.as_ref(), &env, &pending_messages[0].source_chain)?; + let snapshot = take_snapshot(deps.as_ref(), &env, &pending_messages[0].cc_id.chain)?; let participants = snapshot.get_participants(); let id = create_messages_poll( deps.storage, @@ -129,7 +142,7 @@ pub fn verify_messages( let evm_messages = pending_messages .into_iter() .map(TryInto::try_into) - .collect::, _>>()?; + .collect::, _>>()?; Ok(response.add_event( PollStarted::Messages { @@ -195,11 +208,11 @@ fn end_poll_messages( true => Some(message), false => None, }) - .collect::>(); + .collect::>(); for message in messages { if !is_message_verified(deps.as_ref(), message)? { - VERIFIED_MESSAGES.save(deps.storage, &message.id, message)?; + VERIFIED_MESSAGES.save(deps.storage, &message.cc_id, message)?; } } @@ -268,12 +281,12 @@ fn take_snapshot( // todo: add chain param to query after service registry updated // query service registry for active workers let active_workers_query = QueryMsg::GetActiveWorkers { - service_name: config.service_name, + service_name: config.service_name.to_string(), chain_name: chain.clone().into(), }; let workers: Vec = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: config.service_registry.to_string(), + contract_addr: config.service_registry_contract.to_string(), msg: to_binary(&active_workers_query)?, }))?; @@ -322,7 +335,7 @@ fn create_messages_poll( fn remove_pending_message( store: &mut dyn Storage, poll_id: PollID, -) -> Result, ContractError> { +) -> Result, ContractError> { let pending_messages = PENDING_MESSAGES .may_load(store, poll_id)? .ok_or(ContractError::PollNotFound)?; diff --git a/contracts/voting-verifier/src/msg.rs b/contracts/voting-verifier/src/msg.rs index 0c92c2efb..0e7ab01ac 100644 --- a/contracts/voting-verifier/src/msg.rs +++ b/contracts/voting-verifier/src/msg.rs @@ -1,20 +1,20 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use axelar_wasm_std::{ + nonempty, operators::Operators, voting::{PollID, PollResult}, Threshold, }; -use connection_router::msg::Message; -use connection_router::state::{ChainName, MessageId}; +use connection_router::state::{ChainName, CrossChainId, MessageId, NewMessage}; #[cw_serde] pub struct InstantiateMsg { // params to query register service - pub service_registry_address: String, - pub service_name: String, + pub service_registry_address: nonempty::String, + pub service_name: nonempty::String, - pub source_gateway_address: String, + pub source_gateway_address: nonempty::String, pub voting_threshold: Threshold, pub block_expiry: u64, pub confirmation_height: u64, @@ -38,7 +38,7 @@ pub enum ExecuteMsg { // returns a vector of true/false values, indicating current verification status for each message // starts a poll for any not yet verified messages VerifyMessages { - messages: Vec, + messages: Vec, }, // Starts a poll to confirm a worker set update on the external evm gateway @@ -51,7 +51,7 @@ pub enum ExecuteMsg { #[cw_serde] pub struct Poll { poll_id: PollID, - messages: Vec, + messages: Vec, } #[cw_serde] @@ -61,7 +61,7 @@ pub enum QueryMsg { GetPoll { poll_id: PollID }, #[returns(Vec<(String, bool)>)] - IsVerified { messages: Vec }, + IsVerified { messages: Vec }, #[returns(bool)] IsWorkerSetConfirmed { new_operators: Operators }, @@ -69,7 +69,7 @@ pub enum QueryMsg { #[cw_serde] pub struct VerifyMessagesResponse { - pub verification_statuses: Vec<(String, bool)>, + pub verification_statuses: Vec<(CrossChainId, bool)>, } #[cw_serde] diff --git a/contracts/voting-verifier/src/query.rs b/contracts/voting-verifier/src/query.rs index eec4fb51e..e87355e9d 100644 --- a/contracts/voting-verifier/src/query.rs +++ b/contracts/voting-verifier/src/query.rs @@ -1,24 +1,26 @@ +use connection_router::state::{CrossChainId, NewMessage}; use cosmwasm_std::{Deps, StdResult}; -use connection_router::state::Message; - use crate::error::ContractError; use crate::state::VERIFIED_MESSAGES; -pub fn verification_statuses(deps: Deps, messages: Vec) -> StdResult> { +pub fn verification_statuses( + deps: Deps, + messages: Vec, +) -> StdResult> { messages .into_iter() .map(|message| { - is_message_verified(deps, &message).map(|verified| (message.id.to_string(), verified)) + is_message_verified(deps, &message).map(|verified| (message.cc_id, verified)) }) .collect::, _>>() .map_err(Into::into) } -pub fn is_message_verified(deps: Deps, message: &Message) -> Result { - match VERIFIED_MESSAGES.may_load(deps.storage, &message.id)? { +pub fn is_message_verified(deps: Deps, message: &NewMessage) -> Result { + match VERIFIED_MESSAGES.may_load(deps.storage, &message.cc_id)? { Some(stored) if stored != *message => { - Err(ContractError::MessageMismatch(message.id.to_string())) + Err(ContractError::MessageMismatch(message.cc_id.to_string())) } Some(_) => Ok(true), None => Ok(false), diff --git a/contracts/voting-verifier/src/state.rs b/contracts/voting-verifier/src/state.rs index fb3521b36..d7a089c0a 100644 --- a/contracts/voting-verifier/src/state.rs +++ b/contracts/voting-verifier/src/state.rs @@ -3,18 +3,18 @@ use cosmwasm_std::Addr; use cw_storage_plus::{Item, Map}; use axelar_wasm_std::{ - counter, + counter, nonempty, operators::Operators, voting::{PollID, WeightedPoll}, Threshold, }; -use connection_router::state::{ChainName, Message, MessageId}; +use connection_router::state::{ChainName, CrossChainId, NewMessage}; #[cw_serde] pub struct Config { - pub service_registry: Addr, - pub service_name: String, - pub source_gateway_address: String, + pub service_registry_contract: Addr, + pub service_name: nonempty::String, + pub source_gateway_address: nonempty::String, pub voting_threshold: Threshold, pub block_expiry: u64, // number of blocks after which a poll expires pub confirmation_height: u64, @@ -31,13 +31,13 @@ pub const POLL_ID: counter::Counter = counter::Counter::new("poll_id"); pub const POLLS: Map = Map::new("polls"); -pub const PENDING_MESSAGES: Map> = Map::new("pending_messages"); +pub const PENDING_MESSAGES: Map> = Map::new("pending_messages"); -pub const VERIFIED_MESSAGES: Map<&MessageId, Message> = Map::new("verified_messages"); +pub const VERIFIED_MESSAGES: Map<&CrossChainId, NewMessage> = Map::new("verified_messages"); pub const CONFIG: Item = Item::new("config"); -// primary key is hash of Operators struct -pub const CONFIRMED_WORKER_SETS: Map, ()> = Map::new("confirmed_worker_sets"); +type OperatorsHash = Vec; +pub const CONFIRMED_WORKER_SETS: Map = Map::new("confirmed_worker_sets"); pub const PENDING_WORKER_SETS: Map = Map::new("pending_worker_sets"); diff --git a/contracts/voting-verifier/tests/tests.rs b/contracts/voting-verifier/tests/tests.rs index cff2f3ec8..9c843d312 100644 --- a/contracts/voting-verifier/tests/tests.rs +++ b/contracts/voting-verifier/tests/tests.rs @@ -2,9 +2,8 @@ use cosmwasm_std::{from_binary, Addr, Uint64}; use cw_multi_test::{App, ContractWrapper, Executor}; use axelar_wasm_std::operators::Operators; -use axelar_wasm_std::Threshold; -use connection_router::msg::Message; -use connection_router::state::{MessageId, ID_SEPARATOR}; +use axelar_wasm_std::{nonempty, Threshold}; +use connection_router::state::{ChainName, CrossChainId, MessageId, NewMessage, ID_SEPARATOR}; use service_registry::state::Worker; use voting_verifier::{contract, error::ContractError, msg}; @@ -13,17 +12,19 @@ use crate::mock::make_mock_service_registry; pub mod mock; const SENDER: &str = "sender"; -const SOURCE_CHAIN: &str = "source_chain"; +fn source_chain() -> ChainName { + "source_chain".parse().unwrap() +} -fn initialize_contract(app: &mut App, service_registry_address: String) -> Addr { +fn initialize_contract(app: &mut App, service_registry_address: nonempty::String) -> Addr { let msg = msg::InstantiateMsg { service_registry_address, - service_name: "service_name".to_string(), + service_name: "service_name".parse().unwrap(), voting_threshold: Threshold::try_from((1u64, 2u64)).unwrap(), block_expiry: 100, confirmation_height: 100, - source_gateway_address: "gateway_address".to_string(), - source_chain: SOURCE_CHAIN.to_string().try_into().unwrap(), + source_gateway_address: "gateway_address".parse().unwrap(), + source_chain: source_chain(), }; let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); @@ -43,18 +44,20 @@ fn initialize_contract(app: &mut App, service_registry_address: String) -> Addr address } -fn message_id(source_chain: &str, id: &str, index: u64) -> String { - format!("{source_chain}{ID_SEPARATOR}{id}{ID_SEPARATOR}{index}") +fn message_id(id: &str, index: u64) -> String { + format!("{}{}{}", id, ID_SEPARATOR, index) } -fn messages(len: u64) -> Vec { +fn messages(len: u64) -> Vec { (0..len) - .map(|i| Message { - id: message_id(SOURCE_CHAIN, format!("id{i}").as_str(), 0), - source_chain: SOURCE_CHAIN.to_string(), - source_address: format!("source_address{i}"), - destination_chain: format!("destination_chain{i}"), - destination_address: format!("destination_address{i}"), + .map(|i| NewMessage { + cc_id: CrossChainId { + chain: source_chain(), + id: format!("id:{i}").parse().unwrap(), + }, + source_address: format!("source_address{i}").parse().unwrap(), + destination_chain: format!("destination_chain{i}").parse().unwrap(), + destination_address: format!("destination_address{i}").parse().unwrap(), payload_hash: vec![0, 0, 0, 0].into(), }) .collect() @@ -66,24 +69,29 @@ fn should_failed_if_messages_are_not_from_same_source() { let service_registry_address = make_mock_service_registry(&mut app); - let contract_address = initialize_contract(&mut app, service_registry_address.into_string()); + let contract_address = + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let msg = msg::ExecuteMsg::VerifyMessages { messages: vec![ - Message { - id: message_id(SOURCE_CHAIN, "id1", 0), - source_chain: SOURCE_CHAIN.to_string(), - source_address: "source_address1".to_string(), - destination_chain: "destination_chain1".to_string(), - destination_address: "destination_address1".to_string(), + NewMessage { + cc_id: CrossChainId { + chain: source_chain(), + id: "id:1".parse().unwrap(), + }, + source_address: "source_address1".parse().unwrap(), + destination_chain: "destination_chain1".parse().unwrap(), + destination_address: "destination_address1".parse().unwrap(), payload_hash: vec![0, 0, 0, 0].into(), }, - Message { - id: message_id("other_chain", "id2", 0), - source_chain: "other_chain".to_string(), - source_address: "source_address2".to_string(), - destination_chain: "destination_chain2".to_string(), - destination_address: "destination_address2".to_string(), + NewMessage { + cc_id: CrossChainId { + chain: "other_chain".parse().unwrap(), + id: "id:2".parse().unwrap(), + }, + source_address: "source_address2".parse().unwrap(), + destination_chain: "destination_chain2".parse().unwrap(), + destination_address: "destination_address2".parse().unwrap(), payload_hash: vec![0, 0, 0, 0].into(), }, ], @@ -96,10 +104,8 @@ fn should_failed_if_messages_are_not_from_same_source() { err.downcast::() .unwrap() .to_string(), - axelar_wasm_std::ContractError::from(ContractError::SourceChainMismatch( - SOURCE_CHAIN.parse().unwrap() - )) - .to_string() + axelar_wasm_std::ContractError::from(ContractError::SourceChainMismatch(source_chain(),)) + .to_string() ); } @@ -109,27 +115,11 @@ fn should_verify_messages_if_not_verified() { let service_registry_address = make_mock_service_registry(&mut app); - let contract_address = initialize_contract(&mut app, service_registry_address.into_string()); + let contract_address = + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let msg = msg::ExecuteMsg::VerifyMessages { - messages: vec![ - Message { - id: message_id(SOURCE_CHAIN, "id1", 0), - source_chain: SOURCE_CHAIN.to_string(), - source_address: "source_address1".to_string(), - destination_chain: "destination_chain1".to_string(), - destination_address: "destination_address1".to_string(), - payload_hash: vec![0, 0, 0, 0].into(), - }, - Message { - id: message_id(SOURCE_CHAIN, "id2", 0), - source_chain: SOURCE_CHAIN.to_string(), - source_address: "source_address2".to_string(), - destination_chain: "destination_chain2".to_string(), - destination_address: "destination_address2".to_string(), - payload_hash: vec![0, 0, 0, 0].into(), - }, - ], + messages: messages(2), }; let res = app @@ -140,8 +130,20 @@ fn should_verify_messages_if_not_verified() { assert_eq!( reply.verification_statuses, vec![ - ("source_chain:id1:0".to_string(), false), - ("source_chain:id2:0".to_string(), false) + ( + CrossChainId { + id: "id:0".parse().unwrap(), + chain: source_chain() + }, + false + ), + ( + CrossChainId { + id: "id:1".parse().unwrap(), + chain: source_chain() + }, + false + ), ] ); } @@ -152,7 +154,8 @@ fn should_query_message_statuses() { let service_registry_address = make_mock_service_registry(&mut app); - let contract_address = initialize_contract(&mut app, service_registry_address.into_string()); + let contract_address = + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let messages = messages(10); @@ -171,15 +174,15 @@ fn should_query_message_statuses() { reply.verification_statuses, messages .iter() - .map(|message| (message.id.to_string(), false)) - .collect::>() + .map(|message| (message.cc_id.clone(), false)) + .collect::>() ); let query = msg::QueryMsg::IsVerified { messages: messages.clone(), }; - let statuses: Vec<(String, bool)> = app + let statuses: Vec<(CrossChainId, bool)> = app .wrap() .query_wasm_smart(contract_address.clone(), &query) .unwrap(); @@ -188,8 +191,8 @@ fn should_query_message_statuses() { statuses, messages .iter() - .map(|message| (message.id.to_string(), false)) - .collect::>() + .map(|message| (message.cc_id.clone(), false)) + .collect::>() ); let msg: msg::ExecuteMsg = msg::ExecuteMsg::Vote { @@ -220,7 +223,7 @@ fn should_query_message_statuses() { app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]) .unwrap(); - let statuses: Vec<(String, bool)> = app + let statuses: Vec<(CrossChainId, bool)> = app .wrap() .query_wasm_smart(contract_address.clone(), &query) .unwrap(); @@ -230,8 +233,8 @@ fn should_query_message_statuses() { messages .iter() .enumerate() - .map(|(i, message)| (message.id.to_string(), i % 2 == 0)) - .collect::>() + .map(|(i, message)| (message.cc_id.clone(), i % 2 == 0)) + .collect::>() ); } @@ -241,14 +244,15 @@ fn should_start_worker_set_confirmation() { let service_registry_address = make_mock_service_registry(&mut app); - let contract_address = initialize_contract(&mut app, service_registry_address.into_string()); + let contract_address = + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let operators = Operators { weights_by_addresses: vec![(vec![0, 1, 0, 1].into(), 1u64.into())], threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id("id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -269,7 +273,7 @@ fn should_confirm_worker_set() { let service_registry_address = make_mock_service_registry(&mut app); let contract_address = - initialize_contract(&mut app, service_registry_address.clone().into_string()); + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let workers: Vec = app .wrap() @@ -277,7 +281,7 @@ fn should_confirm_worker_set() { service_registry_address, &service_registry::msg::QueryMsg::GetActiveWorkers { service_name: "service_name".to_string(), - chain_name: SOURCE_CHAIN.into(), + chain_name: source_chain().to_string(), }, ) .unwrap(); @@ -287,7 +291,7 @@ fn should_confirm_worker_set() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id("id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -323,7 +327,7 @@ fn should_not_confirm_worker_set() { let service_registry_address = make_mock_service_registry(&mut app); let contract_address = - initialize_contract(&mut app, service_registry_address.clone().into_string()); + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let workers: Vec = app .wrap() @@ -331,7 +335,7 @@ fn should_not_confirm_worker_set() { service_registry_address, &service_registry::msg::QueryMsg::GetActiveWorkers { service_name: "service_name".to_string(), - chain_name: SOURCE_CHAIN.into(), + chain_name: source_chain().to_string(), }, ) .unwrap(); @@ -341,7 +345,7 @@ fn should_not_confirm_worker_set() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id("id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -377,7 +381,7 @@ fn should_confirm_worker_set_after_failed() { let service_registry_address = make_mock_service_registry(&mut app); let contract_address = - initialize_contract(&mut app, service_registry_address.clone().into_string()); + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let workers: Vec = app .wrap() @@ -385,7 +389,7 @@ fn should_confirm_worker_set_after_failed() { service_registry_address, &service_registry::msg::QueryMsg::GetActiveWorkers { service_name: "service_name".to_string(), - chain_name: SOURCE_CHAIN.into(), + chain_name: source_chain().to_string(), }, ) .unwrap(); @@ -395,7 +399,7 @@ fn should_confirm_worker_set_after_failed() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id("id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -427,7 +431,7 @@ fn should_confirm_worker_set_after_failed() { // try again, and this time vote true let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id("id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -463,7 +467,7 @@ fn should_not_confirm_twice() { let service_registry_address = make_mock_service_registry(&mut app); let contract_address = - initialize_contract(&mut app, service_registry_address.clone().into_string()); + initialize_contract(&mut app, service_registry_address.as_ref().parse().unwrap()); let workers: Vec = app .wrap() @@ -471,7 +475,7 @@ fn should_not_confirm_twice() { service_registry_address, &service_registry::msg::QueryMsg::GetActiveWorkers { service_name: "service_name".to_string(), - chain_name: SOURCE_CHAIN.into(), + chain_name: source_chain().to_string(), }, ) .unwrap(); @@ -481,7 +485,7 @@ fn should_not_confirm_twice() { threshold: 1u64.into(), }; let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id("id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); @@ -504,7 +508,7 @@ fn should_not_confirm_twice() { // try again, should fail let msg = msg::ExecuteMsg::ConfirmWorkerSet { - message_id: MessageId::try_from(message_id(SOURCE_CHAIN, "id", 0)).unwrap(), + message_id: MessageId::try_from(message_id("id", 0)).unwrap(), new_operators: operators.clone(), }; let res = app.execute_contract(Addr::unchecked(SENDER), contract_address.clone(), &msg, &[]); From 45fcc060466533b6f4642ae73473426680f0a508 Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 21 Sep 2023 16:23:25 -0400 Subject: [PATCH 3/8] refactor: migrate aggregate verifier to use NewMessage (#113) --- Cargo.lock | 1 + contracts/aggregate-verifier/Cargo.toml | 1 + contracts/aggregate-verifier/src/contract.rs | 31 +- contracts/aggregate-verifier/src/msg.rs | 7 +- contracts/aggregate-verifier/tests/mock.rs | 20 +- contracts/aggregate-verifier/tests/test.rs | 108 ++----- contracts/connection-router/src/state.rs | 23 ++ contracts/gateway/src/contract.rs | 13 +- contracts/gateway/tests/mock.rs | 15 +- contracts/gateway/tests/tests.rs | 282 +++++++++++++++---- contracts/voting-verifier/src/msg.rs | 2 +- packages/axelar-wasm-std/src/nonempty/vec.rs | 1 + 12 files changed, 315 insertions(+), 189 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da834117a..726cb56a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,6 +58,7 @@ dependencies = [ "serde", "serde_json", "thiserror", + "voting-verifier", ] [[package]] diff --git a/contracts/aggregate-verifier/Cargo.toml b/contracts/aggregate-verifier/Cargo.toml index f3a89a20d..36898c8e0 100644 --- a/contracts/aggregate-verifier/Cargo.toml +++ b/contracts/aggregate-verifier/Cargo.toml @@ -43,6 +43,7 @@ schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } serde_json = "1.0.89" thiserror = { workspace = true } +voting-verifier = { workspace = true, features = ["library"] } [dev-dependencies] cw-multi-test = "0.15.1" diff --git a/contracts/aggregate-verifier/src/contract.rs b/contracts/aggregate-verifier/src/contract.rs index 4b337258f..c54a70d3e 100644 --- a/contracts/aggregate-verifier/src/contract.rs +++ b/contracts/aggregate-verifier/src/contract.rs @@ -1,3 +1,4 @@ +use connection_router::state::CrossChainId; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ @@ -6,15 +7,14 @@ use cosmwasm_std::{ }; use cw_utils::{parse_reply_execute_data, MsgExecuteContractResponse}; +use voting_verifier::msg as voting_msg; + use crate::{ error::ContractError, msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, state::{Config, CONFIG}, }; -use connection_router::msg; -use connection_router::state; - use self::execute::verify_messages; #[cfg_attr(not(feature = "library"), entry_point)] @@ -38,28 +38,21 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::VerifyMessages { messages } => { - // todo: this conversion can go away once the message types are merged - let msgs = messages - .into_iter() - .map(state::Message::try_from) - .collect::, _>>()?; - - verify_messages(deps, msgs) - } + ExecuteMsg::VerifyMessages { messages } => verify_messages(deps, messages), } .map_err(axelar_wasm_std::ContractError::from) } pub mod execute { - use cosmwasm_std::{to_binary, SubMsg, WasmMsg}; + use connection_router::state::NewMessage; + use super::*; pub fn verify_messages( deps: DepsMut, - msgs: Vec, + msgs: Vec, ) -> Result { // Simply pass through to a single verifier for now. If there are multiple verification // methods in the future, as well as support for a callback when a message is actually @@ -68,9 +61,7 @@ pub mod execute { Ok(Response::new().add_submessage(SubMsg::reply_on_success( WasmMsg::Execute { contract_addr: verifier.to_string(), - msg: to_binary(&ExecuteMsg::VerifyMessages { - messages: msgs.into_iter().map(msg::Message::from).collect(), - })?, + msg: to_binary(&voting_msg::ExecuteMsg::VerifyMessages { messages: msgs })?, funds: vec![], }, VERIFY_REPLY, @@ -90,7 +81,7 @@ pub fn reply( match parse_reply_execute_data(reply) { Ok(MsgExecuteContractResponse { data: Some(data) }) => { // check format of data - let _: Vec<(String, bool)> = from_binary(&data)?; + let _: Vec<(CrossChainId, bool)> = from_binary(&data)?; // only one verifier, so just return the response as is Ok(Response::new().set_data(data)) @@ -109,11 +100,11 @@ pub fn reply( #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::IsVerified { messages: _ } => { + QueryMsg::IsVerified { messages } => { let verifier = CONFIG.load(deps.storage)?.verifier; deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: verifier.to_string(), - msg: to_binary(&msg)?, + msg: to_binary(&voting_msg::QueryMsg::IsVerified { messages })?, })) } } diff --git a/contracts/aggregate-verifier/src/msg.rs b/contracts/aggregate-verifier/src/msg.rs index 568b459ae..1c9181a88 100644 --- a/contracts/aggregate-verifier/src/msg.rs +++ b/contracts/aggregate-verifier/src/msg.rs @@ -1,4 +1,4 @@ -use connection_router::msg::Message; +use connection_router::state::NewMessage; use cosmwasm_schema::cw_serde; #[cw_serde] @@ -8,12 +8,11 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { - // Returns a vector of (String,bool) tuples for each passed in message, consisting of message ID and current verification status // Permissionless - VerifyMessages { messages: Vec }, + VerifyMessages { messages: Vec }, } #[cw_serde] pub enum QueryMsg { - IsVerified { messages: Vec }, + IsVerified { messages: Vec }, } diff --git a/contracts/aggregate-verifier/tests/mock.rs b/contracts/aggregate-verifier/tests/mock.rs index 4aaf0b3d1..f72bab14a 100644 --- a/contracts/aggregate-verifier/tests/mock.rs +++ b/contracts/aggregate-verifier/tests/mock.rs @@ -1,16 +1,16 @@ use aggregate_verifier::error::ContractError; -use connection_router::msg::Message; +use connection_router::state::{CrossChainId, NewMessage}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, DepsMut, Env, MessageInfo, Response}; use cw_multi_test::{App, ContractWrapper, Executor}; use cw_storage_plus::Map; -const MOCK_VOTING_VERIFIER_MESSAGES: Map = Map::new("voting_verifier_messages"); +const MOCK_VOTING_VERIFIER_MESSAGES: Map = Map::new("voting_verifier_messages"); #[cw_serde] pub enum MockVotingVerifierExecuteMsg { - VerifyMessages { messages: Vec }, - MessagesVerified { messages: Vec }, + VerifyMessages { messages: Vec }, + MessagesVerified { messages: Vec }, } #[cw_serde] @@ -26,18 +26,16 @@ pub fn mock_verifier_execute( MockVotingVerifierExecuteMsg::VerifyMessages { messages } => { let mut res = vec![]; for m in messages { - let m = connection_router::state::Message::try_from(m).unwrap(); - match MOCK_VOTING_VERIFIER_MESSAGES.may_load(deps.storage, m.id.to_string())? { - Some(b) => res.push((m.id.to_string(), b)), - None => res.push((m.id.to_string(), false)), + match MOCK_VOTING_VERIFIER_MESSAGES.may_load(deps.storage, m.cc_id.clone())? { + Some(b) => res.push((m.cc_id, b)), + None => res.push((m.cc_id, false)), } } Ok(Response::new().set_data(to_binary(&res)?)) } MockVotingVerifierExecuteMsg::MessagesVerified { messages } => { for m in messages { - let m = connection_router::state::Message::try_from(m).unwrap(); - MOCK_VOTING_VERIFIER_MESSAGES.save(deps.storage, m.id.to_string(), &true)?; + MOCK_VOTING_VERIFIER_MESSAGES.save(deps.storage, m.cc_id, &true)?; } Ok(Response::new()) } @@ -47,7 +45,7 @@ pub fn mock_verifier_execute( pub fn mark_messages_as_verified( app: &mut App, voting_verifier_address: Addr, - msgs: Vec, + msgs: Vec, ) { app.execute_contract( Addr::unchecked("relayer"), diff --git a/contracts/aggregate-verifier/tests/test.rs b/contracts/aggregate-verifier/tests/test.rs index 090f34097..32210e130 100644 --- a/contracts/aggregate-verifier/tests/test.rs +++ b/contracts/aggregate-verifier/tests/test.rs @@ -1,77 +1,33 @@ use aggregate_verifier::contract::*; use aggregate_verifier::msg::{ExecuteMsg, InstantiateMsg}; -use axelar_wasm_std::ContractError; -use connection_router::msg::Message; -use connection_router::state::ID_SEPARATOR; +use connection_router::state::{CrossChainId, NewMessage, ID_SEPARATOR}; use cosmwasm_std::from_binary; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; +use voting_verifier::msg as voting_msg; use crate::mock::{make_mock_voting_verifier, mark_messages_as_verified}; pub mod mock; -fn generate_messages(count: usize) -> Vec { +fn generate_messages(count: usize) -> Vec { let mut msgs = vec![]; for x in 0..count { let src_chain = "mock-chain"; - let id = format!("{}{}{}", src_chain, ID_SEPARATOR, x); - msgs.push(connection_router::state::Message::new( - id.parse().unwrap(), - "idc".into(), - "mock-chain-2".parse().unwrap(), - src_chain.parse().unwrap(), - "idc".into(), - vec![x as u8, 0, 0, 0].into(), - )); + let id = format!("tx_hash{}{}", ID_SEPARATOR, x); + msgs.push(NewMessage { + cc_id: CrossChainId { + chain: src_chain.parse().unwrap(), + id: id.parse().unwrap(), + }, + destination_address: "idc".parse().unwrap(), + destination_chain: "mock-chain-2".parse().unwrap(), + source_address: "idc".parse().unwrap(), + payload_hash: vec![x as u8, 0, 0, 0].into(), + }); } msgs } -fn convert_messages( - msgs: &Vec, -) -> Vec { - msgs.into_iter().map(|m| m.clone().into()).collect() -} - -#[test] -fn bad_message_id() { - let mut app = App::default(); - let voting_verifier_address = make_mock_voting_verifier(&mut app); - - let code = ContractWrapper::new(execute, instantiate, query).with_reply(reply); - let code_id = app.store_code(Box::new(code)); - - let verifier_address = app - .instantiate_contract( - code_id, - Addr::unchecked("gateway"), - &InstantiateMsg { - verifier_address: voting_verifier_address.to_string(), - }, - &[], - "Contract", - None, - ) - .unwrap(); - let mut msgs = convert_messages(&generate_messages(10)); - msgs[0] = Message { - id: "".to_string(), - ..msgs[0].clone() - }; - let res = app - .execute_contract( - Addr::unchecked("relayer"), - verifier_address.clone(), - &ExecuteMsg::VerifyMessages { messages: msgs }, - &[], - ) - .unwrap_err(); - assert_eq!( - res.downcast::().unwrap().to_string(), - ContractError::from(connection_router::error::ContractError::InvalidMessageId).to_string() - ) -} - #[test] fn verify_messages_empty() { let mut app = App::default(); @@ -101,7 +57,7 @@ fn verify_messages_empty() { &[], ) .unwrap(); - let ret: Vec<(String, bool)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, bool)> = from_binary(&res.data.unwrap()).unwrap(); assert_eq!(ret, vec![]); } @@ -132,17 +88,17 @@ fn verify_messages_not_verified() { Addr::unchecked("relayer"), verifier_address.clone(), &ExecuteMsg::VerifyMessages { - messages: convert_messages(&msgs), + messages: msgs.clone(), }, &[], ) .unwrap(); - let ret: Vec<(String, bool)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, bool)> = from_binary(&res.data.unwrap()).unwrap(); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), false)) - .collect::>() + .map(|m| (m.cc_id.clone(), false)) + .collect::>() ); } @@ -168,24 +124,24 @@ fn verify_messages_verified() { .unwrap(); let msgs = generate_messages(10); - mark_messages_as_verified(&mut app, voting_verifier_address, convert_messages(&msgs)); + mark_messages_as_verified(&mut app, voting_verifier_address, msgs.clone()); let res = app .execute_contract( Addr::unchecked("relayer"), verifier_address.clone(), &ExecuteMsg::VerifyMessages { - messages: convert_messages(&msgs), + messages: msgs.clone(), }, &[], ) .unwrap(); - let ret: Vec<(String, bool)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, bool)> = from_binary(&res.data.unwrap()).unwrap(); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); } @@ -212,31 +168,27 @@ fn verify_messages_mixed_status() { let msgs = generate_messages(10); let (verified, _) = msgs.split_at(5); - mark_messages_as_verified( - &mut app, - voting_verifier_address, - convert_messages(&verified.to_vec()), - ); + mark_messages_as_verified(&mut app, voting_verifier_address, verified.to_vec()); let res = app .execute_contract( Addr::unchecked("relayer"), verifier_address.clone(), &ExecuteMsg::VerifyMessages { - messages: convert_messages(&msgs), + messages: msgs.clone(), }, &[], ) .unwrap(); - let ret: Vec<(String, bool)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, bool)> = from_binary(&res.data.unwrap()).unwrap(); assert_eq!( ret, msgs.iter() .map(|m| if verified.iter().find(|m2| *m2 == m).is_some() { - (m.id.to_string(), true) + (m.cc_id.clone(), true) } else { - (m.id.to_string(), false) + (m.cc_id.clone(), false) }) - .collect::>() + .collect::>() ); } diff --git a/contracts/connection-router/src/state.rs b/contracts/connection-router/src/state.rs index 3f06e0cff..00740be0d 100644 --- a/contracts/connection-router/src/state.rs +++ b/contracts/connection-router/src/state.rs @@ -112,6 +112,29 @@ impl TryFrom for Message { } } +/// temporary conversion until [Message] is removed +impl TryFrom for NewMessage { + type Error = Report; + + fn try_from(msg: msg::Message) -> Result { + let (_, id) = msg + .id + .split_once(ID_SEPARATOR) + .ok_or(ContractError::InvalidMessageId)?; + + Ok(NewMessage { + cc_id: CrossChainId { + id: id.parse()?, + chain: msg.source_chain.parse()?, + }, + destination_address: msg.destination_address.parse()?, + destination_chain: msg.destination_chain.parse()?, + source_address: msg.source_address.parse()?, + payload_hash: msg.payload_hash, + }) + } +} + /// temporary conversion until [Message] is removed impl TryFrom for NewMessage { type Error = Report; diff --git a/contracts/gateway/src/contract.rs b/contracts/gateway/src/contract.rs index b40e52057..7a47010d8 100644 --- a/contracts/gateway/src/contract.rs +++ b/contracts/gateway/src/contract.rs @@ -61,7 +61,7 @@ pub fn execute( pub mod execute { - use cosmwasm_std::{to_binary, QueryRequest, WasmMsg, WasmQuery}; + use cosmwasm_std::{to_binary, QueryRequest, StdError, WasmMsg, WasmQuery}; use crate::{events::GatewayEvent, state::OUTGOING_MESSAGES}; @@ -81,7 +81,11 @@ pub mod execute { let verifier = CONFIG.load(deps.storage)?.verifier; let query_msg = aggregate_verifier::msg::QueryMsg::IsVerified { - messages: msgs.iter().map(|m| m.clone().into()).collect(), + messages: msgs + .iter() + .map(|m| m.clone().try_into()) + .collect::>() + .map_err(|_| StdError::generic_err("invalid messages"))?, //todo: error mapping needs to get removed when gateway is mirated to NewMessage }; let query_response: Vec<(String, bool)> = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { @@ -115,8 +119,9 @@ pub mod execute { msg: to_binary(&aggregate_verifier::msg::ExecuteMsg::VerifyMessages { messages: unverified .into_iter() - .map(connection_router::msg::Message::from) - .collect(), + .map(|m| m.clone().try_into()) + .collect::>() + .map_err(|_| StdError::generic_err("invalid messages"))?, //todo: error mapping needs to get removed when gateway is mirated to NewMessage })?, funds: vec![], })) diff --git a/contracts/gateway/tests/mock.rs b/contracts/gateway/tests/mock.rs index df8b99349..b0fd98f2f 100644 --- a/contracts/gateway/tests/mock.rs +++ b/contracts/gateway/tests/mock.rs @@ -1,4 +1,5 @@ use connection_router::msg::Message; +use connection_router::state::NewMessage; use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw_multi_test::{App, ContractWrapper, Executor}; @@ -9,8 +10,8 @@ const MOCK_VERIFIER_MESSAGES: Map = Map::new("verifier_messages"); #[cw_serde] pub enum MockVerifierExecuteMsg { - VerifyMessages { messages: Vec }, - MessagesVerified { messages: Vec }, + VerifyMessages { messages: Vec }, + MessagesVerified { messages: Vec }, } pub fn mock_verifier_execute( @@ -49,7 +50,7 @@ pub fn mock_verifier_execute( #[cw_serde] pub enum MockVerifierQueryMsg { - IsVerified { messages: Vec }, + IsVerified { messages: Vec }, } pub fn mock_verifier_query(deps: Deps, _env: Env, msg: MockVerifierQueryMsg) -> StdResult { let mut res = vec![]; @@ -73,7 +74,7 @@ pub fn mock_verifier_query(deps: Deps, _env: Env, msg: MockVerifierQueryMsg) -> pub fn is_verified( app: &mut App, verifier_address: Addr, - msgs: Vec, + msgs: Vec, ) -> Vec<(String, bool)> { app.wrap() .query_wasm_smart( @@ -83,11 +84,7 @@ pub fn is_verified( .unwrap() } -pub fn mark_messages_as_verified( - app: &mut App, - verifier_address: Addr, - msgs: Vec, -) { +pub fn mark_messages_as_verified(app: &mut App, verifier_address: Addr, msgs: Vec) { app.execute_contract( Addr::unchecked("relayer"), verifier_address.clone(), diff --git a/contracts/gateway/tests/tests.rs b/contracts/gateway/tests/tests.rs index b9d8448bf..ecb4b8c02 100644 --- a/contracts/gateway/tests/tests.rs +++ b/contracts/gateway/tests/tests.rs @@ -35,7 +35,7 @@ fn verify_one_message() { let src_chain = "mock-chain-2"; let msg = Message { - id: format!("{}{}foobar", src_chain, ID_SEPARATOR,).into(), + id: format!("{}{}txhash{}5", src_chain, ID_SEPARATOR, ID_SEPARATOR).into(), destination_address: "idc".into(), destination_chain: "mock-chain".into(), source_chain: src_chain.into(), @@ -50,8 +50,11 @@ fn verify_one_message() { &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = - is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + let ret: Vec<(String, bool)> = is_verified( + &mut app, + verifier_address.clone(), + vec![msg.clone().try_into().unwrap()], + ); assert_eq!(ret, vec![(msg.id.clone(), false)]); let res = app.execute_contract( @@ -61,11 +64,18 @@ fn verify_one_message() { &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = - is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + let ret: Vec<(String, bool)> = is_verified( + &mut app, + verifier_address.clone(), + vec![msg.clone().try_into().unwrap()], + ); assert_eq!(ret, vec![(msg.id.clone(), false)]); - mark_messages_as_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + mark_messages_as_verified( + &mut app, + verifier_address.clone(), + vec![msg.clone().try_into().unwrap()], + ); let res = app.execute_contract( Addr::unchecked("relayer"), @@ -74,8 +84,11 @@ fn verify_one_message() { &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = - is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + let ret: Vec<(String, bool)> = is_verified( + &mut app, + verifier_address.clone(), + vec![msg.clone().try_into().unwrap()], + ); assert_eq!(ret, vec![(msg.id.clone(), true)]); // should still return true if queried again @@ -86,8 +99,11 @@ fn verify_one_message() { &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = - is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + let ret: Vec<(String, bool)> = is_verified( + &mut app, + verifier_address.clone(), + vec![msg.clone().try_into().unwrap()], + ); assert_eq!(ret, vec![(msg.id, true)]); } @@ -114,7 +130,7 @@ fn generate_messages(count: usize) -> Vec { fn convert_messages( msgs: &Vec, ) -> Vec { - msgs.into_iter().map(|m| m.clone().into()).collect() + msgs.iter().map(|m| m.clone().into()).collect() } #[test] @@ -143,7 +159,11 @@ fn verify_multiple_messages() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::VerifyMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); @@ -151,7 +171,9 @@ fn verify_multiple_messages() { let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!( @@ -164,7 +186,9 @@ fn verify_multiple_messages() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( @@ -177,7 +201,9 @@ fn verify_multiple_messages() { let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!( @@ -190,19 +216,25 @@ fn verify_multiple_messages() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::VerifyMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!( ret, - msgs.into_iter() + msgs.iter() .map(|m| (m.id.to_string(), true)) .collect::>() ); @@ -237,7 +269,10 @@ fn verify_multiple_messages_mixed_status() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs_verified.to_vec()), + msgs_verified + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( @@ -250,7 +285,9 @@ fn verify_multiple_messages_mixed_status() { let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!(ret.len(), msgs.len()); @@ -278,7 +315,9 @@ fn verify_multiple_messages_mixed_status() { let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!(ret.len(), msgs.len()); @@ -299,24 +338,33 @@ fn verify_multiple_messages_mixed_status() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs_unverified.to_vec()), + msgs_unverified + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::VerifyMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!( ret, - msgs.into_iter() + msgs.iter() .map(|m| (m.id.to_string(), true)) .collect::>() ); @@ -349,14 +397,20 @@ fn execute_one_message() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::VerifyMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -368,7 +422,9 @@ fn execute_one_message() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( @@ -381,7 +437,9 @@ fn execute_one_message() { let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -393,14 +451,20 @@ fn execute_one_message() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -440,14 +504,20 @@ fn execute_multiple_messages() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::VerifyMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!( @@ -460,7 +530,9 @@ fn execute_multiple_messages() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( @@ -473,7 +545,9 @@ fn execute_multiple_messages() { let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!( @@ -486,14 +560,20 @@ fn execute_multiple_messages() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!( @@ -537,7 +617,10 @@ fn execute_multiple_messages_mixed_status() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs_verified.to_vec()), + msgs_verified + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( @@ -550,7 +633,9 @@ fn execute_multiple_messages_mixed_status() { let mut ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); ret.sort_by(|a, b| a.0.cmp(&b.0)); assert_eq!(ret.len(), msgs.len()); @@ -607,14 +692,20 @@ fn execute_not_verified_message() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -626,20 +717,28 @@ fn execute_not_verified_message() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -680,20 +779,28 @@ fn execute_pre_verified_message() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -734,20 +841,28 @@ fn execute_twice() { mark_messages_as_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -762,14 +877,20 @@ fn execute_twice() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs.clone())), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); let ret: Vec<(String, bool)> = is_verified( &mut app, verifier_address.clone(), - convert_messages(&msgs.clone()), + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), ); assert_eq!( ret, @@ -899,7 +1020,7 @@ fn duplicate_message_id() { .unwrap(); // make two different messages with the same ID - let mut msgs = convert_messages(&generate_messages(2)); + let mut msgs = generate_messages(2); msgs[1].id = msgs[0].id.clone(); assert_ne!(msgs[0], msgs[1]); @@ -907,7 +1028,11 @@ fn duplicate_message_id() { .execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(msgs.clone()), + &ExecuteMsg::VerifyMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ) .unwrap_err(); @@ -922,7 +1047,11 @@ fn duplicate_message_id() { .execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(msgs.clone()), + &ExecuteMsg::RouteMessages( + msgs.iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ) .unwrap_err(); @@ -934,17 +1063,35 @@ fn duplicate_message_id() { ); //verify one of them - mark_messages_as_verified(&mut app, verifier_address.clone(), msgs[0..1].to_vec()); + mark_messages_as_verified( + &mut app, + verifier_address.clone(), + msgs[0..1] + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(msgs[0..1].to_vec()), + &ExecuteMsg::VerifyMessages( + msgs[0..1] + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = - is_verified(&mut app, verifier_address.clone(), msgs[0..1].to_vec()); + let ret: Vec<(String, bool)> = is_verified( + &mut app, + verifier_address.clone(), + msgs[0..1] + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ); assert_eq!( ret, msgs[0..1] @@ -957,13 +1104,24 @@ fn duplicate_message_id() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(msgs[1..2].to_vec()), + &ExecuteMsg::VerifyMessages( + msgs[1..2] + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = - is_verified(&mut app, verifier_address.clone(), msgs[1..2].to_vec()); + let ret: Vec<(String, bool)> = is_verified( + &mut app, + verifier_address.clone(), + msgs[1..2] + .into_iter() + .map(|msg| msg.clone().try_into().unwrap()) + .collect(), + ); assert_eq!( ret, msgs[1..2] diff --git a/contracts/voting-verifier/src/msg.rs b/contracts/voting-verifier/src/msg.rs index 0e7ab01ac..d65acede4 100644 --- a/contracts/voting-verifier/src/msg.rs +++ b/contracts/voting-verifier/src/msg.rs @@ -60,7 +60,7 @@ pub enum QueryMsg { #[returns(Poll)] GetPoll { poll_id: PollID }, - #[returns(Vec<(String, bool)>)] + #[returns(Vec<(CrossChainId, bool)>)] IsVerified { messages: Vec }, #[returns(bool)] diff --git a/packages/axelar-wasm-std/src/nonempty/vec.rs b/packages/axelar-wasm-std/src/nonempty/vec.rs index cce69e705..6d84d36e7 100644 --- a/packages/axelar-wasm-std/src/nonempty/vec.rs +++ b/packages/axelar-wasm-std/src/nonempty/vec.rs @@ -2,6 +2,7 @@ use crate::nonempty::Error; use cosmwasm_schema::cw_serde; #[cw_serde] +#[serde(try_from = "std::vec::Vec")] pub struct Vec(std::vec::Vec); impl TryFrom> for Vec { From 451f95fdd9dab9ddf61dc780de622235f7b39d45 Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 21 Sep 2023 16:52:45 -0400 Subject: [PATCH 4/8] refactor: migrate gateway to use NewMessage (#118) --- contracts/aggregate-verifier/tests/test.rs | 1 - contracts/connection-router/src/state.rs | 19 + contracts/gateway/src/contract.rs | 76 +-- contracts/gateway/src/events.rs | 22 +- contracts/gateway/src/msg.rs | 10 +- contracts/gateway/src/state.rs | 4 +- contracts/gateway/tests/mock.rs | 30 +- contracts/gateway/tests/tests.rs | 715 ++++++--------------- contracts/multisig-prover/src/execute.rs | 10 +- 9 files changed, 284 insertions(+), 603 deletions(-) diff --git a/contracts/aggregate-verifier/tests/test.rs b/contracts/aggregate-verifier/tests/test.rs index 32210e130..77ada8469 100644 --- a/contracts/aggregate-verifier/tests/test.rs +++ b/contracts/aggregate-verifier/tests/test.rs @@ -4,7 +4,6 @@ use connection_router::state::{CrossChainId, NewMessage, ID_SEPARATOR}; use cosmwasm_std::from_binary; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; -use voting_verifier::msg as voting_msg; use crate::mock::{make_mock_voting_verifier, mark_messages_as_verified}; pub mod mock; diff --git a/contracts/connection-router/src/state.rs b/contracts/connection-router/src/state.rs index 00740be0d..24a3cf84d 100644 --- a/contracts/connection-router/src/state.rs +++ b/contracts/connection-router/src/state.rs @@ -270,6 +270,7 @@ impl TryFrom for Address { #[cw_serde] #[serde(try_from = "String")] +#[derive(Eq, Hash)] pub struct MessageId(String); impl FromStr for MessageId { @@ -333,11 +334,28 @@ impl KeyDeserialize for MessageId { } #[cw_serde] +#[derive(Eq, Hash)] pub struct CrossChainId { pub chain: ChainName, pub id: MessageId, } +/// todo: remove this when state::NewMessage is used +impl FromStr for CrossChainId { + type Err = ContractError; + + fn from_str(s: &str) -> Result { + let parts = s.split_once(ID_SEPARATOR); + let (chain, id) = parts + .map(|(chain, id)| (chain.parse::(), id.parse::())) + .ok_or(ContractError::InvalidMessageId)?; + Ok(CrossChainId { + chain: chain?, + id: id?, + }) + } +} + impl Display for CrossChainId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}{}{}", &self.chain, ID_SEPARATOR, &self.id) @@ -368,6 +386,7 @@ impl KeyDeserialize for CrossChainId { #[cw_serde] #[serde(try_from = "String")] +#[derive(Eq, Hash)] pub struct ChainName(String); impl FromStr for ChainName { diff --git a/contracts/gateway/src/contract.rs b/contracts/gateway/src/contract.rs index 7a47010d8..eb7c48eef 100644 --- a/contracts/gateway/src/contract.rs +++ b/contracts/gateway/src/contract.rs @@ -8,7 +8,7 @@ use crate::{ state::{Config, CONFIG, OUTGOING_MESSAGES}, }; -use connection_router::state::Message; +use connection_router::state::NewMessage; use self::execute::{route_incoming_messages, route_outgoing_messages, verify_messages}; @@ -35,19 +35,8 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::VerifyMessages(messages) => { - let msgs = messages - .into_iter() - .map(Message::try_from) - .collect::, _>>()?; - verify_messages(deps, msgs) - } - ExecuteMsg::RouteMessages(messages) => { - let msgs = messages - .into_iter() - .map(Message::try_from) - .collect::, _>>()?; - + ExecuteMsg::VerifyMessages(msgs) => verify_messages(deps, msgs), + ExecuteMsg::RouteMessages(msgs) => { let router = CONFIG.load(deps.storage)?.router; if info.sender == router { route_outgoing_messages(deps, msgs) @@ -61,40 +50,37 @@ pub fn execute( pub mod execute { - use cosmwasm_std::{to_binary, QueryRequest, StdError, WasmMsg, WasmQuery}; + use connection_router::state::CrossChainId; + use cosmwasm_std::{to_binary, QueryRequest, WasmMsg, WasmQuery}; use crate::{events::GatewayEvent, state::OUTGOING_MESSAGES}; use super::*; - fn contains_duplicates(msgs: &mut Vec) -> bool { + fn contains_duplicates(msgs: &mut Vec) -> bool { let orig_len = msgs.len(); - msgs.sort_unstable_by_key(|a| a.id.to_string()); - msgs.dedup_by(|a, b| a.id == b.id); + msgs.sort_unstable_by_key(|a| a.cc_id.to_string()); + msgs.dedup_by(|a, b| a.cc_id == b.cc_id); orig_len != msgs.len() } fn partition_by_verified( deps: DepsMut, - msgs: Vec, - ) -> Result<(Vec, Vec), ContractError> { + msgs: Vec, + ) -> Result<(Vec, Vec), ContractError> { let verifier = CONFIG.load(deps.storage)?.verifier; let query_msg = aggregate_verifier::msg::QueryMsg::IsVerified { - messages: msgs - .iter() - .map(|m| m.clone().try_into()) - .collect::>() - .map_err(|_| StdError::generic_err("invalid messages"))?, //todo: error mapping needs to get removed when gateway is mirated to NewMessage + messages: msgs.clone(), }; - let query_response: Vec<(String, bool)> = + let query_response: Vec<(CrossChainId, bool)> = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: verifier.to_string(), msg: to_binary(&query_msg)?, }))?; Ok(msgs.into_iter().partition(|m| -> bool { - match query_response.iter().find(|r| m.id.to_string() == r.0) { + match query_response.iter().find(|r| m.cc_id == r.0) { Some((_, v)) => *v, None => false, } @@ -103,7 +89,7 @@ pub mod execute { pub fn verify_messages( deps: DepsMut, - mut msgs: Vec, + mut msgs: Vec, ) -> Result { let config = CONFIG.load(deps.storage)?; let verifier = config.verifier; @@ -117,11 +103,7 @@ pub mod execute { Ok(Response::new().add_message(WasmMsg::Execute { contract_addr: verifier.to_string(), msg: to_binary(&aggregate_verifier::msg::ExecuteMsg::VerifyMessages { - messages: unverified - .into_iter() - .map(|m| m.clone().try_into()) - .collect::>() - .map_err(|_| StdError::generic_err("invalid messages"))?, //todo: error mapping needs to get removed when gateway is mirated to NewMessage + messages: unverified, })?, funds: vec![], })) @@ -129,7 +111,7 @@ pub mod execute { pub fn route_incoming_messages( deps: DepsMut, - mut msgs: Vec, + mut msgs: Vec, ) -> Result { let router = CONFIG.load(deps.storage)?.router; @@ -137,18 +119,13 @@ pub mod execute { return Err(ContractError::DuplicateMessageID); } - let (verified, unverified) = partition_by_verified(deps, msgs)?; + let (verified, unverified) = partition_by_verified(deps, msgs.clone())?; Ok(Response::new() .add_message(WasmMsg::Execute { contract_addr: router.to_string(), msg: to_binary(&connection_router::msg::ExecuteMsg::RouteMessages( - verified - .clone() - .into_iter() - .map(connection_router::state::NewMessage::try_from) - .collect::, _>>() - .map_err(|_| connection_router::error::ContractError::InvalidMessageId)?, //todo: remove when integrating error stack into the gateway + verified.clone(), ))?, funds: vec![], }) @@ -166,10 +143,10 @@ pub mod execute { pub fn route_outgoing_messages( deps: DepsMut, - msgs: Vec, + msgs: Vec, ) -> Result { for m in &msgs { - OUTGOING_MESSAGES.save(deps.storage, m.id.to_string(), m)?; + OUTGOING_MESSAGES.save(deps.storage, m.cc_id.clone(), m)?; } Ok(Response::new().add_events( @@ -182,19 +159,16 @@ pub mod execute { #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::GetMessages { message_ids } => { + QueryMsg::GetMessages { + message_ids: cross_chain_ids, + } => { let mut msgs = vec![]; - for id in message_ids { + for id in cross_chain_ids { msgs.push(OUTGOING_MESSAGES.load(deps.storage, id)?); } - to_binary( - &msgs - .into_iter() - .map(|m| m.into()) - .collect::>(), - ) + to_binary(&msgs) } } } diff --git a/contracts/gateway/src/events.rs b/contracts/gateway/src/events.rs index 10397930d..e787f69d9 100644 --- a/contracts/gateway/src/events.rs +++ b/contracts/gateway/src/events.rs @@ -1,24 +1,26 @@ -use connection_router::events::make_message_event; -use connection_router::state::Message; +use connection_router::events::make_message_event_new; +use connection_router::state::NewMessage; use cosmwasm_std::Event; pub enum GatewayEvent { - MessageVerified { msg: Message }, - MessageVerificationFailed { msg: Message }, - MessageRouted { msg: Message }, - MessageRoutingFailed { msg: Message }, + MessageVerified { msg: NewMessage }, + MessageVerificationFailed { msg: NewMessage }, + MessageRouted { msg: NewMessage }, + MessageRoutingFailed { msg: NewMessage }, } impl From for Event { fn from(other: GatewayEvent) -> Self { match other { - GatewayEvent::MessageVerified { msg } => make_message_event("message_verified", msg), - GatewayEvent::MessageRouted { msg } => make_message_event("message_routed", msg), + GatewayEvent::MessageVerified { msg } => { + make_message_event_new("message_verified", msg) + } + GatewayEvent::MessageRouted { msg } => make_message_event_new("message_routed", msg), GatewayEvent::MessageVerificationFailed { msg } => { - make_message_event("message_verification_failed", msg) + make_message_event_new("message_verification_failed", msg) } GatewayEvent::MessageRoutingFailed { msg } => { - make_message_event("message_routing_failed", msg) + make_message_event_new("message_routing_failed", msg) } } } diff --git a/contracts/gateway/src/msg.rs b/contracts/gateway/src/msg.rs index 4d95e5aa9..be00057cd 100644 --- a/contracts/gateway/src/msg.rs +++ b/contracts/gateway/src/msg.rs @@ -1,4 +1,4 @@ -use connection_router::msg::Message; +use connection_router::state::{CrossChainId, NewMessage}; use cosmwasm_schema::{cw_serde, QueryResponses}; #[cw_serde] @@ -10,15 +10,15 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { // Permissionless - VerifyMessages(Vec), + VerifyMessages(Vec), // Permissionless - RouteMessages(Vec), + RouteMessages(Vec), } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(Vec)] - GetMessages { message_ids: Vec }, + #[returns(Vec)] + GetMessages { message_ids: Vec }, } diff --git a/contracts/gateway/src/state.rs b/contracts/gateway/src/state.rs index 6d123f15d..a73a03923 100644 --- a/contracts/gateway/src/state.rs +++ b/contracts/gateway/src/state.rs @@ -1,4 +1,4 @@ -use connection_router::state::Message; +use connection_router::state::{CrossChainId, NewMessage}; use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; use cw_storage_plus::{Item, Map}; @@ -11,4 +11,4 @@ pub struct Config { pub const CONFIG: Item = Item::new("config"); -pub const OUTGOING_MESSAGES: Map = Map::new("outgoing_messages"); +pub const OUTGOING_MESSAGES: Map = Map::new("outgoing_messages"); diff --git a/contracts/gateway/tests/mock.rs b/contracts/gateway/tests/mock.rs index b0fd98f2f..b8af82d8e 100644 --- a/contracts/gateway/tests/mock.rs +++ b/contracts/gateway/tests/mock.rs @@ -1,5 +1,4 @@ -use connection_router::msg::Message; -use connection_router::state::NewMessage; +use connection_router::state::{CrossChainId, NewMessage}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw_multi_test::{App, ContractWrapper, Executor}; @@ -24,19 +23,17 @@ pub fn mock_verifier_execute( MockVerifierExecuteMsg::VerifyMessages { messages } => { let mut res = vec![]; for m in messages { - let m = connection_router::state::Message::try_from(m).unwrap(); match MOCK_VERIFIER_MESSAGES .may_load(deps.storage, serde_json::to_string(&m).unwrap())? { - Some(b) => res.push((m.id, b)), - None => res.push((m.id, false)), + Some(b) => res.push((m.cc_id, b)), + None => res.push((m.cc_id, false)), } } Ok(Response::new().set_data(to_binary(&res)?)) } MockVerifierExecuteMsg::MessagesVerified { messages } => { for m in messages { - let m = connection_router::state::Message::try_from(m).unwrap(); MOCK_VERIFIER_MESSAGES.save( deps.storage, serde_json::to_string(&m).unwrap(), @@ -58,12 +55,11 @@ pub fn mock_verifier_query(deps: Deps, _env: Env, msg: MockVerifierQueryMsg) -> match msg { MockVerifierQueryMsg::IsVerified { messages } => { for m in messages { - let m = connection_router::state::Message::try_from(m).unwrap(); match MOCK_VERIFIER_MESSAGES .may_load(deps.storage, serde_json::to_string(&m).unwrap())? { - Some(v) => res.push((m.id.to_string(), v)), - None => res.push((m.id.to_string(), false)), + Some(v) => res.push((m.cc_id, v)), + None => res.push((m.cc_id, false)), } } } @@ -75,7 +71,7 @@ pub fn is_verified( app: &mut App, verifier_address: Addr, msgs: Vec, -) -> Vec<(String, bool)> { +) -> Vec<(CrossChainId, bool)> { app.wrap() .query_wasm_smart( verifier_address, @@ -94,8 +90,7 @@ pub fn mark_messages_as_verified(app: &mut App, verifier_address: Addr, msgs: Ve .unwrap(); } -const MOCK_ROUTER_MESSAGES: Map = - Map::new("router_messages"); +const MOCK_ROUTER_MESSAGES: Map = Map::new("router_messages"); pub fn mock_router_execute( deps: DepsMut, @@ -106,8 +101,7 @@ pub fn mock_router_execute( match msg { connection_router::msg::ExecuteMsg::RouteMessages(msgs) => { for msg in msgs { - let msg = connection_router::state::Message::try_from(msg)?; - MOCK_ROUTER_MESSAGES.save(deps.storage, msg.id.to_string(), &msg)?; + MOCK_ROUTER_MESSAGES.save(deps.storage, msg.cc_id.clone(), &msg)?; } } _ => (), @@ -117,7 +111,7 @@ pub fn mock_router_execute( #[cw_serde] pub enum MockRouterQueryMsg { - GetMessages { ids: Vec }, + GetMessages { ids: Vec }, } pub fn mock_router_query(deps: Deps, _env: Env, msg: MockRouterQueryMsg) -> StdResult { let mut msgs = vec![]; @@ -138,13 +132,13 @@ pub fn mock_router_query(deps: Deps, _env: Env, msg: MockRouterQueryMsg) -> StdR pub fn get_router_messages( app: &mut App, router_address: Addr, - msgs: Vec, -) -> Vec { + msgs: Vec, +) -> Vec { app.wrap() .query_wasm_smart( router_address, &MockRouterQueryMsg::GetMessages { - ids: msgs.iter().map(|m| m.id.to_string()).collect(), + ids: msgs.into_iter().map(|m| m.cc_id).collect(), }, ) .unwrap() diff --git a/contracts/gateway/tests/tests.rs b/contracts/gateway/tests/tests.rs index ecb4b8c02..61562d2a9 100644 --- a/contracts/gateway/tests/tests.rs +++ b/contracts/gateway/tests/tests.rs @@ -1,7 +1,9 @@ -use connection_router::msg::Message; -use connection_router::state::ID_SEPARATOR; +use std::collections::HashSet; + use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; + +use connection_router::state::{CrossChainId, NewMessage, ID_SEPARATOR}; use gateway::contract::*; use gateway::error::ContractError; use gateway::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; @@ -10,6 +12,7 @@ use crate::mock::is_verified; use crate::mock::{ get_router_messages, make_mock_router, make_mock_verifier, mark_messages_as_verified, }; + pub mod mock; #[test] fn verify_one_message() { @@ -33,15 +36,7 @@ fn verify_one_message() { ) .unwrap(); - let src_chain = "mock-chain-2"; - let msg = Message { - id: format!("{}{}txhash{}5", src_chain, ID_SEPARATOR, ID_SEPARATOR).into(), - destination_address: "idc".into(), - destination_chain: "mock-chain".into(), - source_chain: src_chain.into(), - source_address: "idc".into(), - payload_hash: vec![0, 0, 0, 0].into(), - }; + let msg = generate_messages(1).swap_remove(0); let res = app.execute_contract( Addr::unchecked("relayer"), @@ -50,89 +45,64 @@ fn verify_one_message() { &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - vec![msg.clone().try_into().unwrap()], - ); - assert_eq!(ret, vec![(msg.id.clone(), false)]); + + let ret = is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + assert_eq!(ret, vec![(msg.cc_id.clone(), false)]); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(vec![msg.clone().into()]), + &ExecuteMsg::VerifyMessages(vec![msg.clone()]), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - vec![msg.clone().try_into().unwrap()], - ); - assert_eq!(ret, vec![(msg.id.clone(), false)]); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - vec![msg.clone().try_into().unwrap()], - ); + let ret = is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + assert_eq!(ret, vec![(msg.cc_id.clone(), false)]); + + mark_messages_as_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(vec![msg.clone().into()]), + &ExecuteMsg::VerifyMessages(vec![msg.clone()]), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - vec![msg.clone().try_into().unwrap()], - ); - assert_eq!(ret, vec![(msg.id.clone(), true)]); + + let ret = is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + assert_eq!(ret, vec![(msg.cc_id.clone(), true)]); // should still return true if queried again let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(vec![msg.clone().into()]), + &ExecuteMsg::VerifyMessages(vec![msg.clone()]), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - vec![msg.clone().try_into().unwrap()], - ); - assert_eq!(ret, vec![(msg.id, true)]); + + let ret = is_verified(&mut app, verifier_address.clone(), vec![msg.clone()]); + assert_eq!(ret, vec![(msg.cc_id, true)]); } -fn generate_messages(count: usize) -> Vec { +fn generate_messages(count: usize) -> Vec { let mut msgs = vec![]; for x in 0..count { - let src_chain = "mock-chain"; - let id = format!( - "{}{}{}{}{}", - src_chain, ID_SEPARATOR, "hash", ID_SEPARATOR, x - ); - msgs.push(connection_router::state::Message::new( - id.parse().unwrap(), - "idc".into(), - "mock-chain-2".parse().unwrap(), - src_chain.parse().unwrap(), - "idc".into(), - vec![x as u8, 0, 0, 0].into(), - )); + msgs.push(NewMessage { + cc_id: CrossChainId { + chain: "mock-chain".parse().unwrap(), + id: format!("{}{}{}", "hash", ID_SEPARATOR, x).parse().unwrap(), + }, + destination_address: "idc".parse().unwrap(), + destination_chain: "mock-chain-2".parse().unwrap(), + source_address: "idc".parse().unwrap(), + payload_hash: vec![x as u8, 0, 0, 0].into(), + }) } msgs } -fn convert_messages( - msgs: &Vec, -) -> Vec { - msgs.iter().map(|m| m.clone().into()).collect() -} - #[test] fn verify_multiple_messages() { let mut app = App::default(); @@ -159,85 +129,43 @@ fn verify_multiple_messages() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::VerifyMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( + + let ret = is_verified( &mut app, verifier_address.clone(), msgs.iter() .map(|msg| msg.clone().try_into().unwrap()) .collect(), ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!( - ret, - msgs.iter() - .map(|m| (m.id.to_string(), false)) - .collect::>() - ); + + let msg_id_set: HashSet<_> = msgs.iter().map(|msg| msg.cc_id.clone()).collect(); + let (_, unverified) = partition_by_verified(&ret); + assert_eq!(msg_id_set, unverified); mark_messages_as_verified( &mut app, verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), + msgs.iter().map(|msg| msg.clone()).collect(), ); - let res = app.execute_contract( - Addr::unchecked("relayer"), - gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs)), - &[], - ); - assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!( - ret, - msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() - ); + for _ in 0..2 { + let res = app.execute_contract( + Addr::unchecked("relayer"), + gateway_address.clone(), + &ExecuteMsg::VerifyMessages(msgs.clone()), + &[], + ); + assert!(res.is_ok()); - let res = app.execute_contract( - Addr::unchecked("relayer"), - gateway_address.clone(), - &ExecuteMsg::VerifyMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), - &[], - ); - assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!( - ret, - msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() - ); + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); + let (verified, _) = partition_by_verified(&ret); + assert_eq!(msg_id_set, verified); + } } #[test] @@ -265,111 +193,66 @@ fn verify_multiple_messages_mixed_status() { let msgs = generate_messages(10); let msgs_verified = &msgs[0..5]; let msgs_unverified = &msgs[5..10]; + let expected_verified: HashSet<_> = msgs_verified.iter().map(|msg| msg.cc_id.clone()).collect(); + let expected_unverified: HashSet<_> = msgs_unverified + .iter() + .map(|msg| msg.cc_id.clone()) + .collect(); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs_verified - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - - let res = app.execute_contract( - Addr::unchecked("relayer"), - gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs)), - &[], - ); - assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!(ret.len(), msgs.len()); - for (id, status) in ret { - let expected_status = if msgs_verified - .iter() - .find(|m| m.id.to_string() == id) - .is_some() - { - true - } else { - false - }; - assert_eq!(expected_status, status); - } + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs_verified.to_vec()); // same call should return same response - let res = app.execute_contract( - Addr::unchecked("relayer"), - gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs)), - &[], - ); - assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!(ret.len(), msgs.len()); - for (id, status) in ret { - let expected_status = if msgs_verified - .iter() - .find(|m| m.id.to_string() == id) - .is_some() - { - true - } else { - false - }; - assert_eq!(expected_status, status); + for _ in 0..2 { + let res = app.execute_contract( + Addr::unchecked("relayer"), + gateway_address.clone(), + &ExecuteMsg::VerifyMessages(msgs.clone()), + &[], + ); + assert!(res.is_ok()); + + let mut ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); + let (verified, unverified) = partition_by_verified(&mut ret); + assert_eq!(expected_verified, verified); + assert_eq!(expected_unverified, unverified); } // mark the rest as verified - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs_unverified - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs_unverified.to_vec()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::VerifyMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); + + let mut ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); + let (verified, unverified) = partition_by_verified(&mut ret); + assert!(unverified.is_empty()); assert_eq!( - ret, - msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + msgs.into_iter() + .map(|msg| msg.cc_id) + .collect::>(), + verified ); } +fn partition_by_verified( + ret: &Vec<(CrossChainId, bool)>, +) -> (HashSet, HashSet) { + let verified: HashSet<_> = ret + .into_iter() + .filter_map(|(id, verified)| if *verified { Some(id.clone()) } else { None }) + .collect(); + let unverified: HashSet<_> = ret + .into_iter() + .filter_map(|(id, verified)| if !*verified { Some(id.clone()) } else { None }) + .collect(); + + (verified, unverified) +} + #[test] fn execute_one_message() { let mut app = App::default(); @@ -405,75 +288,50 @@ fn execute_one_message() { &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), false)) - .collect::>() + .map(|m| (m.cc_id.clone(), false)) + .collect::>() ); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs.clone()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs)), + &ExecuteMsg::VerifyMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); - let ret = get_router_messages(&mut app, router_address, convert_messages(&msgs)); + let ret = get_router_messages(&mut app, router_address, msgs.clone()); assert_eq!(ret, msgs); } @@ -501,89 +359,49 @@ fn execute_multiple_messages() { .unwrap(); let msgs = generate_messages(10); + let expected_messages: HashSet<_> = msgs.iter().map(|msg| msg.cc_id.clone()).collect(); + let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::VerifyMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!( - ret, - msgs.iter() - .map(|m| (m.id.to_string(), false)) - .collect::>() - ); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + let mut ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); + let (verified, unverified) = partition_by_verified(&mut ret); + assert!(verified.is_empty()); + assert_eq!(expected_messages, unverified); + + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs.clone()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages(convert_messages(&msgs)), + &ExecuteMsg::VerifyMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!( - ret, - msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() - ); + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); + let (verified, unverified) = partition_by_verified(&ret); + assert!(unverified.is_empty()); + assert_eq!(expected_messages, verified); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!( - ret, - msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() - ); - let ret = get_router_messages(&mut app, router_address, convert_messages(&msgs)); + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); + let (verified, unverified) = partition_by_verified(&ret); + assert!(unverified.is_empty()); + assert_eq!(expected_messages, verified); + + let ret = get_router_messages(&mut app, router_address, msgs.clone()); assert_eq!(ret, msgs); } @@ -613,55 +431,30 @@ fn execute_multiple_messages_mixed_status() { let msgs = generate_messages(10); let msgs_verified = &msgs[0..5]; let msgs_unverified = &msgs[5..10]; + let expected_verified: HashSet<_> = msgs_verified.iter().map(|msg| msg.cc_id.clone()).collect(); + let expected_unverified: HashSet<_> = msgs_unverified + .iter() + .map(|msg| msg.cc_id.clone()) + .collect(); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs_verified - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs_verified.to_vec()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs)), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let mut ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); - ret.sort_by(|a, b| a.0.cmp(&b.0)); - assert_eq!(ret.len(), msgs.len()); - for (id, status) in ret { - let expected_status = if msgs_verified - .iter() - .find(|m| m.id.to_string() == id) - .is_some() - { - true - } else { - false - }; - assert_eq!(expected_status, status); - } - let ret = get_router_messages( - &mut app, - router_address.clone(), - convert_messages(&msgs_verified.to_vec()), - ); + + let mut ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); + let (verified, unverified) = partition_by_verified(&mut ret); + assert_eq!(expected_verified, verified); + assert_eq!(expected_unverified, unverified); + + let ret = get_router_messages(&mut app, router_address.clone(), msgs_verified.to_vec()); assert_eq!(ret, msgs_verified); - let ret = get_router_messages( - &mut app, - router_address, - convert_messages(&msgs_unverified.to_vec()), - ); + let ret = get_router_messages(&mut app, router_address, msgs_unverified.to_vec()); assert_eq!(ret, vec![]); } @@ -692,62 +485,38 @@ fn execute_not_verified_message() { let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), false)) - .collect::>() + .map(|m| (m.cc_id.clone(), false)) + .collect::>() ); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs.clone()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); - let ret = get_router_messages(&mut app, router_address, convert_messages(&msgs)); + let ret = get_router_messages(&mut app, router_address, msgs.clone()); assert_eq!(ret, msgs); } @@ -776,40 +545,25 @@ fn execute_pre_verified_message() { let msgs = generate_messages(1); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs.clone()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); - let ret = get_router_messages(&mut app, router_address, convert_messages(&msgs)); + let ret = get_router_messages(&mut app, router_address, msgs.clone()); assert_eq!(ret, msgs); } @@ -838,65 +592,41 @@ fn execute_twice() { let msgs = generate_messages(1); - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs.clone()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); - let ret = get_router_messages(&mut app, router_address, convert_messages(&msgs)); + let ret = get_router_messages(&mut app, router_address, msgs.clone()); assert_eq!(ret, msgs); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + + let ret = is_verified(&mut app, verifier_address.clone(), msgs.clone()); assert_eq!( ret, msgs.iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); } @@ -928,24 +658,21 @@ fn receive_one_message() { let res = app.execute_contract( router_address, gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs)), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec = app + let ret: Vec = app .wrap() .query_wasm_smart( gateway_address, &QueryMsg::GetMessages { - message_ids: msgs.iter().map(|m| m.id.to_string()).collect(), + message_ids: msgs.iter().map(|m| m.cc_id.clone()).collect(), }, ) .unwrap(); - assert_eq!( - ret, - msgs.into_iter().map(|m| m.into()).collect::>() - ); + assert_eq!(ret, msgs); } #[test] @@ -976,24 +703,21 @@ fn receive_many_messages() { let res = app.execute_contract( router_address, gateway_address.clone(), - &ExecuteMsg::RouteMessages(convert_messages(&msgs)), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ); assert!(res.is_ok()); - let ret: Vec = app + let ret: Vec = app .wrap() .query_wasm_smart( gateway_address, &QueryMsg::GetMessages { - message_ids: msgs.iter().map(|m| m.id.to_string()).collect(), + message_ids: msgs.iter().map(|m| m.cc_id.clone()).collect(), }, ) .unwrap(); - assert_eq!( - ret, - msgs.into_iter().map(|m| m.into()).collect::>() - ); + assert_eq!(ret, msgs); } #[test] @@ -1021,18 +745,14 @@ fn duplicate_message_id() { // make two different messages with the same ID let mut msgs = generate_messages(2); - msgs[1].id = msgs[0].id.clone(); + msgs[1].cc_id.id = msgs[0].cc_id.id.clone(); assert_ne!(msgs[0], msgs[1]); let err = app .execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::VerifyMessages(msgs.clone()), &[], ) .unwrap_err(); @@ -1047,11 +767,7 @@ fn duplicate_message_id() { .execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::RouteMessages( - msgs.iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::RouteMessages(msgs.clone()), &[], ) .unwrap_err(); @@ -1063,70 +779,39 @@ fn duplicate_message_id() { ); //verify one of them - mark_messages_as_verified( - &mut app, - verifier_address.clone(), - msgs[0..1] - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + mark_messages_as_verified(&mut app, verifier_address.clone(), msgs[0..1].to_vec()); let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages( - msgs[0..1] - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::VerifyMessages(msgs[0..1].to_vec()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs[0..1] - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + let ret = is_verified(&mut app, verifier_address.clone(), msgs[0..1].to_vec()); assert_eq!( ret, msgs[0..1] .iter() - .map(|m| (m.id.to_string(), true)) - .collect::>() + .map(|m| (m.cc_id.clone(), true)) + .collect::>() ); // other should not be verified let res = app.execute_contract( Addr::unchecked("relayer"), gateway_address.clone(), - &ExecuteMsg::VerifyMessages( - msgs[1..2] - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ), + &ExecuteMsg::VerifyMessages(msgs[1..2].to_vec()), &[], ); assert!(res.is_ok()); - let ret: Vec<(String, bool)> = is_verified( - &mut app, - verifier_address.clone(), - msgs[1..2] - .into_iter() - .map(|msg| msg.clone().try_into().unwrap()) - .collect(), - ); + let ret = is_verified(&mut app, verifier_address.clone(), msgs[1..2].to_vec()); assert_eq!( ret, msgs[1..2] .iter() - .map(|m| (m.id.to_string(), false)) - .collect::>() + .map(|m| (m.cc_id.clone(), false)) + .collect::>() ); } diff --git a/contracts/multisig-prover/src/execute.rs b/contracts/multisig-prover/src/execute.rs index 10a412634..1aed5a809 100644 --- a/contracts/multisig-prover/src/execute.rs +++ b/contracts/multisig-prover/src/execute.rs @@ -7,6 +7,7 @@ use multisig::key::{KeyType, PublicKey}; use std::str::FromStr; use axelar_wasm_std::snapshot; +use connection_router::state::CrossChainId; use connection_router::{msg::Message, state::ChainName}; use service_registry::state::Worker; @@ -65,7 +66,14 @@ fn get_messages( ) -> Result, ContractError> { let length = message_ids.len(); - let query = gateway::msg::QueryMsg::GetMessages { message_ids }; + let ids = message_ids + .into_iter() + .map(|id| { + id.parse::() + .expect("ids should have correct format") + }) + .collect::>(); + let query = gateway::msg::QueryMsg::GetMessages { message_ids: ids }; let messages: Vec = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: gateway.into(), msg: to_binary(&query)?, From e06213dda69a99b7fb7a472a5933f3ee1dcd9588 Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 21 Sep 2023 17:33:20 -0400 Subject: [PATCH 5/8] refactor: give lambda parameters telling names where it makes sense (#121) --- contracts/aggregate-verifier/tests/test.rs | 18 ++++++--- contracts/connection-router/tests/mock.rs | 2 +- contracts/gateway/src/contract.rs | 8 ++-- contracts/gateway/tests/mock.rs | 2 +- contracts/gateway/tests/tests.rs | 24 +++++------ contracts/multisig-prover/src/encoding/abi.rs | 26 ++++++------ contracts/multisig-prover/src/execute.rs | 6 +-- contracts/multisig-prover/src/query.rs | 4 +- .../src/test/mocks/voting_verifier.rs | 2 +- .../multisig-prover/src/test/test_data.rs | 4 +- contracts/multisig/src/key.rs | 2 +- contracts/multisig/src/secp256k1.rs | 4 +- contracts/service-registry/src/contract.rs | 40 ++++++++++--------- packages/axelar-wasm-std/src/voting.rs | 2 +- 14 files changed, 78 insertions(+), 66 deletions(-) diff --git a/contracts/aggregate-verifier/tests/test.rs b/contracts/aggregate-verifier/tests/test.rs index 77ada8469..1c5801c58 100644 --- a/contracts/aggregate-verifier/tests/test.rs +++ b/contracts/aggregate-verifier/tests/test.rs @@ -96,7 +96,7 @@ fn verify_messages_not_verified() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), false)) + .map(|msg| (msg.cc_id.clone(), false)) .collect::>() ); } @@ -139,7 +139,7 @@ fn verify_messages_verified() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); } @@ -183,10 +183,16 @@ fn verify_messages_mixed_status() { assert_eq!( ret, msgs.iter() - .map(|m| if verified.iter().find(|m2| *m2 == m).is_some() { - (m.cc_id.clone(), true) - } else { - (m.cc_id.clone(), false) + .map(|msg| { + if verified + .iter() + .find(|verified_msg| *verified_msg == msg) + .is_some() + { + (msg.cc_id.clone(), true) + } else { + (msg.cc_id.clone(), false) + } }) .collect::>() ); diff --git a/contracts/connection-router/tests/mock.rs b/contracts/connection-router/tests/mock.rs index c8cf1ccaf..7f20d62c8 100644 --- a/contracts/connection-router/tests/mock.rs +++ b/contracts/connection-router/tests/mock.rs @@ -57,7 +57,7 @@ pub fn get_gateway_messages( .query_wasm_smart( gateway_address, &MockGatewayQueryMsg::GetMessages { - ids: msgs.iter().map(|m| m.cc_id.clone()).collect(), + ids: msgs.iter().map(|msg| msg.cc_id.clone()).collect(), }, ) .unwrap() diff --git a/contracts/gateway/src/contract.rs b/contracts/gateway/src/contract.rs index eb7c48eef..dca0ff58a 100644 --- a/contracts/gateway/src/contract.rs +++ b/contracts/gateway/src/contract.rs @@ -59,7 +59,7 @@ pub mod execute { fn contains_duplicates(msgs: &mut Vec) -> bool { let orig_len = msgs.len(); - msgs.sort_unstable_by_key(|a| a.cc_id.to_string()); + msgs.sort_unstable_by_key(|msg| msg.cc_id.to_string()); msgs.dedup_by(|a, b| a.cc_id == b.cc_id); orig_len != msgs.len() } @@ -79,8 +79,8 @@ pub mod execute { msg: to_binary(&query_msg)?, }))?; - Ok(msgs.into_iter().partition(|m| -> bool { - match query_response.iter().find(|r| m.cc_id == r.0) { + Ok(msgs.into_iter().partition(|msg| -> bool { + match query_response.iter().find(|r| msg.cc_id == r.0) { Some((_, v)) => *v, None => false, } @@ -151,7 +151,7 @@ pub mod execute { Ok(Response::new().add_events( msgs.into_iter() - .map(|m| GatewayEvent::MessageRouted { msg: m }.into()), + .map(|msg| GatewayEvent::MessageRouted { msg }.into()), )) } } diff --git a/contracts/gateway/tests/mock.rs b/contracts/gateway/tests/mock.rs index b8af82d8e..a12a8252d 100644 --- a/contracts/gateway/tests/mock.rs +++ b/contracts/gateway/tests/mock.rs @@ -138,7 +138,7 @@ pub fn get_router_messages( .query_wasm_smart( router_address, &MockRouterQueryMsg::GetMessages { - ids: msgs.into_iter().map(|m| m.cc_id).collect(), + ids: msgs.into_iter().map(|msg| msg.cc_id).collect(), }, ) .unwrap() diff --git a/contracts/gateway/tests/tests.rs b/contracts/gateway/tests/tests.rs index 61562d2a9..01bfa690a 100644 --- a/contracts/gateway/tests/tests.rs +++ b/contracts/gateway/tests/tests.rs @@ -293,7 +293,7 @@ fn execute_one_message() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), false)) + .map(|msg| (msg.cc_id.clone(), false)) .collect::>() ); @@ -311,7 +311,7 @@ fn execute_one_message() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); @@ -327,7 +327,7 @@ fn execute_one_message() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); @@ -494,7 +494,7 @@ fn execute_not_verified_message() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), false)) + .map(|msg| (msg.cc_id.clone(), false)) .collect::>() ); @@ -512,7 +512,7 @@ fn execute_not_verified_message() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); @@ -559,7 +559,7 @@ fn execute_pre_verified_message() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); @@ -606,7 +606,7 @@ fn execute_twice() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); @@ -625,7 +625,7 @@ fn execute_twice() { assert_eq!( ret, msgs.iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); } @@ -668,7 +668,7 @@ fn receive_one_message() { .query_wasm_smart( gateway_address, &QueryMsg::GetMessages { - message_ids: msgs.iter().map(|m| m.cc_id.clone()).collect(), + message_ids: msgs.iter().map(|msg| msg.cc_id.clone()).collect(), }, ) .unwrap(); @@ -713,7 +713,7 @@ fn receive_many_messages() { .query_wasm_smart( gateway_address, &QueryMsg::GetMessages { - message_ids: msgs.iter().map(|m| m.cc_id.clone()).collect(), + message_ids: msgs.iter().map(|msg| msg.cc_id.clone()).collect(), }, ) .unwrap(); @@ -793,7 +793,7 @@ fn duplicate_message_id() { ret, msgs[0..1] .iter() - .map(|m| (m.cc_id.clone(), true)) + .map(|msg| (msg.cc_id.clone(), true)) .collect::>() ); @@ -811,7 +811,7 @@ fn duplicate_message_id() { ret, msgs[1..2] .iter() - .map(|m| (m.cc_id.clone(), false)) + .map(|msg| (msg.cc_id.clone(), false)) .collect::>() ); } diff --git a/contracts/multisig-prover/src/encoding/abi.rs b/contracts/multisig-prover/src/encoding/abi.rs index 8f140785f..cfb952f40 100644 --- a/contracts/multisig-prover/src/encoding/abi.rs +++ b/contracts/multisig-prover/src/encoding/abi.rs @@ -115,10 +115,11 @@ pub fn make_operators(worker_set: WorkerSet) -> Operators { let mut operators: Vec<(HexBinary, Uint256)> = worker_set .signers .iter() - .map(|s| { + .map(|signer| { ( - evm_address(s.pub_key.as_ref()).expect("couldn't convert pubkey to evm address"), - s.weight, + evm_address(signer.pub_key.as_ref()) + .expect("couldn't convert pubkey to evm address"), + signer.weight, ) }) .collect(); @@ -154,10 +155,11 @@ pub fn transfer_operatorship_params(worker_set: &WorkerSet) -> Result = worker_set .signers .iter() - .map(|s| { + .map(|signer| { ( - evm_address(s.pub_key.as_ref()).expect("couldn't convert pubkey to evm address"), - s.weight, + evm_address(signer.pub_key.as_ref()) + .expect("couldn't convert pubkey to evm address"), + signer.weight, ) }) .collect(); @@ -183,8 +185,8 @@ pub fn transfer_operatorship_params(worker_set: &WorkerSet) -> Result Result { let pub_key = - PublicKey::from_sec1_bytes(pub_key).map_err(|e| ContractError::InvalidPublicKey { - reason: e.to_string(), + PublicKey::from_sec1_bytes(pub_key).map_err(|err| ContractError::InvalidPublicKey { + reason: err.to_string(), })?; let pub_key = pub_key.to_encoded_point(false); @@ -198,19 +200,19 @@ pub fn command_params( payload_hash: HexBinary, ) -> Result { let destination_address = - ethereum_types::Address::from_str(&destination_address).map_err(|e| { + ethereum_types::Address::from_str(&destination_address).map_err(|err| { ContractError::InvalidMessage { - reason: format!("destination_address is not a valid EVM address: {}", e), + reason: format!("destination_address is not a valid EVM address: {}", err), } })?; let payload_hash: [u8; 32] = payload_hash .as_slice() .try_into() - .map_err(|e| ContractError::InvalidMessage { + .map_err(|err| ContractError::InvalidMessage { reason: format!( "payload_hash length is not a valid keccak256 hash length: {}", - e + err ), })?; Ok(ethabi::encode(&[ diff --git a/contracts/multisig-prover/src/execute.rs b/contracts/multisig-prover/src/execute.rs index 1aed5a809..b6c694d39 100644 --- a/contracts/multisig-prover/src/execute.rs +++ b/contracts/multisig-prover/src/execute.rs @@ -250,10 +250,10 @@ pub fn confirm_worker_set(deps: DepsMut) -> Result { pub_keys_by_address: worker_set .signers .into_iter() - .map(|s| { + .map(|signer| { ( - s.address.to_string(), - (KeyType::Ecdsa, s.pub_key.as_ref().into()), + signer.address.to_string(), + (KeyType::Ecdsa, signer.pub_key.as_ref().into()), ) }) .collect(), diff --git a/contracts/multisig-prover/src/query.rs b/contracts/multisig-prover/src/query.rs index ca5620d4f..d6643c7cd 100644 --- a/contracts/multisig-prover/src/query.rs +++ b/contracts/multisig-prover/src/query.rs @@ -29,8 +29,8 @@ pub fn get_proof(deps: Deps, multisig_session_id: Uint64) -> StdResult { let execute_data = batch .encode_execute_data(multisig.quorum, multisig.signers) - .map_err(|e| { - StdError::generic_err(format!("failed to encode execute data: {}", e)) + .map_err(|err| { + StdError::generic_err(format!("failed to encode execute data: {}", err)) })?; ProofStatus::Completed { execute_data } diff --git a/contracts/multisig-prover/src/test/mocks/voting_verifier.rs b/contracts/multisig-prover/src/test/mocks/voting_verifier.rs index 18a34890d..133172dbc 100644 --- a/contracts/multisig-prover/src/test/mocks/voting_verifier.rs +++ b/contracts/multisig-prover/src/test/mocks/voting_verifier.rs @@ -50,7 +50,7 @@ pub fn confirm_worker_set( ) { let mut new_operators: Vec<(HexBinary, Uint256)> = workers .iter() - .map(|w| (w.operator.clone(), w.weight)) + .map(|worker| (worker.operator.clone(), worker.weight)) .collect(); new_operators.sort_by_key(|op| op.0.clone()); app.execute_contract( diff --git a/contracts/multisig-prover/src/test/test_data.rs b/contracts/multisig-prover/src/test/test_data.rs index 16b9d32da..63258cd4c 100644 --- a/contracts/multisig-prover/src/test/test_data.rs +++ b/contracts/multisig-prover/src/test/test_data.rs @@ -183,8 +183,8 @@ pub fn operators() -> Vec { .unwrap(), operator: HexBinary::from_hex(operator).unwrap(), weight: Uint256::from(weight), - signature: signature.map(|s| { - (KeyType::Ecdsa, HexBinary::from_hex(s).unwrap()) + signature: signature.map(|sig| { + (KeyType::Ecdsa, HexBinary::from_hex(sig).unwrap()) .try_into() .unwrap() }), diff --git a/contracts/multisig/src/key.rs b/contracts/multisig/src/key.rs index 672b2691e..84e3a2405 100644 --- a/contracts/multisig/src/key.rs +++ b/contracts/multisig/src/key.rs @@ -31,7 +31,7 @@ where { let pk: HexBinary = Deserialize::deserialize(deserializer)?; PublicKey::try_from((KeyType::Ecdsa, pk.clone())) - .map_err(|e| D::Error::custom(format!("failed to deserialize public key: {}", e)))?; + .map_err(|err| D::Error::custom(format!("failed to deserialize public key: {}", err)))?; Ok(pk) } diff --git a/contracts/multisig/src/secp256k1.rs b/contracts/multisig/src/secp256k1.rs index 6880a60fd..46b2d7259 100644 --- a/contracts/multisig/src/secp256k1.rs +++ b/contracts/multisig/src/secp256k1.rs @@ -6,9 +6,9 @@ use crate::ContractError; const ECDSA_SIGNATURE_LEN: usize = 64; pub fn ecdsa_verify(msg_hash: &[u8], sig: &[u8], pub_key: &[u8]) -> Result { - secp256k1_verify(msg_hash, &sig[0..ECDSA_SIGNATURE_LEN], pub_key).map_err(|e| { + secp256k1_verify(msg_hash, &sig[0..ECDSA_SIGNATURE_LEN], pub_key).map_err(|err| { ContractError::SignatureVerificationFailed { - reason: e.to_string(), + reason: err.to_string(), } }) } diff --git a/contracts/service-registry/src/contract.rs b/contracts/service-registry/src/contract.rs index 5ce2562ea..370a5cbd8 100644 --- a/contracts/service-registry/src/contract.rs +++ b/contracts/service-registry/src/contract.rs @@ -70,7 +70,7 @@ pub fn execute( execute::require_governance(&deps, info)?; let workers = workers .into_iter() - .map(|w| deps.api.addr_validate(&w)) + .map(|worker| deps.api.addr_validate(&worker)) .collect::, _>>()?; execute::update_worker_authorization_status( deps, @@ -86,7 +86,7 @@ pub fn execute( execute::require_governance(&deps, info)?; let workers = workers .into_iter() - .map(|w| deps.api.addr_validate(&w)) + .map(|worker| deps.api.addr_validate(&worker)) .collect::, _>>()?; execute::update_worker_authorization_status( deps, @@ -137,21 +137,25 @@ pub mod execute { ) -> Result { let key = &service_name.clone(); - SERVICES.update(deps.storage, key, |s| -> Result { - match s { - None => Ok(Service { - name: service_name, - service_contract, - min_num_workers, - max_num_workers, - min_worker_bond, - bond_denom, - unbonding_period_days, - description, - }), - _ => Err(ContractError::ServiceAlreadyExists), - } - })?; + SERVICES.update( + deps.storage, + key, + |service| -> Result { + match service { + None => Ok(Service { + name: service_name, + service_contract, + min_num_workers, + max_num_workers, + min_worker_bond, + bond_denom, + unbonding_period_days, + description, + }), + _ => Err(ContractError::ServiceAlreadyExists), + } + }, + )?; // Response with attributes? event? Ok(Response::new()) @@ -328,7 +332,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result to_binary(&query::get_active_workers(deps, service_name, chain_name)?) - .map_err(|e| e.into()), + .map_err(|err| err.into()), } } diff --git a/packages/axelar-wasm-std/src/voting.rs b/packages/axelar-wasm-std/src/voting.rs index f5e005aac..33ff1bcda 100644 --- a/packages/axelar-wasm-std/src/voting.rs +++ b/packages/axelar-wasm-std/src/voting.rs @@ -197,7 +197,7 @@ impl Poll for WeightedPoll { results: self .votes .iter() - .map(|v| *v >= self.snapshot.quorum.into()) + .map(|tally| *tally >= self.snapshot.quorum.into()) .collect(), }) } From 5bbcdf18e8b93cd5dd44ac0ab2a9e5b1a91020e1 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:32:36 -0400 Subject: [PATCH 6/8] feat(multisig-prover): encode commands via bcs (#115) * feat(multisig-prover): encode commands via bcs --- Cargo.lock | 11 + contracts/multisig-prover/Cargo.toml | 1 + contracts/multisig-prover/src/encoding/abi.rs | 4 +- contracts/multisig-prover/src/encoding/bcs.rs | 224 ++++++++++++++++++ contracts/multisig-prover/src/encoding/mod.rs | 28 ++- contracts/multisig-prover/src/error.rs | 3 + contracts/multisig-prover/src/execute.rs | 4 +- 7 files changed, 267 insertions(+), 8 deletions(-) create mode 100644 contracts/multisig-prover/src/encoding/bcs.rs diff --git a/Cargo.lock b/Cargo.lock index 726cb56a7..4a84bdf9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,6 +429,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bcs" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd3ffe8b19a604421a5d461d4a70346223e535903fbc3067138bddbebddcf77" +dependencies = [ + "serde", + "thiserror", +] + [[package]] name = "bech32" version = "0.9.1" @@ -3047,6 +3057,7 @@ dependencies = [ "anyhow", "axelar-wasm-std", "axelar-wasm-std-derive", + "bcs", "connection-router", "cosmwasm-schema", "cosmwasm-std", diff --git a/contracts/multisig-prover/Cargo.toml b/contracts/multisig-prover/Cargo.toml index 6ab434dd5..27d76c5d0 100644 --- a/contracts/multisig-prover/Cargo.toml +++ b/contracts/multisig-prover/Cargo.toml @@ -30,6 +30,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \ [dependencies] axelar-wasm-std = { workspace = true } axelar-wasm-std-derive = { workspace = true } +bcs = "0.1.5" connection-router = { workspace = true, features = ["library"] } cosmwasm-schema = "1.1.3" cosmwasm-std = "1.1.3" diff --git a/contracts/multisig-prover/src/encoding/abi.rs b/contracts/multisig-prover/src/encoding/abi.rs index cfb952f40..0a296ba79 100644 --- a/contracts/multisig-prover/src/encoding/abi.rs +++ b/contracts/multisig-prover/src/encoding/abi.rs @@ -46,7 +46,7 @@ pub fn encode(data: &Data) -> HexBinary { .into() } -pub fn msg_to_sign(command_batch: &CommandBatch) -> HexBinary { +pub fn msg_digest(command_batch: &CommandBatch) -> HexBinary { let msg = Keccak256::digest(encode(&command_batch.data).as_slice()); // Prefix for standard EVM signed data https://eips.ethereum.org/EIPS/eip-191 @@ -614,7 +614,7 @@ mod test { encoder: Encoder::Abi, }; - let res = batch.msg_to_sign(); + let res = batch.msg_digest(); let expected_msg = test_data::msg_to_sign(); assert_eq!(res, expected_msg); diff --git a/contracts/multisig-prover/src/encoding/bcs.rs b/contracts/multisig-prover/src/encoding/bcs.rs new file mode 100644 index 000000000..b8bccd9a9 --- /dev/null +++ b/contracts/multisig-prover/src/encoding/bcs.rs @@ -0,0 +1,224 @@ +use bcs::to_bytes; +use cosmwasm_std::{HexBinary, Uint256}; +use itertools::Itertools; + +use crate::error::ContractError; + +use super::Data; + +// TODO: all of the public functions in this file should be moved to a trait, +// that has an abi and bcs implementation (and possibly others) + +pub fn command_params( + source_chain: String, + source_address: String, + destination_address: String, + payload_hash: HexBinary, +) -> Result { + if payload_hash.len() != 32 { + return Err(ContractError::InvalidMessage { + reason: format!("payload hash is not 32 bytes {}", payload_hash.to_hex()), + }); + } + + let destination_address = <[u8; 32]>::try_from( + HexBinary::from_hex(&destination_address)?.to_vec(), + ) + .map_err(|_| ContractError::InvalidMessage { + reason: format!( + "destination_address is not a valid Sui address: {}", + destination_address + ), + })?; + + Ok(to_bytes(&( + source_chain, + source_address, + destination_address, + payload_hash.to_vec(), + )) + .expect("couldn't serialize command as bcs") + .into()) +} + +fn u256_to_u64(chain_id: Uint256) -> u64 { + chain_id.to_string().parse().expect("chain_id is invalid") +} + +fn make_command_id(command_id: &HexBinary) -> [u8; 32] { + // command-ids are fixed length sequences + command_id + .to_vec() + .try_into() + .expect("couldn't convert command id to 32 byte array") +} + +pub fn encode(data: &Data) -> HexBinary { + // destination chain id must be u64 for sui + let destination_chain_id = u256_to_u64(data.destination_chain_id); + + let (commands_ids, command_types, command_params): (Vec<[u8; 32]>, Vec, Vec>) = + data.commands + .iter() + .map(|command| { + ( + make_command_id(&command.id), + command.ty.to_string(), + command.params.to_vec(), + ) + }) + .multiunzip(); + + to_bytes(&( + destination_chain_id, + commands_ids, + command_types, + command_params, + )) + .expect("couldn't encode batch as bcs") + .into() +} + +#[cfg(test)] +mod test { + + use std::vec; + + use bcs::from_bytes; + use cosmwasm_std::{HexBinary, Uint256}; + + use crate::{ + encoding::{ + bcs::{command_params, encode, make_command_id, u256_to_u64}, + Data, + }, + types::Command, + }; + + #[test] + fn test_chain_id_as_u64() { + let chain_id = 1u64; + assert_eq!(chain_id, u256_to_u64(Uint256::from(chain_id as u128))); + } + + #[test] + #[should_panic] + fn test_chain_id_as_u64_fails() { + let chain_id = u128::MAX; + u256_to_u64(Uint256::from(chain_id)); + } + + #[test] + fn test_make_command_id() { + assert_eq!([0; 32], make_command_id(&HexBinary::from(vec![0; 32]))); + } + + #[test] + #[should_panic] + fn test_make_command_id_fails_too_large() { + make_command_id(&HexBinary::from(vec![0; 30])); + } + + #[test] + fn test_command_params() { + let res = command_params( + "Ethereum".into(), + "00".into(), + "01".repeat(32).into(), + HexBinary::from_hex(&"02".repeat(32)).unwrap(), + ); + assert!(res.is_ok()); + + let res = res.unwrap(); + let params = from_bytes(&res.to_vec()); + assert!(params.is_ok()); + let (source_chain, source_address, destination_address, payload_hash): ( + String, + String, + [u8; 32], + Vec, + ) = params.unwrap(); + assert_eq!(source_chain, "Ethereum".to_string()); + + assert_eq!(source_address, "00".to_string()); + + assert_eq!( + destination_address.to_vec(), + HexBinary::from_hex(&"01".repeat(32)).unwrap().to_vec() + ); + + assert_eq!(payload_hash, vec![2; 32]); + } + + #[test] + fn test_invalid_destination_address() { + let res = command_params( + "Ethereum".into(), + "00".into(), + "01".into(), + HexBinary::from_hex("02").unwrap(), + ); + assert!(!res.is_ok()); + } + + #[test] + fn test_encode() { + let source_chain = "Ethereum"; + let source_address = "AA"; + let destination_address = "BB".repeat(32); + let payload_hash = HexBinary::from_hex(&"CC".repeat(32)).unwrap(); + let destination_chain_id = 1u64; + let command_id = HexBinary::from_hex(&"FF".repeat(32)).unwrap(); + let data = Data { + destination_chain_id: destination_chain_id.into(), + commands: vec![Command { + id: command_id.clone(), + ty: crate::types::CommandType::ApproveContractCall, + params: command_params( + source_chain.into(), + source_address.into(), + destination_address.clone().into(), + payload_hash.clone().into(), + ) + .unwrap(), + }], + }; + let encoded = encode(&data); + let decoded: Result<(u64, Vec<[u8; 32]>, Vec, Vec>), _> = + from_bytes(&encoded.to_vec()); + assert!(decoded.is_ok()); + let (chain_id, command_ids, command_types, params) = decoded.unwrap(); + + assert_eq!(chain_id, destination_chain_id); + + assert_eq!(command_ids.len(), 1); + assert_eq!(command_ids[0].to_vec(), command_id.to_vec()); + + assert_eq!(command_types.len(), 1); + assert_eq!( + command_types[0], + crate::types::CommandType::ApproveContractCall.to_string() + ); + + assert_eq!(params.len(), 1); + let command = from_bytes(¶ms[0]); + assert!(command.is_ok()); + let ( + source_chain_decoded, + source_address_decoded, + destination_address_decoded, + payload_hash_decoded, + ): (String, String, [u8; 32], Vec) = command.unwrap(); + + assert_eq!(source_chain_decoded, source_chain); + + assert_eq!(source_address_decoded, source_address); + + assert_eq!( + destination_address_decoded.to_vec(), + HexBinary::from_hex(&destination_address).unwrap().to_vec() + ); + + assert_eq!(payload_hash_decoded, payload_hash.to_vec()); + } +} diff --git a/contracts/multisig-prover/src/encoding/mod.rs b/contracts/multisig-prover/src/encoding/mod.rs index 4236e38a5..c6a9c03f3 100644 --- a/contracts/multisig-prover/src/encoding/mod.rs +++ b/contracts/multisig-prover/src/encoding/mod.rs @@ -1,4 +1,5 @@ mod abi; +mod bcs; use axelar_wasm_std::operators::Operators; use cosmwasm_schema::cw_serde; @@ -31,7 +32,12 @@ fn make_command(msg: Message, encoding: Encoder) -> Result todo!(), + Encoder::Bcs => bcs::command_params( + msg.source_chain, + msg.source_address, + msg.destination_address, + msg.payload_hash, + )?, }, id: command_id(msg.id), }) @@ -104,9 +110,9 @@ impl CommandBatchBuilder { } impl CommandBatch { - pub fn msg_to_sign(&self) -> HexBinary { + pub fn msg_digest(&self) -> HexBinary { match self.encoder { - Encoder::Abi => abi::msg_to_sign(self), + Encoder::Abi => abi::msg_digest(self), Encoder::Bcs => todo!(), } } @@ -133,7 +139,7 @@ impl Data { pub fn encode(&self, encoder: Encoder) -> HexBinary { match encoder { Encoder::Abi => abi::encode(self), - Encoder::Bcs => todo!(), + Encoder::Bcs => bcs::encode(self), } } } @@ -187,6 +193,20 @@ mod test { .unwrap() ); assert_eq!(res.ty, CommandType::ApproveContractCall); + + let mut router_message = router_message.to_owned(); + router_message.destination_address = "FF".repeat(32); + let res = make_command(router_message.to_owned(), Encoder::Bcs); + assert!(res.is_ok()); + + let res = res.unwrap(); + + assert_eq!( + res.id, + HexBinary::from_hex("3ee2f8af2201994e3518c9ce6848774785c2eef3bdbf9f954899497616dd59af") + .unwrap() + ); + assert_eq!(res.ty, CommandType::ApproveContractCall); } #[test] diff --git a/contracts/multisig-prover/src/error.rs b/contracts/multisig-prover/src/error.rs index e236b6025..e269465b3 100644 --- a/contracts/multisig-prover/src/error.rs +++ b/contracts/multisig-prover/src/error.rs @@ -35,6 +35,9 @@ pub enum ContractError { #[error(transparent)] NonEmptyError(#[from] nonempty::Error), + #[error(transparent)] + BcsError(#[from] bcs::Error), + #[error("worker set has not changed sufficiently since last update")] WorkerSetUnchanged, diff --git a/contracts/multisig-prover/src/execute.rs b/contracts/multisig-prover/src/execute.rs index b6c694d39..15eaa5243 100644 --- a/contracts/multisig-prover/src/execute.rs +++ b/contracts/multisig-prover/src/execute.rs @@ -50,7 +50,7 @@ pub fn construct_proof(deps: DepsMut, message_ids: Vec) -> Result Result Date: Fri, 22 Sep 2023 11:51:20 -0400 Subject: [PATCH 7/8] fix: add necessary env variables to codecov job (#123) --- .github/workflows/codecov.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml index 61abab3af..9415571fa 100644 --- a/.github/workflows/codecov.yaml +++ b/.github/workflows/codecov.yaml @@ -4,8 +4,8 @@ on: pull_request: push: branches: - - 'main' - - 'releases/**' + - main + - releases/** jobs: coverage: @@ -35,6 +35,9 @@ jobs: - name: Generate code coverage run: cargo llvm-cov --workspace --lcov --output-path lcov.info + env: + RUSTFLAGS: --cfg tracing_unstable + RUST_BACKTRACE: 1 - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 From ff6041542bf1da930dec10aa5ca7eabff143acd4 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:58:43 -0400 Subject: [PATCH 8/8] feat(multisig-prover): encode bcs proof (#116) * feat(multisig-prover): encode bcs proof --- contracts/multisig-prover/src/encoding/bcs.rs | 136 +++++++++++++++++- 1 file changed, 129 insertions(+), 7 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/bcs.rs b/contracts/multisig-prover/src/encoding/bcs.rs index b8bccd9a9..7605da1d6 100644 --- a/contracts/multisig-prover/src/encoding/bcs.rs +++ b/contracts/multisig-prover/src/encoding/bcs.rs @@ -1,14 +1,50 @@ use bcs::to_bytes; use cosmwasm_std::{HexBinary, Uint256}; use itertools::Itertools; +use multisig::{key::Signature, msg::Signer}; -use crate::error::ContractError; +use crate::{error::ContractError, types::Operator}; use super::Data; // TODO: all of the public functions in this file should be moved to a trait, // that has an abi and bcs implementation (and possibly others) +#[allow(dead_code)] +fn encode_proof( + quorum: Uint256, + signers: Vec<(Signer, Option)>, +) -> Result { + let mut operators = make_operators_with_sigs(signers); + operators.sort(); // gateway requires operators to be sorted + + let (addresses, weights, signatures): (Vec<_>, Vec<_>, Vec<_>) = operators + .iter() + .map(|op| { + ( + op.address.to_vec(), + u256_to_u128(op.weight), + op.signature.as_ref().map(|sig| sig.as_ref().to_vec()), + ) + }) + .multiunzip(); + + let signatures: Vec> = signatures.into_iter().flatten().collect(); + let quorum = u256_to_u128(quorum); + Ok(to_bytes(&(addresses, weights, quorum, signatures))?.into()) +} + +fn make_operators_with_sigs(signers_with_sigs: Vec<(Signer, Option)>) -> Vec { + signers_with_sigs + .into_iter() + .map(|(signer, sig)| Operator { + address: signer.pub_key.into(), + weight: signer.weight, + signature: sig, + }) + .collect() +} + pub fn command_params( source_chain: String, source_address: String, @@ -41,10 +77,6 @@ pub fn command_params( .into()) } -fn u256_to_u64(chain_id: Uint256) -> u64 { - chain_id.to_string().parse().expect("chain_id is invalid") -} - fn make_command_id(command_id: &HexBinary) -> [u8; 32] { // command-ids are fixed length sequences command_id @@ -79,28 +111,118 @@ pub fn encode(data: &Data) -> HexBinary { .into() } +fn u256_to_u128(val: Uint256) -> u128 { + val.to_string().parse().expect("value is larger than u128") +} + +fn u256_to_u64(chain_id: Uint256) -> u64 { + chain_id + .to_string() + .parse() + .expect("value is larger than u64") +} + #[cfg(test)] mod test { use std::vec; use bcs::from_bytes; - use cosmwasm_std::{HexBinary, Uint256}; + use cosmwasm_std::{Addr, HexBinary, Uint256}; + use multisig::{ + key::{PublicKey, Signature}, + msg::Signer, + }; use crate::{ encoding::{ - bcs::{command_params, encode, make_command_id, u256_to_u64}, + bcs::{ + command_params, encode, encode_proof, make_command_id, u256_to_u128, u256_to_u64, + }, Data, }, types::Command, }; + #[test] + fn test_u256_to_u128() { + let val = u128::MAX; + assert_eq!(val, u256_to_u128(Uint256::from(val))); + } + #[test] fn test_chain_id_as_u64() { let chain_id = 1u64; assert_eq!(chain_id, u256_to_u64(Uint256::from(chain_id as u128))); } + #[test] + #[should_panic] + fn test_u256_to_u128_fails() { + let _ = u256_to_u128(Uint256::MAX); + } + + #[test] + fn test_encode_proof() { + let signers = vec![ + (Signer { + address: Addr::unchecked("axelarvaloper1ff675m593vve8yh82lzhdnqfpu7m23cxstr6h4"), + weight: Uint256::from(10u128), + pub_key: PublicKey::Ecdsa( + HexBinary::from_hex( + "03c6ddb0fcee7b528da1ef3c9eed8d51eeacd7cc28a8baa25c33037c5562faa6e4", + ) + .unwrap(), + ), + }, + Some(Signature::Ecdsa( + HexBinary::from_hex("283786d844a7c4d1d424837074d0c8ec71becdcba4dd42b5307cb543a0e2c8b81c10ad541defd5ce84d2a608fc454827d0b65b4865c8192a2ea1736a5c4b72021b").unwrap()))), + (Signer { + address: Addr::unchecked("axelarvaloper1x86a8prx97ekkqej2x636utrdu23y8wupp9gk5"), + weight: Uint256::from(10u128), + pub_key: PublicKey::Ecdsa( + HexBinary::from_hex( + "03d123ce370b163acd576be0e32e436bb7e63262769881d35fa3573943bf6c6f81", + ) + .unwrap(), + ), + }, + Some(Signature::Ecdsa( + HexBinary::from_hex("283786d844a7c4d1d424837074d0c8ec71becdcba4dd42b5307cb543a0e2c8b81c10ad541defd5ce84d2a608fc454827d0b65b4865c8192a2ea1736a5c4b72021b").unwrap())))]; + + let quorum = Uint256::from(10u128); + let proof = encode_proof(quorum, signers.clone()); + + assert!(proof.is_ok()); + let proof = proof.unwrap(); + let decoded_proof: Result<(Vec>, Vec, u128, Vec>), _> = + from_bytes(&proof); + assert!(decoded_proof.is_ok()); + let (operators, weights, quorum_decoded, signatures): ( + Vec>, + Vec, + u128, + Vec>, + ) = decoded_proof.unwrap(); + + assert_eq!(operators.len(), signers.len()); + assert_eq!(weights.len(), signers.len()); + assert_eq!(signatures.len(), signers.len()); + assert_eq!(quorum_decoded, 10u128); + + for i in 0..signers.len() { + assert_eq!( + operators[i], + HexBinary::from(signers[i].0.pub_key.clone()).to_vec() + ); + assert_eq!(weights[i], 10u128); + assert_eq!( + signatures[i], + HexBinary::from(signers[i].1.clone().unwrap()).to_vec() + ); + } + } + #[test] #[should_panic] fn test_chain_id_as_u64_fails() {