diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..4dc58dc07 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +## [Unreleased](https://github.com/axelarnetwork/axelar-amplifier/tree/HEAD) + +[Full Changelog](https://github.com/axelarnetwork/axelar-amplifier/compare/ampd-v1.2.0..HEAD) + +- Change event index in message ids from u32 to u64. Emit message id from voting verifier [#666](https://github.com/axelarnetwork/axelar-amplifier/pull/666) + +#### Migration Notes + +The voting verifier contracts must be migrated before ampd is upgraded. Existing ampd instances will continue to work even after the contract migration, but we recommend upgrading ampd. \ No newline at end of file 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..7879f32aa 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,29 @@ 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 + ); + } + + #[test] + fn should_not_verify_verifier_set_if_log_index_greater_than_u32_max() { + let (gateway_address, tx_receipt, mut verifier_set) = + matching_verifier_set_and_tx_receipt(); + + verifier_set.message_id.event_index = u32::MAX as u64 + 1; assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound @@ -215,7 +233,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 +266,28 @@ 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.message_id.event_index = 2; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound ); - msg.event_index = 2; + msg.message_id.event_index = 3; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound ); - msg.event_index = 3; + } + + #[test] + fn should_not_verify_msg_if_log_index_greater_than_u32_max() { + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); + + msg.message_id.event_index = u32::MAX as u64 + 1; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -295,8 +324,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 +359,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/evm_verify_msg.rs b/ampd/src/handlers/evm_verify_msg.rs index 6fe8ca131..7380a49ca 100644 --- a/ampd/src/handlers/evm_verify_msg.rs +++ b/ampd/src/handlers/evm_verify_msg.rs @@ -32,8 +32,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 +120,6 @@ where }) .collect()) } - fn vote_msg(&self, poll_id: PollId, votes: Vec) -> MsgExecuteContract { MsgExecuteContract { sender: self.verifier.as_ref().clone(), @@ -174,24 +172,25 @@ where return Ok(vec![]); } - let tx_hashes: HashSet<_> = messages.iter().map(|message| message.tx_id).collect(); + let tx_hashes: HashSet = messages + .iter() + .map(|msg| msg.message_id.tx_hash.into()) + .collect(); 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 +199,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 +225,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 +247,11 @@ mod tests { use crate::PREFIX; fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_ids = [ + HexTxHashAndEventIndex::new(Hash::random(), 0u64), + HexTxHashAndEventIndex::new(Hash::random(), 1u64), + HexTxHashAndEventIndex::new(Hash::random(), 10u64), + ]; PollStarted::Messages { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -261,26 +266,30 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below events use the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed 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: u32::try_from(msg_ids[0].event_index).unwrap(), + 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: u32::try_from(msg_ids[1].event_index).unwrap(), + 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: u32::try_from(msg_ids[2].event_index).unwrap(), + 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..85f1ed2a6 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,13 @@ mod tests { } fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_id = HexTxHashAndEventIndex::new(Hash::random(), 100u64); PollStarted::VerifierSet { + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed 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: u32::try_from(msg_id.event_index).unwrap(), + 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..afed50e88 100644 --- a/ampd/src/handlers/mvx_verify_msg.rs +++ b/ampd/src/handlers/mvx_verify_msg.rs @@ -316,11 +316,17 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed messages: vec![TxEventConfirmation { tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" .parse() .unwrap(), event_index: 1, + message_id: + "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..efceec210 100644 --- a/ampd/src/handlers/mvx_verify_verifier_set.rs +++ b/ampd/src/handlers/mvx_verify_verifier_set.rs @@ -343,11 +343,16 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed verifier_set: VerifierSetConfirmation { tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" .parse() .unwrap(), event_index: 1, + message_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312-1" + .to_string() + .try_into() + .unwrap(), verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), }, } diff --git a/ampd/src/handlers/stellar_verify_msg.rs b/ampd/src/handlers/stellar_verify_msg.rs index 7de378a12..a4ab51ab6 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,26 @@ 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); + #[allow(deprecated)] + // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed + TxEventConfirmation { + tx_id: msg_id.tx_hash_as_hex(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + 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..fb77e519f 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(), @@ -292,9 +297,11 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed 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: u32::try_from(msg_id.event_index).unwrap(), + 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..542084ffc 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(), @@ -337,9 +342,11 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed messages: vec![TxEventConfirmation { - tx_id: TransactionDigest::random().to_string().parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_digest_as_base58(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + 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..2b42d9d44 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(), @@ -240,9 +240,11 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed verifier_set: VerifierSetConfirmation { - tx_id: TransactionDigest::random().to_string().parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_digest_as_base58(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + 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 6dbd54b5b..5efa0e926 100644 --- a/ampd/src/stellar/verifier.rs +++ b/ampd/src/stellar/verifier.rs @@ -74,8 +74,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, ) } @@ -88,8 +88,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, ) } @@ -98,7 +101,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; @@ -126,6 +129,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}; @@ -152,7 +156,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), @@ -163,7 +167,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), @@ -236,7 +240,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), @@ -247,7 +251,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), @@ -292,8 +296,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()), ))), @@ -326,7 +329,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()), @@ -344,8 +347,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() @@ -383,7 +385,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..6a4b527ac 100644 --- a/contracts/voting-verifier/src/events.rs +++ b/contracts/voting-verifier/src/events.rs @@ -136,12 +136,16 @@ impl From for Event { #[cw_serde] pub struct VerifierSetConfirmation { + #[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 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 +154,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 +191,19 @@ 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)?; + #[allow(deprecated)] + let (tx_id, event_index) = parse_message_id(&message_id, &msg_id_format)?; + #[allow(deprecated)] + // TODO: remove this attribute when tx_id and event_index are removed from the event Ok(Self { tx_id, event_index, + message_id, verifier_set, }) } @@ -191,8 +211,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, @@ -206,11 +229,15 @@ pub struct TxEventConfirmation { impl TryFrom<(Message, &MessageIdFormat)> for TxEventConfirmation { type Error = ContractError; fn try_from((msg, msg_id_format): (Message, &MessageIdFormat)) -> Result { + #[allow(deprecated)] let (tx_id, event_index) = parse_message_id(&msg.cc_id.message_id, msg_id_format)?; + #[allow(deprecated)] + // TODO: remove this attribute when tx_id and event_index are removed from the event 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, @@ -338,8 +365,7 @@ mod test { TxEventConfirmation::try_from((msg.clone(), &MessageIdFormat::HexTxHashAndEventIndex)) .unwrap(); - assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.message_id, msg.cc_id.message_id); compare_event_to_message(event, msg); } @@ -353,8 +379,7 @@ mod test { let event = TxEventConfirmation::try_from((msg.clone(), &MessageIdFormat::HexTxHash)).unwrap(); - assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); - assert_eq!(event.event_index, 0); + assert_eq!(event.message_id, msg.cc_id.message_id); compare_event_to_message(event, msg); } @@ -372,8 +397,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.message_id, msg.cc_id.message_id); compare_event_to_message(event, msg); } @@ -404,7 +428,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 +436,13 @@ 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.message_id, msg_id.to_string().try_into().unwrap()); assert_eq!(event.verifier_set, verifier_set); } @@ -427,7 +450,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 +458,13 @@ 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.message_id, msg_id.to_string().try_into().unwrap()); assert_eq!(event.verifier_set, verifier_set); } @@ -456,7 +478,7 @@ mod test { }; let event = VerifierSetConfirmation::new( - msg_id, + msg_id.to_string().parse().unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set, ); @@ -467,7 +489,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 +498,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-core-std/src/nexus/execute.rs b/packages/axelar-core-std/src/nexus/execute.rs index ace895da8..074dc1ff4 100644 --- a/packages/axelar-core-std/src/nexus/execute.rs +++ b/packages/axelar-core-std/src/nexus/execute.rs @@ -34,7 +34,7 @@ fn parse_message_id(message_id: &str) -> Result<(nonempty::Vec, u64), Error> let tx_id = nonempty::Vec::::try_from(id.tx_hash.to_vec()) .change_context(Error::InvalidMessageId(message_id.into()))?; - Ok((tx_id, id.event_index.into())) + Ok((tx_id, id.event_index)) } impl From for Message { @@ -102,7 +102,7 @@ mod test { destination_address: "something else".parse().unwrap(), payload_hash: [1; 32], source_tx_id: msg_id.tx_hash.to_vec().try_into().unwrap(), - source_tx_index: msg_id.event_index as u64, + source_tx_index: msg_id.event_index, id: msg_id.to_string(), }; diff --git a/packages/axelar-wasm-std/Cargo.toml b/packages/axelar-wasm-std/Cargo.toml index 6ad8685eb..3472b6de8 100644 --- a/packages/axelar-wasm-std/Cargo.toml +++ b/packages/axelar-wasm-std/Cargo.toml @@ -45,6 +45,7 @@ report = { workspace = true } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } serde_json = "1.0.89" +serde_with = { version = "3.11.0", features = ["macros"] } sha3 = { workspace = true } stellar-xdr = { workspace = true } strum = { 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..97f269bce 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,34 @@ 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 { + HexBinary::from(self.tx_hash) + .to_hex() + .to_string() + .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 +108,7 @@ mod tests { format!("0x{}", HexBinary::from(bytes).to_hex()) } - fn random_event_index() -> u32 { + fn random_event_index() -> u64 { rand::random() } @@ -223,7 +233,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()); } }