diff --git a/Cargo.lock b/Cargo.lock index 4677d98fb..6830e3192 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,7 +269,7 @@ dependencies = [ "router-api", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.11.0", "service-registry-api", "sha3", "stellar", @@ -857,6 +857,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "serde_with 3.11.0", "sha3", "stellar-xdr", "strum 0.25.0", @@ -7497,9 +7498,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ "base64 0.22.1", "chrono", @@ -7509,7 +7510,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros 3.8.1", + "serde_with_macros 3.11.0", "time", ] @@ -7527,9 +7528,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling 0.20.9", "proc-macro2 1.0.85", @@ -7933,7 +7934,7 @@ dependencies = [ "hex", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.11.0", "stellar-strkey 0.0.8", ] diff --git a/ampd/src/evm/verifier.rs b/ampd/src/evm/verifier.rs index b29c702f0..d849448d2 100644 --- a/ampd/src/evm/verifier.rs +++ b/ampd/src/evm/verifier.rs @@ -2,7 +2,6 @@ use axelar_wasm_std::voting::Vote; use ethers_contract::EthLogDecode; use ethers_core::types::{Log, TransactionReceipt, H256}; use evm_gateway::{IAxelarAmplifierGatewayEvents, WeightedSigners}; -use num_traits::cast; use crate::handlers::evm_verify_msg::Message; use crate::handlers::evm_verify_verifier_set::VerifierSetConfirmation; @@ -16,7 +15,7 @@ impl PartialEq> for &Message { match event { IAxelarAmplifierGatewayEvents::ContractCallFilter(event) => { - log.transaction_hash == Some(self.tx_id) + log.transaction_hash == Some(self.message_id.tx_hash.into()) && event.sender == self.source_address && self.destination_chain == event.destination_chain && event.destination_contract_address == self.destination_address @@ -37,7 +36,7 @@ impl PartialEq> for &VerifierSetConfirmation { Err(_) => return false, }; - log.transaction_hash == Some(self.tx_id) + log.transaction_hash == Some(self.message_id.tx_hash.into()) && event.signers_hash == weighted_signers.hash() && event.signers == weighted_signers.abi_encode() } @@ -53,9 +52,9 @@ fn has_failed(tx_receipt: &TransactionReceipt) -> bool { fn event<'a>( gateway_address: &EVMAddress, tx_receipt: &'a TransactionReceipt, - log_index: u32, + log_index: u64, ) -> Option> { - let log_index: usize = cast(log_index).expect("log_index must be a valid usize"); + let log_index: usize = usize::try_from(log_index).ok()?; tx_receipt .logs @@ -74,7 +73,7 @@ fn verify<'a, V>( tx_receipt: &'a TransactionReceipt, to_verify: V, expected_transaction_hash: H256, - expected_event_index: u32, + expected_event_index: u64, ) -> Vote where V: PartialEq>, @@ -98,7 +97,13 @@ pub fn verify_message( tx_receipt: &TransactionReceipt, msg: &Message, ) -> Vote { - verify(gateway_address, tx_receipt, msg, msg.tx_id, msg.event_index) + verify( + gateway_address, + tx_receipt, + msg, + msg.message_id.tx_hash.into(), + msg.message_id.event_index, + ) } pub fn verify_verifier_set( @@ -110,13 +115,14 @@ pub fn verify_verifier_set( gateway_address, tx_receipt, confirmation, - confirmation.tx_id, - confirmation.event_index, + confirmation.message_id.tx_hash.into(), + confirmation.message_id.event_index, ) } #[cfg(test)] mod tests { + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::Vote; use cosmwasm_std::Uint128; use ethers_contract::EthEvent; @@ -137,7 +143,7 @@ mod tests { let (gateway_address, tx_receipt, mut verifier_set) = matching_verifier_set_and_tx_receipt(); - verifier_set.tx_id = Hash::random(); + verifier_set.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound @@ -172,17 +178,17 @@ mod tests { let (gateway_address, tx_receipt, mut verifier_set) = matching_verifier_set_and_tx_receipt(); - verifier_set.event_index = 0; + verifier_set.message_id.event_index = 0; assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound ); - verifier_set.event_index = 2; + verifier_set.message_id.event_index = 2; assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound ); - verifier_set.event_index = 3; + verifier_set.message_id.event_index = 3; assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound @@ -215,7 +221,7 @@ mod tests { fn should_not_verify_msg_if_tx_id_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); - msg.tx_id = Hash::random(); + msg.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -248,17 +254,17 @@ mod tests { fn should_not_verify_msg_if_log_index_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); - msg.event_index = 0; + msg.message_id.event_index = 0; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound ); - msg.event_index = 2; + msg.message_id.event_index = 2; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound ); - msg.event_index = 3; + msg.message_id.event_index = 3; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -295,8 +301,7 @@ mod tests { let verifier_set = build_verifier_set(KeyType::Ecdsa, &ecdsa_test_data::signers()); let verifier_set = VerifierSetConfirmation { - tx_id, - event_index: log_index, + message_id: HexTxHashAndEventIndex::new(tx_id, log_index as u64), verifier_set, }; @@ -331,8 +336,10 @@ mod tests { let gateway_address = EVMAddress::random(); let msg = Message { - tx_id, - event_index: log_index, + message_id: HexTxHashAndEventIndex::new(tx_id, log_index as u64) + .to_string() + .parse() + .unwrap(), source_address: "0xd48e199950589a4336e4dc43bd2c72ba0c0baa86" .parse() .unwrap(), diff --git a/ampd/src/handlers/errors.rs b/ampd/src/handlers/errors.rs index bd04d31ef..03608f16f 100644 --- a/ampd/src/handlers/errors.rs +++ b/ampd/src/handlers/errors.rs @@ -12,4 +12,6 @@ pub enum Error { TxReceipts, #[error("unsupported key type {0}")] KeyType(String), + #[error("failed to parse message ids")] + MessageIds, } diff --git a/ampd/src/handlers/evm_verify_msg.rs b/ampd/src/handlers/evm_verify_msg.rs index 6fe8ca131..e273df2d2 100644 --- a/ampd/src/handlers/evm_verify_msg.rs +++ b/ampd/src/handlers/evm_verify_msg.rs @@ -1,5 +1,6 @@ use std::collections::{HashMap, HashSet}; use std::convert::TryInto; +use std::str::FromStr; use async_trait::async_trait; use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; @@ -12,6 +13,7 @@ use ethers_core::types::{TransactionReceipt, U64}; use events::Error::EventTypeMismatch; use events_derive::try_from; use futures::future::join_all; +use itertools::Itertools; use router_api::ChainName; use serde::Deserialize; use tokio::sync::watch::Receiver; @@ -32,8 +34,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct Message { - pub tx_id: Hash, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub destination_address: String, pub destination_chain: ChainName, pub source_address: EVMAddress, @@ -121,7 +122,6 @@ where }) .collect()) } - fn vote_msg(&self, poll_id: PollId, votes: Vec) -> MsgExecuteContract { MsgExecuteContract { sender: self.verifier.as_ref().clone(), @@ -174,24 +174,26 @@ where return Ok(vec![]); } - let tx_hashes: HashSet<_> = messages.iter().map(|message| message.tx_id).collect(); + let tx_hashes: HashSet = messages + .iter() + .map(|msg| Hash::from_str(&msg.message_id.tx_hash_as_hex().to_string())) + .try_collect() + .change_context(Error::MessageIds)?; let finalized_tx_receipts = self .finalized_tx_receipts(tx_hashes, confirmation_height) .await?; let poll_id_str: String = poll_id.into(); let source_chain_str: String = source_chain.into(); - let message_ids = messages - .iter() - .map(|message| { - HexTxHashAndEventIndex::new(message.tx_id, message.event_index).to_string() - }) - .collect::>(); let votes = info_span!( "verify messages from an EVM chain", poll_id = poll_id_str, source_chain = source_chain_str, - message_ids = message_ids.as_value() + message_ids = messages + .iter() + .map(|msg| msg.message_id.to_string()) + .collect::>() + .as_value(), ) .in_scope(|| { info!("ready to verify messages in poll",); @@ -200,7 +202,7 @@ where .iter() .map(|msg| { finalized_tx_receipts - .get(&msg.tx_id) + .get(&msg.message_id.tx_hash.into()) .map_or(Vote::NotFound, |tx_receipt| { verify_message(&source_gateway_address, tx_receipt, msg) }) @@ -226,6 +228,7 @@ mod tests { use std::convert::TryInto; use std::str::FromStr; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use base64::engine::general_purpose::STANDARD; use base64::Engine; use cosmwasm_std; @@ -247,6 +250,11 @@ mod tests { use crate::PREFIX; fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_ids = vec![ + HexTxHashAndEventIndex::new(Hash::random(), 0u64), + HexTxHashAndEventIndex::new(Hash::random(), 1u64), + HexTxHashAndEventIndex::new(Hash::random(), 10u64), + ]; PollStarted::Messages { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -263,24 +271,27 @@ mod tests { }, messages: vec![ TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 0, + tx_id: msg_ids[0].tx_hash_as_hex(), + event_index: msg_ids[0].event_index as u32, + message_id: msg_ids[0].to_string().parse().unwrap(), source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), payload_hash: Hash::random().to_fixed_bytes(), }, TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 1, + tx_id: msg_ids[1].tx_hash_as_hex(), + event_index: msg_ids[1].event_index as u32, + message_id: msg_ids[1].to_string().parse().unwrap(), source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), payload_hash: Hash::random().to_fixed_bytes(), }, TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 10, + tx_id: msg_ids[2].tx_hash_as_hex(), + event_index: msg_ids[2].event_index as u32, + message_id: msg_ids[2].to_string().parse().unwrap(), source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), diff --git a/ampd/src/handlers/evm_verify_verifier_set.rs b/ampd/src/handlers/evm_verify_verifier_set.rs index 6097f85b9..5f0239424 100644 --- a/ampd/src/handlers/evm_verify_verifier_set.rs +++ b/ampd/src/handlers/evm_verify_verifier_set.rs @@ -30,8 +30,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct VerifierSetConfirmation { - pub tx_id: Hash, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub verifier_set: VerifierSet, } @@ -166,14 +165,13 @@ where } let tx_receipt = self - .finalized_tx_receipt(verifier_set.tx_id, confirmation_height) + .finalized_tx_receipt(verifier_set.message_id.tx_hash.into(), confirmation_height) .await?; let vote = info_span!( "verify a new verifier set for an EVM chain", poll_id = poll_id.to_string(), source_chain = source_chain.to_string(), - id = HexTxHashAndEventIndex::new(verifier_set.tx_id, verifier_set.event_index) - .to_string() + id = verifier_set.message_id.to_string() ) .in_scope(|| { info!("ready to verify a new verifier set in poll"); @@ -201,6 +199,7 @@ mod tests { use std::convert::TryInto; use std::str::FromStr; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use base64::engine::general_purpose::STANDARD; use base64::Engine; use error_stack::{Report, Result}; @@ -271,10 +270,12 @@ mod tests { } fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_id = HexTxHashAndEventIndex::new(Hash::random(), 100u64); PollStarted::VerifierSet { verifier_set: VerifierSetConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 100, + tx_id: msg_id.tx_hash_as_hex(), + event_index: msg_id.event_index as u32, + message_id: msg_id.to_string().parse().unwrap(), verifier_set: build_verifier_set(KeyType::Ecdsa, &ecdsa_test_data::signers()), }, metadata: PollMetadata { diff --git a/ampd/src/handlers/mvx_verify_msg.rs b/ampd/src/handlers/mvx_verify_msg.rs index a836ef40e..37aaeefcb 100644 --- a/ampd/src/handlers/mvx_verify_msg.rs +++ b/ampd/src/handlers/mvx_verify_msg.rs @@ -321,6 +321,12 @@ mod tests { .parse() .unwrap(), event_index: 1, + message_id: format!( + "0xdfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312-1" + ) + .to_string() + .parse() + .unwrap(), source_address: "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7" .parse() .unwrap(), diff --git a/ampd/src/handlers/mvx_verify_verifier_set.rs b/ampd/src/handlers/mvx_verify_verifier_set.rs index 1953ee000..ce0feebfb 100644 --- a/ampd/src/handlers/mvx_verify_verifier_set.rs +++ b/ampd/src/handlers/mvx_verify_verifier_set.rs @@ -1,362 +1,366 @@ -use std::convert::TryInto; - -use async_trait::async_trait; -use axelar_wasm_std::voting::{PollId, Vote}; -use cosmrs::cosmwasm::MsgExecuteContract; -use cosmrs::tx::Msg; -use cosmrs::Any; -use error_stack::ResultExt; -use events::Error::EventTypeMismatch; -use events::Event; -use events_derive::try_from; -use multisig::verifier_set::VerifierSet; -use multiversx_sdk::data::address::Address; -use serde::Deserialize; -use tokio::sync::watch::Receiver; -use tracing::{info, info_span}; -use valuable::Valuable; -use voting_verifier::msg::ExecuteMsg; - -use crate::event_processor::EventHandler; -use crate::handlers::errors::Error; -use crate::mvx::proxy::MvxProxy; -use crate::mvx::verifier::verify_verifier_set; -use crate::types::{Hash, TMAddress}; - -#[derive(Deserialize, Debug)] -pub struct VerifierSetConfirmation { - pub tx_id: Hash, - pub event_index: u32, - pub verifier_set: VerifierSet, -} - -#[derive(Deserialize, Debug)] -#[try_from("wasm-verifier_set_poll_started")] -struct PollStartedEvent { - poll_id: PollId, - source_gateway_address: Address, - verifier_set: VerifierSetConfirmation, - participants: Vec, - expires_at: u64, -} - -pub struct Handler

-where - P: MvxProxy + Send + Sync, -{ - verifier: TMAddress, - voting_verifier_contract: TMAddress, - blockchain: P, - latest_block_height: Receiver, -} - -impl

Handler

-where - P: MvxProxy + Send + Sync, -{ - pub fn new( - verifier: TMAddress, - voting_verifier_contract: TMAddress, - blockchain: P, - latest_block_height: Receiver, - ) -> Self { - Self { - verifier, - voting_verifier_contract, - blockchain, - latest_block_height, - } - } - - fn vote_msg(&self, poll_id: PollId, vote: Vote) -> MsgExecuteContract { - MsgExecuteContract { - sender: self.verifier.as_ref().clone(), - contract: self.voting_verifier_contract.as_ref().clone(), - msg: serde_json::to_vec(&ExecuteMsg::Vote { - poll_id, - votes: vec![vote], - }) - .expect("vote msg should serialize"), - funds: vec![], - } - } -} - -#[async_trait] -impl

EventHandler for Handler

-where - P: MvxProxy + Send + Sync, -{ - type Err = Error; - - async fn handle(&self, event: &Event) -> error_stack::Result, Error> { - if !event.is_from_contract(self.voting_verifier_contract.as_ref()) { - return Ok(vec![]); - } - - let PollStartedEvent { - poll_id, - source_gateway_address, - verifier_set, - participants, - expires_at, - .. - } = match event.try_into() as error_stack::Result<_, _> { - Err(report) if matches!(report.current_context(), EventTypeMismatch(_)) => { - return Ok(vec![]); - } - event => event.change_context(Error::DeserializeEvent)?, - }; - - if !participants.contains(&self.verifier) { - return Ok(vec![]); - } - - let latest_block_height = *self.latest_block_height.borrow(); - if latest_block_height >= expires_at { - info!(poll_id = poll_id.to_string(), "skipping expired poll"); - return Ok(vec![]); - } - - let transaction_info = self - .blockchain - .transaction_info_with_results(&verifier_set.tx_id) - .await; - - let vote = info_span!( - "verify a new verifier set for MultiversX", - poll_id = poll_id.to_string(), - id = format!("{}_{}", verifier_set.tx_id, verifier_set.event_index) - ) - .in_scope(|| { - info!("ready to verify a new worker set in poll"); - - let vote = transaction_info.map_or(Vote::NotFound, |transaction| { - verify_verifier_set(&source_gateway_address, &transaction, verifier_set) - }); - info!( - vote = vote.as_value(), - "ready to vote for a new worker set in poll" - ); - - vote - }); - - Ok(vec![self - .vote_msg(poll_id, vote) - .into_any() - .expect("vote msg should serialize")]) - } -} - -#[cfg(test)] -mod tests { - use std::convert::TryInto; - - use cosmrs::cosmwasm::MsgExecuteContract; - use cosmrs::tx::Msg; - use cosmwasm_std; - use cosmwasm_std::{HexBinary, Uint128}; - use error_stack::Result; - use events::Event; - use hex::ToHex; - use multisig::key::KeyType; - use multisig::test::common::{build_verifier_set, ed25519_test_data}; - use tokio::sync::watch; - use tokio::test as async_test; - use voting_verifier::events::{PollMetadata, PollStarted, VerifierSetConfirmation}; - - use super::PollStartedEvent; - use crate::event_processor::EventHandler; - use crate::handlers::tests::into_structured_event; - use crate::mvx::proxy::MockMvxProxy; - use crate::types::TMAddress; - use crate::PREFIX; - - #[test] - fn should_deserialize_verifier_set_poll_started_event() { - let event: Result = into_structured_event( - verifier_set_poll_started_event(participants(5, None), 100), - &TMAddress::random(PREFIX), - ) - .try_into(); - - assert!(event.is_ok()); - - let event = event.unwrap(); - - assert!(event.poll_id == 100u64.into()); - assert!( - event.source_gateway_address.to_bech32_string().unwrap() - == "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" - ); - - let verifier_set = event.verifier_set; - - assert!( - verifier_set.tx_id.encode_hex::() - == "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" - ); - assert!(verifier_set.event_index == 1u32); - assert!(verifier_set.verifier_set.signers.len() == 3); - assert_eq!(verifier_set.verifier_set.threshold, Uint128::from(2u128)); - - let mut signers = verifier_set.verifier_set.signers.values(); - let signer1 = signers.next().unwrap(); - let signer2 = signers.next().unwrap(); - - assert_eq!(signer1.pub_key.as_ref(), HexBinary::from_hex( - "45e67eaf446e6c26eb3a2b55b64339ecf3a4d1d03180bee20eb5afdd23fa644f", - ) - .unwrap().as_ref()); - assert_eq!(signer1.weight, Uint128::from(1u128)); - - assert_eq!(signer2.pub_key.as_ref(), HexBinary::from_hex( - "dd9822c7fa239dda9913ebee813ecbe69e35d88ff651548d5cc42c033a8a667b", - ) - .unwrap().as_ref()); - assert_eq!(signer2.weight, Uint128::from(1u128)); - } - - #[async_test] - async fn not_poll_started_event() { - let event = into_structured_event( - cosmwasm_std::Event::new("transfer"), - &TMAddress::random(PREFIX), - ); - - let handler = super::Handler::new( - TMAddress::random(PREFIX), - TMAddress::random(PREFIX), - MockMvxProxy::new(), - watch::channel(0).1, - ); - - assert_eq!(handler.handle(&event).await.unwrap(), vec![]); - } - - #[async_test] - async fn contract_is_not_voting_verifier() { - let event = into_structured_event( - verifier_set_poll_started_event(participants(5, None), 100), - &TMAddress::random(PREFIX), - ); - - let handler = super::Handler::new( - TMAddress::random(PREFIX), - TMAddress::random(PREFIX), - MockMvxProxy::new(), - watch::channel(0).1, - ); - - assert_eq!(handler.handle(&event).await.unwrap(), vec![]); - } - - #[async_test] - async fn verifier_is_not_a_participant() { - let voting_verifier = TMAddress::random(PREFIX); - let event = into_structured_event( - verifier_set_poll_started_event(participants(5, None), 100), - &voting_verifier, - ); - - let handler = super::Handler::new( - TMAddress::random(PREFIX), - voting_verifier, - MockMvxProxy::new(), - watch::channel(0).1, - ); - - assert_eq!(handler.handle(&event).await.unwrap(), vec![]); - } - - #[async_test] - async fn should_skip_expired_poll() { - let mut proxy = MockMvxProxy::new(); - proxy - .expect_transaction_info_with_results() - .returning(|_| None); - - let voting_verifier = TMAddress::random(PREFIX); - let verifier = TMAddress::random(PREFIX); - let expiration = 100u64; - let event: Event = into_structured_event( - verifier_set_poll_started_event( - vec![verifier.clone()].into_iter().collect(), - expiration, - ), - &voting_verifier, - ); - - let (tx, rx) = watch::channel(expiration - 1); - - let handler = super::Handler::new(verifier, voting_verifier, proxy, rx); - - // poll is not expired yet, should hit proxy - let actual = handler.handle(&event).await.unwrap(); - assert_eq!(actual.len(), 1); - - let _ = tx.send(expiration + 1); - - // poll is expired - assert_eq!(handler.handle(&event).await.unwrap(), vec![]); - } - - #[async_test] - async fn should_vote_correctly() { - let mut proxy = MockMvxProxy::new(); - proxy - .expect_transaction_info_with_results() - .returning(|_| None); - - let voting_verifier = TMAddress::random(PREFIX); - let worker = TMAddress::random(PREFIX); - - let event = into_structured_event( - verifier_set_poll_started_event(participants(5, Some(worker.clone())), 100), - &voting_verifier, - ); - - let handler = super::Handler::new(worker, voting_verifier, proxy, watch::channel(0).1); - - let actual = handler.handle(&event).await.unwrap(); - assert_eq!(actual.len(), 1); - assert!(MsgExecuteContract::from_any(actual.first().unwrap()).is_ok()); - } - - fn verifier_set_poll_started_event( - participants: Vec, - expires_at: u64, - ) -> PollStarted { - PollStarted::VerifierSet { - metadata: PollMetadata { - poll_id: "100".parse().unwrap(), - source_chain: "multiversx".parse().unwrap(), - source_gateway_address: - "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" - .parse() - .unwrap(), - confirmation_height: 15, - expires_at, - participants: participants - .into_iter() - .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) - .collect(), - }, - verifier_set: VerifierSetConfirmation { - tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" - .parse() - .unwrap(), - event_index: 1, - verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), - }, - } - } - - fn participants(n: u8, worker: Option) -> Vec { - (0..n) - .map(|_| TMAddress::random(PREFIX)) - .chain(worker) - .collect() - } -} +use std::convert::TryInto; + +use async_trait::async_trait; +use axelar_wasm_std::voting::{PollId, Vote}; +use cosmrs::cosmwasm::MsgExecuteContract; +use cosmrs::tx::Msg; +use cosmrs::Any; +use error_stack::ResultExt; +use events::Error::EventTypeMismatch; +use events::Event; +use events_derive::try_from; +use multisig::verifier_set::VerifierSet; +use multiversx_sdk::data::address::Address; +use serde::Deserialize; +use tokio::sync::watch::Receiver; +use tracing::{info, info_span}; +use valuable::Valuable; +use voting_verifier::msg::ExecuteMsg; + +use crate::event_processor::EventHandler; +use crate::handlers::errors::Error; +use crate::mvx::proxy::MvxProxy; +use crate::mvx::verifier::verify_verifier_set; +use crate::types::{Hash, TMAddress}; + +#[derive(Deserialize, Debug)] +pub struct VerifierSetConfirmation { + pub tx_id: Hash, + pub event_index: u32, + pub verifier_set: VerifierSet, +} + +#[derive(Deserialize, Debug)] +#[try_from("wasm-verifier_set_poll_started")] +struct PollStartedEvent { + poll_id: PollId, + source_gateway_address: Address, + verifier_set: VerifierSetConfirmation, + participants: Vec, + expires_at: u64, +} + +pub struct Handler

+where + P: MvxProxy + Send + Sync, +{ + verifier: TMAddress, + voting_verifier_contract: TMAddress, + blockchain: P, + latest_block_height: Receiver, +} + +impl

Handler

+where + P: MvxProxy + Send + Sync, +{ + pub fn new( + verifier: TMAddress, + voting_verifier_contract: TMAddress, + blockchain: P, + latest_block_height: Receiver, + ) -> Self { + Self { + verifier, + voting_verifier_contract, + blockchain, + latest_block_height, + } + } + + fn vote_msg(&self, poll_id: PollId, vote: Vote) -> MsgExecuteContract { + MsgExecuteContract { + sender: self.verifier.as_ref().clone(), + contract: self.voting_verifier_contract.as_ref().clone(), + msg: serde_json::to_vec(&ExecuteMsg::Vote { + poll_id, + votes: vec![vote], + }) + .expect("vote msg should serialize"), + funds: vec![], + } + } +} + +#[async_trait] +impl

EventHandler for Handler

+where + P: MvxProxy + Send + Sync, +{ + type Err = Error; + + async fn handle(&self, event: &Event) -> error_stack::Result, Error> { + if !event.is_from_contract(self.voting_verifier_contract.as_ref()) { + return Ok(vec![]); + } + + let PollStartedEvent { + poll_id, + source_gateway_address, + verifier_set, + participants, + expires_at, + .. + } = match event.try_into() as error_stack::Result<_, _> { + Err(report) if matches!(report.current_context(), EventTypeMismatch(_)) => { + return Ok(vec![]); + } + event => event.change_context(Error::DeserializeEvent)?, + }; + + if !participants.contains(&self.verifier) { + return Ok(vec![]); + } + + let latest_block_height = *self.latest_block_height.borrow(); + if latest_block_height >= expires_at { + info!(poll_id = poll_id.to_string(), "skipping expired poll"); + return Ok(vec![]); + } + + let transaction_info = self + .blockchain + .transaction_info_with_results(&verifier_set.tx_id) + .await; + + let vote = info_span!( + "verify a new verifier set for MultiversX", + poll_id = poll_id.to_string(), + id = format!("{}_{}", verifier_set.tx_id, verifier_set.event_index) + ) + .in_scope(|| { + info!("ready to verify a new worker set in poll"); + + let vote = transaction_info.map_or(Vote::NotFound, |transaction| { + verify_verifier_set(&source_gateway_address, &transaction, verifier_set) + }); + info!( + vote = vote.as_value(), + "ready to vote for a new worker set in poll" + ); + + vote + }); + + Ok(vec![self + .vote_msg(poll_id, vote) + .into_any() + .expect("vote msg should serialize")]) + } +} + +#[cfg(test)] +mod tests { + use std::convert::TryInto; + + use cosmrs::cosmwasm::MsgExecuteContract; + use cosmrs::tx::Msg; + use cosmwasm_std; + use cosmwasm_std::{HexBinary, Uint128}; + use error_stack::Result; + use events::Event; + use hex::ToHex; + use multisig::key::KeyType; + use multisig::test::common::{build_verifier_set, ed25519_test_data}; + use tokio::sync::watch; + use tokio::test as async_test; + use voting_verifier::events::{PollMetadata, PollStarted, VerifierSetConfirmation}; + + use super::PollStartedEvent; + use crate::event_processor::EventHandler; + use crate::handlers::tests::into_structured_event; + use crate::mvx::proxy::MockMvxProxy; + use crate::types::TMAddress; + use crate::PREFIX; + + #[test] + fn should_deserialize_verifier_set_poll_started_event() { + let event: Result = into_structured_event( + verifier_set_poll_started_event(participants(5, None), 100), + &TMAddress::random(PREFIX), + ) + .try_into(); + + assert!(event.is_ok()); + + let event = event.unwrap(); + + assert!(event.poll_id == 100u64.into()); + assert!( + event.source_gateway_address.to_bech32_string().unwrap() + == "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" + ); + + let verifier_set = event.verifier_set; + + assert!( + verifier_set.tx_id.encode_hex::() + == "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" + ); + assert!(verifier_set.event_index == 1u32); + assert!(verifier_set.verifier_set.signers.len() == 3); + assert_eq!(verifier_set.verifier_set.threshold, Uint128::from(2u128)); + + let mut signers = verifier_set.verifier_set.signers.values(); + let signer1 = signers.next().unwrap(); + let signer2 = signers.next().unwrap(); + + assert_eq!(signer1.pub_key.as_ref(), HexBinary::from_hex( + "45e67eaf446e6c26eb3a2b55b64339ecf3a4d1d03180bee20eb5afdd23fa644f", + ) + .unwrap().as_ref()); + assert_eq!(signer1.weight, Uint128::from(1u128)); + + assert_eq!(signer2.pub_key.as_ref(), HexBinary::from_hex( + "dd9822c7fa239dda9913ebee813ecbe69e35d88ff651548d5cc42c033a8a667b", + ) + .unwrap().as_ref()); + assert_eq!(signer2.weight, Uint128::from(1u128)); + } + + #[async_test] + async fn not_poll_started_event() { + let event = into_structured_event( + cosmwasm_std::Event::new("transfer"), + &TMAddress::random(PREFIX), + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + TMAddress::random(PREFIX), + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn contract_is_not_voting_verifier() { + let event = into_structured_event( + verifier_set_poll_started_event(participants(5, None), 100), + &TMAddress::random(PREFIX), + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + TMAddress::random(PREFIX), + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn verifier_is_not_a_participant() { + let voting_verifier = TMAddress::random(PREFIX); + let event = into_structured_event( + verifier_set_poll_started_event(participants(5, None), 100), + &voting_verifier, + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + voting_verifier, + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn should_skip_expired_poll() { + let mut proxy = MockMvxProxy::new(); + proxy + .expect_transaction_info_with_results() + .returning(|_| None); + + let voting_verifier = TMAddress::random(PREFIX); + let verifier = TMAddress::random(PREFIX); + let expiration = 100u64; + let event: Event = into_structured_event( + verifier_set_poll_started_event( + vec![verifier.clone()].into_iter().collect(), + expiration, + ), + &voting_verifier, + ); + + let (tx, rx) = watch::channel(expiration - 1); + + let handler = super::Handler::new(verifier, voting_verifier, proxy, rx); + + // poll is not expired yet, should hit proxy + let actual = handler.handle(&event).await.unwrap(); + assert_eq!(actual.len(), 1); + + let _ = tx.send(expiration + 1); + + // poll is expired + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn should_vote_correctly() { + let mut proxy = MockMvxProxy::new(); + proxy + .expect_transaction_info_with_results() + .returning(|_| None); + + let voting_verifier = TMAddress::random(PREFIX); + let worker = TMAddress::random(PREFIX); + + let event = into_structured_event( + verifier_set_poll_started_event(participants(5, Some(worker.clone())), 100), + &voting_verifier, + ); + + let handler = super::Handler::new(worker, voting_verifier, proxy, watch::channel(0).1); + + let actual = handler.handle(&event).await.unwrap(); + assert_eq!(actual.len(), 1); + assert!(MsgExecuteContract::from_any(actual.first().unwrap()).is_ok()); + } + + fn verifier_set_poll_started_event( + participants: Vec, + expires_at: u64, + ) -> PollStarted { + PollStarted::VerifierSet { + metadata: PollMetadata { + poll_id: "100".parse().unwrap(), + source_chain: "multiversx".parse().unwrap(), + source_gateway_address: + "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" + .parse() + .unwrap(), + confirmation_height: 15, + expires_at, + participants: participants + .into_iter() + .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) + .collect(), + }, + verifier_set: VerifierSetConfirmation { + tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" + .parse() + .unwrap(), + event_index: 1, + message_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312-1" + .to_string() + .parse() + .unwrap(), + verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), + }, + } + } + + fn participants(n: u8, worker: Option) -> Vec { + (0..n) + .map(|_| TMAddress::random(PREFIX)) + .chain(worker) + .collect() + } +} diff --git a/ampd/src/handlers/stellar_verify_msg.rs b/ampd/src/handlers/stellar_verify_msg.rs index 7de378a12..b0d9d5b7e 100644 --- a/ampd/src/handlers/stellar_verify_msg.rs +++ b/ampd/src/handlers/stellar_verify_msg.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -11,8 +12,7 @@ use events::Event; use events_derive::try_from; use prost_types::Any; use router_api::ChainName; -use serde::de::Error as DeserializeError; -use serde::{Deserialize, Deserializer}; +use serde::Deserialize; use serde_with::{serde_as, DisplayFromStr}; use stellar_xdr::curr::{ScAddress, ScBytes, ScString}; use tokio::sync::watch::Receiver; @@ -27,24 +27,10 @@ use crate::stellar::http_client::Client; use crate::stellar::verifier::verify_message; use crate::types::TMAddress; -pub fn deserialize_tx_id<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let tx_id = String::deserialize(deserializer)?; - - tx_id - .strip_prefix("0x") - .map(String::from) - .ok_or(D::Error::custom(Error::DeserializeEvent)) -} - #[serde_as] #[derive(Deserialize, Debug, Clone)] pub struct Message { - #[serde(deserialize_with = "deserialize_tx_id")] - pub tx_id: String, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub destination_address: ScString, pub destination_chain: ScString, #[serde_as(as = "DisplayFromStr")] @@ -132,7 +118,7 @@ impl EventHandler for Handler { let tx_hashes: HashSet<_> = messages .iter() - .map(|message| message.tx_id.clone()) + .map(|message| message.message_id.tx_hash_as_hex_no_prefix().to_string()) .collect(); let transaction_responses = self @@ -143,7 +129,7 @@ impl EventHandler for Handler { let message_ids = messages .iter() - .map(|message| format!("{}-{}", message.tx_id.clone(), message.event_index)) + .map(|message| message.message_id.to_string()) .collect::>(); let votes = info_span!( @@ -159,7 +145,7 @@ impl EventHandler for Handler { .iter() .map(|msg| { transaction_responses - .get(&msg.tx_id) + .get(&msg.message_id.tx_hash_as_hex_no_prefix().to_string()) .map_or(Vote::NotFound, |tx_response| { verify_message(&source_gateway_address, tx_response, msg) }) @@ -185,6 +171,7 @@ mod tests { use std::collections::HashMap; use std::convert::TryInto; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; use error_stack::Result; @@ -329,18 +316,24 @@ mod tests { .collect(), }, messages: (0..2) - .map(|i| TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: i, - source_address: ScAddress::Contract(stellar_xdr::curr::Hash::from( - Hash::random().0, - )) - .to_string() - .try_into() - .unwrap(), - destination_chain: "ethereum".parse().unwrap(), - destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), - payload_hash: Hash::random().to_fixed_bytes(), + .map(|i| { + let msg_id = HexTxHashAndEventIndex::new(Hash::random(), i as u64); + TxEventConfirmation { + tx_id: msg_id.tx_hash_as_hex(), + event_index: msg_id.event_index as u32, + message_id: msg_id.to_string().parse().unwrap(), + source_address: ScAddress::Contract(stellar_xdr::curr::Hash::from( + Hash::random().0, + )) + .to_string() + .try_into() + .unwrap(), + destination_chain: "ethereum".parse().unwrap(), + destination_address: format!("0x{:x}", EVMAddress::random()) + .parse() + .unwrap(), + payload_hash: Hash::random().to_fixed_bytes(), + } }) .collect::>(), } diff --git a/ampd/src/handlers/stellar_verify_verifier_set.rs b/ampd/src/handlers/stellar_verify_verifier_set.rs index a7eb57f57..b1771bc02 100644 --- a/ampd/src/handlers/stellar_verify_verifier_set.rs +++ b/ampd/src/handlers/stellar_verify_verifier_set.rs @@ -1,6 +1,7 @@ use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -18,7 +19,6 @@ use tracing::{info, info_span}; use valuable::Valuable; use voting_verifier::msg::ExecuteMsg; -use super::stellar_verify_msg::deserialize_tx_id; use crate::event_processor::EventHandler; use crate::handlers::errors::Error; use crate::handlers::errors::Error::DeserializeEvent; @@ -28,9 +28,7 @@ use crate::types::TMAddress; #[derive(Deserialize, Debug)] pub struct VerifierSetConfirmation { - #[serde(deserialize_with = "deserialize_tx_id")] - pub tx_id: String, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub verifier_set: VerifierSet, } @@ -112,14 +110,19 @@ impl EventHandler for Handler { let transaction_response = self .http_client - .transaction_response(verifier_set.tx_id.clone()) + .transaction_response( + verifier_set + .message_id + .tx_hash_as_hex_no_prefix() + .to_string(), + ) .await .change_context(Error::TxReceipts)?; let vote = info_span!( "verify a new verifier set", poll_id = poll_id.to_string(), - id = format!("0x{}-{}", verifier_set.tx_id, verifier_set.event_index), + id = verifier_set.message_id.to_string(), ) .in_scope(|| { info!("ready to verify verifier set in poll",); @@ -147,6 +150,7 @@ impl EventHandler for Handler { mod tests { use std::convert::TryInto; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; use error_stack::Result; @@ -275,6 +279,7 @@ mod tests { } fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_id = HexTxHashAndEventIndex::new(Hash::random(), 0u64); PollStarted::VerifierSet { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -293,8 +298,9 @@ mod tests { .collect(), }, verifier_set: VerifierSetConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_hash_as_hex(), + event_index: msg_id.event_index as u32, + message_id: msg_id.to_string().parse().unwrap(), verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), }, } diff --git a/ampd/src/handlers/sui_verify_msg.rs b/ampd/src/handlers/sui_verify_msg.rs index 3d60cb2b0..b18e8af7e 100644 --- a/ampd/src/handlers/sui_verify_msg.rs +++ b/ampd/src/handlers/sui_verify_msg.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -11,7 +12,7 @@ use events::Error::EventTypeMismatch; use events::Event; use events_derive::try_from; use serde::Deserialize; -use sui_types::base_types::{SuiAddress, TransactionDigest}; +use sui_types::base_types::SuiAddress; use tokio::sync::watch::Receiver; use tracing::info; use voting_verifier::msg::ExecuteMsg; @@ -26,8 +27,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct Message { - pub tx_id: TransactionDigest, - pub event_index: u32, + pub message_id: Base58TxDigestAndEventIndex, pub destination_address: String, pub destination_chain: router_api::ChainName, pub source_address: SuiAddress, @@ -122,7 +122,10 @@ where // Does not assume voting verifier emits unique tx ids. // RPC will throw an error if the input contains any duplicate, deduplicate tx ids to avoid unnecessary failures. - let deduplicated_tx_ids: HashSet<_> = messages.iter().map(|msg| msg.tx_id).collect(); + let deduplicated_tx_ids: HashSet<_> = messages + .iter() + .map(|msg| msg.message_id.tx_digest.into()) + .collect(); let transaction_blocks = self .rpc_client .finalized_transaction_blocks(deduplicated_tx_ids) @@ -133,7 +136,7 @@ where .iter() .map(|msg| { transaction_blocks - .get(&msg.tx_id) + .get(&msg.message_id.tx_digest.into()) .map_or(Vote::NotFound, |tx_block| { verify_message(&source_gateway_address, tx_block, msg) }) @@ -152,6 +155,7 @@ mod tests { use std::collections::HashMap; use std::convert::TryInto; + use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; use cosmwasm_std; @@ -322,6 +326,7 @@ mod tests { } fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_id = Base58TxDigestAndEventIndex::new(TransactionDigest::random(), 0u64); PollStarted::Messages { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -338,8 +343,9 @@ mod tests { .collect(), }, messages: vec![TxEventConfirmation { - tx_id: TransactionDigest::random().to_string().parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_digest_as_base58(), + event_index: msg_id.event_index as u32, + message_id: msg_id.to_string().parse().unwrap(), source_address: SuiAddress::random_for_testing_only() .to_string() .parse() diff --git a/ampd/src/handlers/sui_verify_verifier_set.rs b/ampd/src/handlers/sui_verify_verifier_set.rs index 4a159c916..30ffdc073 100644 --- a/ampd/src/handlers/sui_verify_verifier_set.rs +++ b/ampd/src/handlers/sui_verify_verifier_set.rs @@ -12,7 +12,7 @@ use events::Event; use events_derive::try_from; use multisig::verifier_set::VerifierSet; use serde::Deserialize; -use sui_types::base_types::{SuiAddress, TransactionDigest}; +use sui_types::base_types::SuiAddress; use tokio::sync::watch::Receiver; use tracing::{info, info_span}; use valuable::Valuable; @@ -26,8 +26,7 @@ use crate::types::TMAddress; #[derive(Deserialize, Debug)] pub struct VerifierSetConfirmation { - pub tx_id: TransactionDigest, - pub event_index: u32, + pub message_id: Base58TxDigestAndEventIndex, pub verifier_set: VerifierSet, } @@ -121,15 +120,14 @@ where let transaction_block = self .rpc_client - .finalized_transaction_block(verifier_set.tx_id) + .finalized_transaction_block(verifier_set.message_id.tx_digest.into()) .await .change_context(Error::TxReceipts)?; let vote = info_span!( "verify a new verifier set for Sui", poll_id = poll_id.to_string(), - id = Base58TxDigestAndEventIndex::new(verifier_set.tx_id, verifier_set.event_index) - .to_string() + id = verifier_set.message_id.to_string() ) .in_scope(|| { let vote = transaction_block.map_or(Vote::NotFound, |tx_receipt| { @@ -155,6 +153,7 @@ where mod tests { use std::convert::TryInto; + use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use error_stack::{Report, Result}; use ethers_providers::ProviderError; use events::Event; @@ -225,6 +224,7 @@ mod tests { participants: Vec, expires_at: u64, ) -> PollStarted { + let msg_id = Base58TxDigestAndEventIndex::new(TransactionDigest::random(), 0u64); PollStarted::VerifierSet { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -241,8 +241,9 @@ mod tests { .collect(), }, verifier_set: VerifierSetConfirmation { - tx_id: TransactionDigest::random().to_string().parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_digest_as_base58(), + event_index: msg_id.event_index as u32, + message_id: msg_id.to_string().parse().unwrap(), verifier_set: build_verifier_set(KeyType::Ecdsa, &ecdsa_test_data::signers()), }, } diff --git a/ampd/src/stellar/http_client.rs b/ampd/src/stellar/http_client.rs index 469d8774a..568b0304e 100644 --- a/ampd/src/stellar/http_client.rs +++ b/ampd/src/stellar/http_client.rs @@ -3,7 +3,6 @@ use std::str::FromStr; use error_stack::{report, Result}; use futures::future::join_all; -use num_traits::cast; use stellar_rs::horizon_client::HorizonClient; use stellar_rs::transactions::prelude::{SingleTransactionRequest, TransactionResponse}; use stellar_xdr::curr::{ContractEvent, Limits, ReadXdr, ScAddress, TransactionMeta, VecM}; @@ -53,10 +52,10 @@ impl TxResponse { !self.successful } - pub fn event(&self, index: u32) -> Option<&ContractEvent> { + pub fn event(&self, index: u64) -> Option<&ContractEvent> { match self.contract_events { Some(ref events) => { - let log_index: usize = cast(index).expect("event index must be a valid usize"); + let log_index = usize::try_from(index).ok()?; events.get(log_index) } None => None, diff --git a/ampd/src/stellar/verifier.rs b/ampd/src/stellar/verifier.rs index c59933f8c..f767a6b10 100644 --- a/ampd/src/stellar/verifier.rs +++ b/ampd/src/stellar/verifier.rs @@ -71,8 +71,8 @@ pub fn verify_message(gateway_address: &ScAddress, tx_receipt: &TxResponse, msg: gateway_address, tx_receipt, msg, - msg.tx_id.clone(), - msg.event_index, + msg.message_id.tx_hash_as_hex_no_prefix().to_string(), + msg.message_id.event_index, ) } @@ -85,8 +85,11 @@ pub fn verify_verifier_set( gateway_address, tx_receipt, verifier_set_confirmation, - verifier_set_confirmation.tx_id.clone(), - verifier_set_confirmation.event_index, + verifier_set_confirmation + .message_id + .tx_hash_as_hex_no_prefix() + .to_string(), + verifier_set_confirmation.message_id.event_index, ) } @@ -95,7 +98,7 @@ fn verify<'a>( tx_receipt: &'a TxResponse, to_verify: impl PartialEq<&'a ContractEventBody>, expected_tx_id: String, - expected_event_index: u32, + expected_event_index: u64, ) -> Vote { if expected_tx_id != tx_receipt.transaction_hash { return Vote::NotFound; @@ -123,6 +126,7 @@ fn verify<'a>( mod test { use std::str::FromStr; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::Vote; use cosmrs::tx::MessageExt; use cosmwasm_std::{Addr, HexBinary, Uint128}; @@ -149,7 +153,7 @@ mod test { #[test] fn should_not_verify_msg_if_tx_id_does_not_match() { let (gateway_address, tx_response, mut msg) = matching_msg_and_tx_block(); - msg.tx_id = "different_tx_hash".to_string(); + msg.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_message(&gateway_address, &tx_response, &msg), @@ -160,7 +164,7 @@ mod test { #[test] fn should_not_verify_msg_if_event_index_does_not_match() { let (gateway_address, tx_response, mut msg) = matching_msg_and_tx_block(); - msg.event_index = 1; + msg.message_id.event_index = 1; assert_eq!( verify_message(&gateway_address, &tx_response, &msg), @@ -233,7 +237,7 @@ mod test { #[test] fn should_not_verify_verifier_set_if_tx_id_does_not_match() { let (gateway_address, tx_response, mut confirmation) = matching_verifier_set_and_tx_block(); - confirmation.tx_id = "different_tx_hash".to_string(); + confirmation.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_verifier_set(&gateway_address, &tx_response, &confirmation), @@ -244,7 +248,7 @@ mod test { #[test] fn should_not_verify_verifier_set_if_event_index_does_not_match() { let (gateway_address, tx_response, mut confirmation) = matching_verifier_set_and_tx_block(); - confirmation.event_index = 1; + confirmation.message_id.event_index = 1; assert_eq!( verify_verifier_set(&gateway_address, &tx_response, &confirmation), @@ -289,8 +293,7 @@ mod test { let signing_key = SigningKey::generate(&mut OsRng); let msg = Message { - tx_id: format!("{:x}", Hash::random()), - event_index: 0, + message_id: HexTxHashAndEventIndex::new(Hash::random(), 0u64), source_address: ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519( Uint256::from(signing_key.verifying_key().to_bytes()), ))), @@ -323,7 +326,7 @@ mod test { }; let tx_response = TxResponse { - transaction_hash: msg.tx_id.clone(), + transaction_hash: msg.message_id.tx_hash_as_hex_no_prefix().to_string(), source_address: msg.source_address.clone(), successful: true, contract_events: Some(vec![event].try_into().unwrap()), @@ -341,8 +344,7 @@ mod test { let threshold = Uint128::new(2u128); let verifier_set_confirmation = VerifierSetConfirmation { - tx_id: format!("{:x}", Hash::random()), - event_index: 0, + message_id: HexTxHashAndEventIndex::new(Hash::random(), 0u64), verifier_set: VerifierSet { signers: signers .iter() @@ -380,7 +382,10 @@ mod test { }; let tx_response = TxResponse { - transaction_hash: verifier_set_confirmation.tx_id.clone(), + transaction_hash: verifier_set_confirmation + .message_id + .tx_hash_as_hex_no_prefix() + .to_string(), source_address: ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519( Uint256::from(SigningKey::generate(&mut OsRng).verifying_key().to_bytes()), ))), diff --git a/ampd/src/sui/verifier.rs b/ampd/src/sui/verifier.rs index b16dba471..9b772cc4c 100644 --- a/ampd/src/sui/verifier.rs +++ b/ampd/src/sui/verifier.rs @@ -107,9 +107,9 @@ pub fn verify_message( transaction_block: &SuiTransactionBlockResponse, message: &Message, ) -> Vote { - match find_event(transaction_block, message.event_index as u64) { + match find_event(transaction_block, message.message_id.event_index) { Some(event) - if transaction_block.digest == message.tx_id + if transaction_block.digest == message.message_id.tx_digest.into() && event.type_ == EventType::ContractCall.struct_tag(gateway_address) && event == message => { @@ -124,9 +124,9 @@ pub fn verify_verifier_set( transaction_block: &SuiTransactionBlockResponse, confirmation: &VerifierSetConfirmation, ) -> Vote { - match find_event(transaction_block, confirmation.event_index as u64) { + match find_event(transaction_block, confirmation.message_id.event_index) { Some(event) - if transaction_block.digest == confirmation.tx_id + if transaction_block.digest == confirmation.message_id.tx_digest.into() && event.type_ == EventType::SignersRotated.struct_tag(gateway_address) && event == confirmation => { @@ -138,6 +138,7 @@ pub fn verify_verifier_set( #[cfg(test)] mod tests { + use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use axelar_wasm_std::voting::Vote; use cosmrs::crypto::PublicKey; use cosmwasm_std::{Addr, HexBinary, Uint128}; @@ -166,7 +167,7 @@ mod tests { fn should_not_verify_msg_if_tx_id_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); - msg.tx_id = TransactionDigest::random(); + msg.message_id.tx_digest = TransactionDigest::random().into(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -177,7 +178,7 @@ mod tests { fn should_not_verify_msg_if_event_index_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); - msg.event_index = rand::random::(); + msg.message_id.event_index = rand::random::(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -275,7 +276,7 @@ mod tests { #[test] fn should_not_verify_verifier_set_if_event_seq_mismatch() { let (gateway_address, tx_block, mut verifier_set) = matching_verifier_set_and_tx_block(); - verifier_set.event_index = rand::random(); + verifier_set.message_id.event_index = rand::random(); assert_eq!( verify_verifier_set(&gateway_address, &tx_block, &verifier_set), @@ -347,8 +348,10 @@ mod tests { let gateway_address = SuiAddress::random_for_testing_only(); let msg = Message { - tx_id: TransactionDigest::random(), - event_index: rand::random::(), + message_id: Base58TxDigestAndEventIndex::new( + TransactionDigest::random(), + rand::random::(), + ), source_address: SuiAddress::random_for_testing_only(), destination_chain: rand_chain_name(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), @@ -364,8 +367,8 @@ mod tests { }; let event = SuiEvent { id: EventID { - tx_digest: msg.tx_id, - event_seq: msg.event_index as u64, + tx_digest: msg.message_id.tx_digest.into(), + event_seq: msg.message_id.event_index, }, package_id: gateway_address.into(), transaction_module: "gateway".parse().unwrap(), @@ -382,7 +385,7 @@ mod tests { }; let tx_block = SuiTransactionBlockResponse { - digest: msg.tx_id, + digest: msg.message_id.tx_digest.into(), events: Some(SuiTransactionBlockEvents { data: vec![event] }), ..Default::default() }; @@ -415,8 +418,10 @@ mod tests { let created_at = rand::random(); let threshold = Uint128::one(); let verifier_set_confirmation = VerifierSetConfirmation { - tx_id: TransactionDigest::random(), - event_index: rand::random(), + message_id: Base58TxDigestAndEventIndex::new( + TransactionDigest::random(), + rand::random::(), + ), verifier_set: VerifierSet { signers: signers .iter() @@ -451,8 +456,8 @@ mod tests { }; let event = SuiEvent { id: EventID { - tx_digest: verifier_set_confirmation.tx_id, - event_seq: verifier_set_confirmation.event_index as u64, + tx_digest: verifier_set_confirmation.message_id.tx_digest.into(), + event_seq: verifier_set_confirmation.message_id.event_index, }, package_id: gateway_address.into(), transaction_module: "gateway".parse().unwrap(), @@ -469,7 +474,7 @@ mod tests { }; let tx_block = SuiTransactionBlockResponse { - digest: verifier_set_confirmation.tx_id, + digest: verifier_set_confirmation.message_id.tx_digest.into(), events: Some(SuiTransactionBlockEvents { data: vec![event] }), ..Default::default() }; diff --git a/contracts/router/src/contract/execute.rs b/contracts/router/src/contract/execute.rs index 08334bd78..7a2790771 100644 --- a/contracts/router/src/contract/execute.rs +++ b/contracts/router/src/contract/execute.rs @@ -286,7 +286,7 @@ mod test { let id = HexTxHashAndEventIndex { tx_hash: bytes, - event_index: random::(), + event_index: random::(), } .to_string(); diff --git a/contracts/voting-verifier/src/contract.rs b/contracts/voting-verifier/src/contract.rs index bf68113eb..099f392a9 100644 --- a/contracts/voting-verifier/src/contract.rs +++ b/contracts/voting-verifier/src/contract.rs @@ -72,7 +72,7 @@ pub fn execute( } => Ok(execute::verify_verifier_set( deps, env, - &message_id, + message_id, new_verifier_set, )?), ExecuteMsg::UpdateVotingThreshold { @@ -232,7 +232,7 @@ mod test { deps } - fn message_id(id: &str, index: u32, msg_id_format: &MessageIdFormat) -> nonempty::String { + fn message_id(id: &str, index: u64, msg_id_format: &MessageIdFormat) -> nonempty::String { match msg_id_format { MessageIdFormat::HexTxHashAndEventIndex => HexTxHashAndEventIndex { tx_hash: Keccak256::digest(id.as_bytes()).into(), @@ -266,7 +266,7 @@ mod test { } } - fn messages(len: u32, msg_id_format: &MessageIdFormat) -> Vec { + fn messages(len: u64, msg_id_format: &MessageIdFormat) -> Vec { (0..len) .map(|i| Message { cc_id: CrossChainId::new(source_chain(), message_id("id", i, msg_id_format)) @@ -477,7 +477,7 @@ mod test { let mut deps = setup(verifiers.clone(), &msg_id_format); let messages_count = 5; let messages_in_progress = 3; - let messages = messages(messages_count as u32, &msg_id_format); + let messages = messages(messages_count as u64, &msg_id_format); execute( deps.as_mut(), diff --git a/contracts/voting-verifier/src/contract/execute.rs b/contracts/voting-verifier/src/contract/execute.rs index 79766491d..fc8fe18ec 100644 --- a/contracts/voting-verifier/src/contract/execute.rs +++ b/contracts/voting-verifier/src/contract/execute.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use axelar_wasm_std::address::{validate_address, AddressFormat}; use axelar_wasm_std::utils::TryMapExt; use axelar_wasm_std::voting::{PollId, PollResults, Vote, WeightedPoll}; -use axelar_wasm_std::{snapshot, MajorityThreshold, VerificationStatus}; +use axelar_wasm_std::{nonempty, snapshot, MajorityThreshold, VerificationStatus}; use cosmwasm_std::{ to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, OverflowError, OverflowOperation, Response, Storage, WasmMsg, @@ -43,7 +43,7 @@ pub fn update_voting_threshold( pub fn verify_verifier_set( deps: DepsMut, env: Env, - message_id: &str, + message_id: nonempty::String, new_verifier_set: VerifierSet, ) -> Result { let status = verifier_set_status(deps.as_ref(), &new_verifier_set, env.block.height)?; diff --git a/contracts/voting-verifier/src/contract/query.rs b/contracts/voting-verifier/src/contract/query.rs index 56726c2f4..e5f07f971 100644 --- a/contracts/voting-verifier/src/contract/query.rs +++ b/contracts/voting-verifier/src/contract/query.rs @@ -300,7 +300,7 @@ mod tests { ) .unwrap(); - let messages = (0..poll.poll_size as u32).map(message); + let messages = (0..poll.poll_size as u64).map(message); messages.clone().enumerate().for_each(|(idx, msg)| { poll_messages() .save( @@ -321,7 +321,7 @@ mod tests { ); } - fn message(id: u32) -> Message { + fn message(id: u64) -> Message { Message { cc_id: CrossChainId::new( "source-chain", diff --git a/contracts/voting-verifier/src/events.rs b/contracts/voting-verifier/src/events.rs index 61d2cf404..db6e2160c 100644 --- a/contracts/voting-verifier/src/events.rs +++ b/contracts/voting-verifier/src/events.rs @@ -138,10 +138,12 @@ impl From for Event { pub struct VerifierSetConfirmation { pub tx_id: nonempty::String, pub event_index: u32, + pub message_id: nonempty::String, pub verifier_set: VerifierSet, } /// If parsing is successful, returns (tx_id, event_index). Otherwise returns ContractError::InvalidMessageID +#[deprecated(since = "1.1.0", note = "don't parse message id, just emit as is")] fn parse_message_id( message_id: &str, msg_id_format: &MessageIdFormat, @@ -150,19 +152,31 @@ fn parse_message_id( MessageIdFormat::Base58TxDigestAndEventIndex => { let id = Base58TxDigestAndEventIndex::from_str(message_id) .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; - Ok((id.tx_digest_as_base58(), id.event_index)) + Ok(( + id.tx_digest_as_base58(), + u32::try_from(id.event_index) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?, + )) } MessageIdFormat::HexTxHashAndEventIndex => { let id = HexTxHashAndEventIndex::from_str(message_id) .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; - Ok((id.tx_hash_as_hex(), id.event_index)) + Ok(( + id.tx_hash_as_hex(), + u32::try_from(id.event_index) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?, + )) } MessageIdFormat::Base58SolanaTxSignatureAndEventIndex => { let id = Base58SolanaTxSignatureAndEventIndex::from_str(message_id) .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; - Ok((id.signature_as_base58(), id.event_index)) + Ok(( + id.signature_as_base58(), + u32::try_from(id.event_index) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?, + )) } MessageIdFormat::HexTxHash => { let id = HexTxHash::from_str(message_id) @@ -175,15 +189,16 @@ fn parse_message_id( impl VerifierSetConfirmation { pub fn new( - message_id: &str, + message_id: nonempty::String, msg_id_format: MessageIdFormat, verifier_set: VerifierSet, ) -> Result { - let (tx_id, event_index) = parse_message_id(message_id, &msg_id_format)?; + let (tx_id, event_index) = parse_message_id(&message_id, &msg_id_format)?; Ok(Self { tx_id, event_index, + message_id, verifier_set, }) } @@ -191,8 +206,11 @@ impl VerifierSetConfirmation { #[cw_serde] pub struct TxEventConfirmation { + #[deprecated(since = "1.1.0", note = "use message_id field instead")] pub tx_id: nonempty::String, + #[deprecated(since = "1.1.0", note = "use message_id field instead")] pub event_index: u32, + pub message_id: nonempty::String, pub destination_address: Address, pub destination_chain: ChainName, pub source_address: Address, @@ -211,6 +229,7 @@ impl TryFrom<(Message, &MessageIdFormat)> for TxEventConfirmation { Ok(TxEventConfirmation { tx_id, event_index, + message_id: msg.cc_id.message_id, destination_address: msg.destination_address, destination_chain: msg.destination_chain, source_address: msg.source_address, @@ -339,7 +358,7 @@ mod test { .unwrap(); assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.event_index as u64, msg_id.event_index); compare_event_to_message(event, msg); } @@ -373,7 +392,7 @@ mod test { .unwrap(); assert_eq!(event.tx_id, msg_id.tx_digest_as_base58()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.event_index as u64, msg_id.event_index); compare_event_to_message(event, msg); } @@ -404,7 +423,7 @@ mod test { fn should_make_verifier_set_confirmation_with_hex_msg_id() { let msg_id = HexTxHashAndEventIndex { tx_hash: random_32_bytes(), - event_index: rand::random::(), + event_index: rand::random::() as u64, }; let verifier_set = VerifierSet { signers: BTreeMap::new(), @@ -412,14 +431,14 @@ mod test { created_at: 1, }; let event = VerifierSetConfirmation::new( - &msg_id.to_string(), + msg_id.to_string().parse().unwrap(), MessageIdFormat::HexTxHashAndEventIndex, verifier_set.clone(), ) .unwrap(); assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.event_index as u64, msg_id.event_index); assert_eq!(event.verifier_set, verifier_set); } @@ -427,7 +446,7 @@ mod test { fn should_make_verifier_set_confirmation_with_base58_msg_id() { let msg_id = Base58TxDigestAndEventIndex { tx_digest: random_32_bytes(), - event_index: rand::random::(), + event_index: rand::random::() as u64, }; let verifier_set = VerifierSet { signers: BTreeMap::new(), @@ -435,14 +454,14 @@ mod test { created_at: 1, }; let event = VerifierSetConfirmation::new( - &msg_id.to_string(), + msg_id.to_string().parse().unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set.clone(), ) .unwrap(); assert_eq!(event.tx_id, msg_id.tx_digest_as_base58()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.event_index as u64, msg_id.event_index); assert_eq!(event.verifier_set, verifier_set); } @@ -456,7 +475,7 @@ mod test { }; let event = VerifierSetConfirmation::new( - msg_id, + msg_id.to_string().parse().unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set, ); @@ -467,7 +486,7 @@ mod test { fn make_verifier_set_confirmation_should_fail_with_different_msg_id_format() { let msg_id = HexTxHashAndEventIndex { tx_hash: random_32_bytes(), - event_index: rand::random::(), + event_index: rand::random::(), }; let verifier_set = VerifierSet { signers: BTreeMap::new(), @@ -476,7 +495,7 @@ mod test { }; let event = VerifierSetConfirmation::new( - &msg_id.to_string(), + msg_id.to_string().parse().unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set, ); diff --git a/packages/axelar-wasm-std/Cargo.toml b/packages/axelar-wasm-std/Cargo.toml index 6ad8685eb..4a2b64757 100644 --- a/packages/axelar-wasm-std/Cargo.toml +++ b/packages/axelar-wasm-std/Cargo.toml @@ -44,6 +44,7 @@ regex = { version = "1.10.0", default-features = false, features = ["perf", "std report = { workspace = true } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } +serde_with = { version = "3.11.0", features = ["macros"]} serde_json = "1.0.89" sha3 = { workspace = true } stellar-xdr = { workspace = true } diff --git a/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs b/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs index 1c75542b2..35ae9405a 100644 --- a/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs @@ -5,14 +5,16 @@ use std::str::FromStr; use error_stack::{Report, ResultExt}; use lazy_static::lazy_static; use regex::Regex; +use serde_with::DeserializeFromStr; use super::Error; use crate::hash::Hash; use crate::nonempty; +#[derive(Debug, DeserializeFromStr)] pub struct Base58TxDigestAndEventIndex { pub tx_digest: Hash, - pub event_index: u32, + pub event_index: u64, } impl Base58TxDigestAndEventIndex { @@ -23,7 +25,7 @@ impl Base58TxDigestAndEventIndex { .expect("failed to convert tx hash to non-empty string") } - pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { + pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { Self { tx_digest: tx_id.into(), event_index: event_index.into(), @@ -96,7 +98,7 @@ mod tests { bs58::encode(random_bytes()).into_string() } - fn random_event_index() -> u32 { + fn random_event_index() -> u64 { rand::random() } @@ -285,7 +287,7 @@ mod tests { fn should_not_parse_msg_id_with_overflowing_event_index() { let event_index: u64 = u64::MAX; let tx_digest = random_tx_digest(); - let res = Base58TxDigestAndEventIndex::from_str(&format!("{}-{}", tx_digest, event_index)); + let res = Base58TxDigestAndEventIndex::from_str(&format!("{}-{}1", tx_digest, event_index)); assert!(res.is_err()); } diff --git a/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs b/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs index 1a625f910..d90c2e066 100644 --- a/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs @@ -14,7 +14,7 @@ type RawSignature = [u8; 64]; pub struct Base58SolanaTxSignatureAndEventIndex { // Base58 decoded bytes of the Solana signature. pub raw_signature: RawSignature, - pub event_index: u32, + pub event_index: u64, } impl Base58SolanaTxSignatureAndEventIndex { @@ -25,7 +25,7 @@ impl Base58SolanaTxSignatureAndEventIndex { .expect("failed to convert tx hash to non-empty string") } - pub fn new(tx_id: impl Into, event_index: impl Into) -> Self { + pub fn new(tx_id: impl Into, event_index: impl Into) -> Self { Self { raw_signature: tx_id.into(), event_index: event_index.into(), @@ -102,7 +102,7 @@ mod tests { bs58::encode(random_bytes()).into_string() } - fn random_event_index() -> u32 { + fn random_event_index() -> u64 { rand::random() } @@ -327,7 +327,7 @@ mod tests { let event_index: u64 = u64::MAX; let tx_digest = random_tx_digest(); let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( - "{}-{}", + "{}-{}1", tx_digest, event_index )); assert!(res.is_err()); diff --git a/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs b/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs index 9f9bf8376..a960b2630 100644 --- a/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs @@ -6,24 +6,32 @@ use cosmwasm_std::HexBinary; use error_stack::{Report, ResultExt}; use lazy_static::lazy_static; use regex::Regex; +use serde_with::DeserializeFromStr; use super::Error; use crate::hash::Hash; use crate::nonempty; +#[derive(Debug, DeserializeFromStr, Clone)] pub struct HexTxHashAndEventIndex { pub tx_hash: Hash, - pub event_index: u32, + pub event_index: u64, } impl HexTxHashAndEventIndex { pub fn tx_hash_as_hex(&self) -> nonempty::String { - format!("0x{}", HexBinary::from(self.tx_hash).to_hex()) + format!("0x{}", self.tx_hash_as_hex_no_prefix()) .try_into() .expect("failed to convert tx hash to non-empty string") } - pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { + pub fn tx_hash_as_hex_no_prefix(&self) -> nonempty::String { + format!("{}", HexBinary::from(self.tx_hash).to_hex()) + .try_into() + .expect("failed to convert tx hash to non-empty string") + } + + pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { Self { tx_hash: tx_id.into(), event_index: event_index.into(), @@ -98,7 +106,7 @@ mod tests { format!("0x{}", HexBinary::from(bytes).to_hex()) } - fn random_event_index() -> u32 { + fn random_event_index() -> u64 { rand::random() } @@ -223,7 +231,7 @@ mod tests { fn should_not_parse_msg_id_with_overflowing_event_index() { let event_index: u64 = u64::MAX; let tx_hash = random_hash(); - let res = HexTxHashAndEventIndex::from_str(&format!("{}-{}", tx_hash, event_index)); + let res = HexTxHashAndEventIndex::from_str(&format!("{}-{}1", tx_hash, event_index)); assert!(res.is_err()); } }