From dd37ef235d92bc524a814a32bfa4852d51729487 Mon Sep 17 00:00:00 2001 From: Roberts Pumpurs <33699735+roberts-pumpurs@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:51:31 +0300 Subject: [PATCH] feat: transfer ops event, message executed event, rkyv encoding (#416) * refactor: clean up axelar message primitives * refactor: remove command PDA parameter from `rotate_signers` * feat: gateway events will be rkyv encoded --- .../axelar-message-primitives/src/lib.rs | 4 - .../helpers/test-fixtures/src/execute_data.rs | 6 +- .../helpers/test-fixtures/src/test_setup.rs | 74 +++--- .../src/instruction.rs | 8 +- .../tests/evm-e2e/from_solana_to_evm.rs | 46 ++-- .../tests/module/send_to_gateway.rs | 20 +- solana/programs/gas-service/src/events.rs | 2 +- .../programs/gas-service/src/instruction.rs | 2 +- solana/programs/gas-service/src/processor.rs | 2 +- .../programs/gas-service/tests/integration.rs | 2 +- .../gateway/src/axelar_auth_weighted.rs | 4 +- solana/programs/gateway/src/commands.rs | 1 + solana/programs/gateway/src/events.rs | 210 +++++++++++------- solana/programs/gateway/src/instructions.rs | 20 +- .../gateway/src/processor/approve_messages.rs | 3 +- .../gateway/src/processor/call_contract.rs | 9 +- .../src/processor/initialize_config.rs | 2 +- .../gateway/src/processor/rotate_signers.rs | 22 +- .../src/processor/transfer_operatorship.rs | 7 + .../gateway/src/processor/validate_message.rs | 4 + .../gateway/src/state/verifier_set_tracker.rs | 2 +- .../gateway/tests/module/approve_messages.rs | 20 +- .../tests/module/initialize_execute_data.rs | 14 +- solana/programs/gateway/tests/module/main.rs | 55 ++++- .../gateway/tests/module/rotate_signers.rs | 59 ++--- solana/xtask/src/cli/cmd/solana.rs | 2 +- .../src/cli/cmd/solana/message_limits.rs | 4 +- .../cli/cmd/testnet/solana_interactions.rs | 24 +- 28 files changed, 365 insertions(+), 263 deletions(-) diff --git a/solana/helpers/axelar-message-primitives/src/lib.rs b/solana/helpers/axelar-message-primitives/src/lib.rs index ac6e057e..58adabae 100644 --- a/solana/helpers/axelar-message-primitives/src/lib.rs +++ b/solana/helpers/axelar-message-primitives/src/lib.rs @@ -7,7 +7,3 @@ pub use address::*; pub use destination_program_id::*; pub use payload::*; pub use u256::*; - -pub mod command { - pub use super::u256::*; -} diff --git a/solana/helpers/test-fixtures/src/execute_data.rs b/solana/helpers/test-fixtures/src/execute_data.rs index f169f3e2..534d4241 100644 --- a/solana/helpers/test-fixtures/src/execute_data.rs +++ b/solana/helpers/test-fixtures/src/execute_data.rs @@ -11,7 +11,7 @@ pub fn prepare_execute_data( payload: Payload, signers: &SigningVerifierSet, domain_separator: &[u8; 32], -) -> (Vec, VerifierSet) { +) -> (ExecuteData, VerifierSet) { // Setup let verifier_set = signers.verifier_set(); let payload_hash = hash_payload(domain_separator, &verifier_set, &payload, hasher_impl()); @@ -40,10 +40,10 @@ pub fn prepare_execute_data( .unwrap(); // Confidence check: ExecuteData can be deserialized - ExecuteData::from_bytes(&execute_data_bytes).expect("valid deserialization"); + let execute_data = ExecuteData::from_bytes(&execute_data_bytes).expect("valid deserialization"); // Confidence check: ExecuteData can be cast to its archive ArchivedExecuteData::from_bytes(&execute_data_bytes).expect("valid archival"); - (execute_data_bytes, verifier_set) + (execute_data, verifier_set) } diff --git a/solana/helpers/test-fixtures/src/test_setup.rs b/solana/helpers/test-fixtures/src/test_setup.rs index 3390c38d..39e42693 100644 --- a/solana/helpers/test-fixtures/src/test_setup.rs +++ b/solana/helpers/test-fixtures/src/test_setup.rs @@ -1,11 +1,10 @@ use std::path::PathBuf; -use axelar_message_primitives::command::U256; -use axelar_message_primitives::DataPayload; +use axelar_message_primitives::{DataPayload, U256}; use axelar_rkyv_encoding::rkyv::validation::validators::DefaultValidator; use axelar_rkyv_encoding::rkyv::CheckBytes; use axelar_rkyv_encoding::types::u128::U128; -use axelar_rkyv_encoding::types::{Message, Payload, VerifierSet}; +use axelar_rkyv_encoding::types::{ExecuteData, Message, Payload, VerifierSet}; use borsh::BorshDeserialize; use gateway::commands::OwnedCommand; use gateway::hasher_impl; @@ -438,42 +437,62 @@ impl TestFixture { .unwrap(); } - async fn init_approve_messages_execute_data( + pub async fn init_approve_messages_execute_data( &mut self, gateway_root_pda: &Pubkey, payload: Payload, signers: &SigningVerifierSet, domain_separator: &[u8; 32], - ) -> (Pubkey, Vec) { + ) -> ( + Pubkey, + ExecuteData, + GatewayExecuteData, + ) { let (raw_data, _) = prepare_execute_data(payload, signers, domain_separator); let execute_data_pda = self .init_approve_messages_execute_data_with_custom_data( gateway_root_pda, - &raw_data, + &raw_data.to_bytes::<0>().unwrap(), domain_separator, ) .await; + let execute_data = GatewayExecuteData::new( + &raw_data.to_bytes::<0>().unwrap(), + gateway_root_pda, + domain_separator, + ) + .unwrap(); - (execute_data_pda, raw_data) + (execute_data_pda, raw_data, execute_data) } - async fn init_rotate_signers_execute_data( + pub async fn init_rotate_signers_execute_data( &mut self, gateway_root_pda: &Pubkey, payload: Payload, signers: &SigningVerifierSet, domain_separator: &[u8; 32], - ) -> (Pubkey, Vec) { + ) -> ( + Pubkey, + ExecuteData, + GatewayExecuteData, + ) { let (raw_data, _) = prepare_execute_data(payload, signers, domain_separator); let execute_data_pda = self .init_rotate_signers_execute_data_with_custom_data( gateway_root_pda, - &raw_data, + &raw_data.to_bytes::<0>().unwrap(), domain_separator, ) .await; + let execute_data = GatewayExecuteData::new( + &raw_data.to_bytes::<0>().unwrap(), + gateway_root_pda, + domain_separator, + ) + .unwrap(); - (execute_data_pda, raw_data) + (execute_data_pda, raw_data, execute_data) } pub async fn init_execute_data( @@ -485,22 +504,26 @@ impl TestFixture { ) -> (Pubkey, Vec) { match &payload { Payload::Messages(_) => { - self.init_approve_messages_execute_data( - gateway_root_pda, - payload, - signers, - domain_separator, - ) - .await + let res = self + .init_approve_messages_execute_data( + gateway_root_pda, + payload, + signers, + domain_separator, + ) + .await; + (res.0, res.1.to_bytes::<0>().unwrap()) } Payload::VerifierSet(_) => { - self.init_rotate_signers_execute_data( - gateway_root_pda, - payload, - signers, - domain_separator, - ) - .await + let res = self + .init_rotate_signers_execute_data( + gateway_root_pda, + payload, + signers, + domain_separator, + ) + .await; + (res.0, res.1.to_bytes::<0>().unwrap()) } } } @@ -818,7 +841,6 @@ impl TestFixture { domain_separator, ) .await; - let tx = self .rotate_signers_with_metadata( gateway_root_pda, diff --git a/solana/programs/axelar-solana-memo-program/src/instruction.rs b/solana/programs/axelar-solana-memo-program/src/instruction.rs index c8654c21..02904c25 100644 --- a/solana/programs/axelar-solana-memo-program/src/instruction.rs +++ b/solana/programs/axelar-solana-memo-program/src/instruction.rs @@ -36,9 +36,9 @@ pub enum AxelarMemoInstruction { /// Memo to send to the gateway memo: String, /// Destination chain we want to communicate with - destination_chain: Vec, + destination_chain: String, /// Destination contract address on the destination chain - destination_address: Vec, + destination_address: String, }, } @@ -75,8 +75,8 @@ pub fn call_gateway_with_memo( gateway_root_pda: &Pubkey, sender: &Pubkey, memo: String, - destination_chain: Vec, - destination_address: Vec, + destination_chain: String, + destination_address: String, ) -> Result { let instruction_data = AxelarCallableInstruction::Native(AxelarMemoInstruction::SendToGateway { diff --git a/solana/programs/axelar-solana-memo-program/tests/evm-e2e/from_solana_to_evm.rs b/solana/programs/axelar-solana-memo-program/tests/evm-e2e/from_solana_to_evm.rs index f001fa35..93af18c3 100644 --- a/solana/programs/axelar-solana-memo-program/tests/evm-e2e/from_solana_to_evm.rs +++ b/solana/programs/axelar-solana-memo-program/tests/evm-e2e/from_solana_to_evm.rs @@ -1,10 +1,12 @@ -use std::borrow::Cow; +use std::str::FromStr; use axelar_solana_memo_program::instruction::call_gateway_with_memo; +use ethers_core::utils::hex::ToHex; use ethers_core::utils::keccak256; use evm_contracts_test_suite::evm_contracts_rs::contracts::axelar_memo::ReceivedMemoFilter; -use gateway::events::{CallContract, GatewayEvent}; +use gateway::events::{ArchivedCallContract, ArchivedGatewayEvent, EventContainer, GatewayEvent}; use solana_program_test::tokio; +use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; @@ -28,11 +30,11 @@ async fn test_send_from_solana_to_evm() { let solana_id = "solana-localnet"; let memo = "🐪🐪🐪🐪"; let destination_address: ethers_core::types::Address = evm_memo.address(); - let destination_chain = "ethereum".to_string().into_bytes(); + let destination_chain = "ethereum".to_string(); // Action: // - send message from Solana memo program to Solana gateway - let call_contract = call_solana_gateway( + let gateway_event = call_solana_gateway( &solana_chain.gateway_root_pda, &mut solana_chain.fixture, memo, @@ -40,10 +42,14 @@ async fn test_send_from_solana_to_evm() { &destination_address, ) .await; + let ArchivedGatewayEvent::CallContract(call_contract) = gateway_event.parse() else { + panic!("Expected CallContract event, got {:?}", gateway_event); + }; + // - EVM operators sign the contract call let (messages, proof) = evm_prepare_approve_contract_call( solana_id, - &call_contract, + call_contract, &mut weighted_signers, domain_separator, ); @@ -71,12 +77,12 @@ async fn test_send_from_solana_to_evm() { .unwrap(); assert!(is_approved, "contract call was not approved"); assert_eq!( - keccak256(call_contract.payload.clone()), + keccak256(&call_contract.payload), call_contract.payload_hash ); assert_eq!( evm_memo.address(), - ethers_core::types::Address::from_slice(call_contract.destination_address.as_slice()) + ethers_core::types::Address::from_str(call_contract.destination_address.as_str()).unwrap() ); // Action - Relayer calls the EVM memo program with the payload @@ -85,7 +91,7 @@ async fn test_send_from_solana_to_evm() { message.source_chain, message.message_id, message.source_address, - call_contract.payload.into(), + call_contract.payload.to_vec().into(), ) .send() .await @@ -112,7 +118,7 @@ async fn test_send_from_solana_to_evm() { fn evm_prepare_approve_contract_call( solana_id: &str, - call_contract: &CallContract, + call_contract: &ArchivedCallContract, signer_set: &mut evm_contracts_test_suite::evm_weighted_signers::WeightedSigners, domain_separator: [u8; 32], ) -> ( @@ -123,10 +129,11 @@ fn evm_prepare_approve_contract_call( evm_contracts_test_suite::evm_contracts_rs::contracts::axelar_amplifier_gateway::Message { source_chain: solana_id.to_string(), message_id: "message555".to_string(), - source_address: call_contract.sender.to_string(), - contract_address: ethers_core::types::Address::from_slice( - call_contract.destination_address.as_slice(), - ), + source_address: Pubkey::from(call_contract.sender).to_string(), + contract_address: ethers_core::types::Address::from_str( + call_contract.destination_address.as_str(), + ) + .unwrap(), payload_hash: call_contract.payload_hash, }; let approve_contract_call_command = @@ -145,16 +152,18 @@ async fn call_solana_gateway( gateway_root_pda: &solana_sdk::pubkey::Pubkey, solana_fixture: &mut test_fixtures::test_setup::TestFixture, memo: &str, - destination_chain: Vec, + destination_chain: String, destination_address: ðers_core::types::H160, -) -> CallContract { +) -> EventContainer { + let destination_address = destination_address.encode_hex(); + dbg!(&destination_address); let transaction = Transaction::new_signed_with_payer( &[call_gateway_with_memo( gateway_root_pda, &solana_fixture.payer.pubkey(), memo.to_string(), destination_chain, - destination_address.as_bytes().to_vec(), + destination_address, ) .unwrap()], Some(&solana_fixture.payer.pubkey()), @@ -178,9 +187,6 @@ async fn call_solana_gateway( .iter() .find_map(GatewayEvent::parse_log) .expect("Gateway event was not emitted?"); - let GatewayEvent::CallContract(Cow::Owned(call_contract)) = gateway_event else { - panic!("Expected CallContract event, got {:?}", gateway_event); - }; - call_contract + gateway_event } diff --git a/solana/programs/axelar-solana-memo-program/tests/module/send_to_gateway.rs b/solana/programs/axelar-solana-memo-program/tests/module/send_to_gateway.rs index 022ab9f5..790e7971 100644 --- a/solana/programs/axelar-solana-memo-program/tests/module/send_to_gateway.rs +++ b/solana/programs/axelar-solana-memo-program/tests/module/send_to_gateway.rs @@ -1,8 +1,7 @@ -use std::borrow::Cow; - use axelar_solana_memo_program::instruction::call_gateway_with_memo; +use ethers_core::abi::AbiEncode; use ethers_core::utils::keccak256; -use gateway::events::GatewayEvent; +use gateway::events::{CallContract, GatewayEvent}; use solana_program_test::tokio; use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; @@ -14,8 +13,8 @@ async fn test_succesfully_send_to_gateway() { // Setup let mut solana_chain = program_test().await; let memo = "🐪🐪🐪🐪"; - let destination_address = ethers_core::types::Address::random().0.to_vec(); - let destination_chain = "ethereum".to_string().into_bytes(); + let destination_address = ethers_core::types::Address::random().encode_hex(); + let destination_chain = "ethereum".to_string(); // Action: send message to gateway let transaction = Transaction::new_signed_with_payer( @@ -53,15 +52,16 @@ async fn test_succesfully_send_to_gateway() { .iter() .find_map(GatewayEvent::parse_log) .expect("Gateway event was not emitted?"); + let gateway_event = gateway_event.parse(); assert_eq!( gateway_event, - GatewayEvent::CallContract(Cow::Owned(gateway::events::CallContract { - sender: solana_chain.fixture.payer.pubkey(), + &GatewayEvent::CallContract(CallContract { + sender: solana_chain.fixture.payer.pubkey().to_bytes(), destination_chain, - destination_address: destination_address.to_vec(), - payload_hash: keccak256(memo.as_bytes()), + destination_address, payload: memo.as_bytes().to_vec(), - })), + payload_hash: keccak256(memo.as_bytes()) + }), "Mismatched gateway event" ); } diff --git a/solana/programs/gas-service/src/events.rs b/solana/programs/gas-service/src/events.rs index 4a23d989..32e3aed8 100644 --- a/solana/programs/gas-service/src/events.rs +++ b/solana/programs/gas-service/src/events.rs @@ -1,6 +1,6 @@ //! Axelar Gas Service events. -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use base64::engine::general_purpose; use base64::Engine as _; use borsh::{BorshDeserialize, BorshSerialize}; diff --git a/solana/programs/gas-service/src/instruction.rs b/solana/programs/gas-service/src/instruction.rs index a24e06c0..22e9d77b 100644 --- a/solana/programs/gas-service/src/instruction.rs +++ b/solana/programs/gas-service/src/instruction.rs @@ -1,7 +1,7 @@ //! Instruction module; consist of fasade instructions, test ix constructors and //! internal helpers. -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use borsh::{to_vec, BorshDeserialize, BorshSerialize}; use solana_program::instruction::{AccountMeta, Instruction}; use solana_program::program_error::ProgramError; diff --git a/solana/programs/gas-service/src/processor.rs b/solana/programs/gas-service/src/processor.rs index 00c500f7..a754f688 100644 --- a/solana/programs/gas-service/src/processor.rs +++ b/solana/programs/gas-service/src/processor.rs @@ -1,6 +1,6 @@ //! Program processor -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use borsh::BorshDeserialize; use solana_program::account_info::{next_account_info, AccountInfo}; use solana_program::entrypoint::ProgramResult; diff --git a/solana/programs/gas-service/tests/integration.rs b/solana/programs/gas-service/tests/integration.rs index e0cfc094..f3b4f89d 100644 --- a/solana/programs/gas-service/tests/integration.rs +++ b/solana/programs/gas-service/tests/integration.rs @@ -1,7 +1,7 @@ mod common; use anyhow::{Ok, Result}; -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use base64::engine::general_purpose; use base64::Engine; use borsh::BorshDeserialize; diff --git a/solana/programs/gateway/src/axelar_auth_weighted.rs b/solana/programs/gateway/src/axelar_auth_weighted.rs index 6da4ee5e..5a272898 100644 --- a/solana/programs/gateway/src/axelar_auth_weighted.rs +++ b/solana/programs/gateway/src/axelar_auth_weighted.rs @@ -99,11 +99,11 @@ impl AxelarAuthWeighted { } let epoch = self.current_epoch(); - let diff: BnumU256 = epoch + let elapsed: BnumU256 = epoch .checked_sub(verifier_set_tracker.epoch) .ok_or(AxelarAuthWeightedError::EpochCalculationOverflow)? .into(); - if diff >= self.previous_signers_retention.into() { + if elapsed >= self.previous_signers_retention.into() { msg!("verifier set is too old"); return Err(AxelarAuthWeightedError::InvalidSignerSet); } diff --git a/solana/programs/gateway/src/commands.rs b/solana/programs/gateway/src/commands.rs index 7ce0007b..7e0e2dce 100644 --- a/solana/programs/gateway/src/commands.rs +++ b/solana/programs/gateway/src/commands.rs @@ -1,4 +1,5 @@ #![allow(missing_docs)] +// todo -- delete this whole file use std::io::prelude::{Read, Write}; use std::io::{self}; diff --git a/solana/programs/gateway/src/events.rs b/solana/programs/gateway/src/events.rs index 4b786424..85de687e 100644 --- a/solana/programs/gateway/src/events.rs +++ b/solana/programs/gateway/src/events.rs @@ -1,43 +1,54 @@ //! Types used for logging messages. -use std::borrow::Cow; use axelar_rkyv_encoding::types::{ArchivedMessage, Message}; use base64::engine::general_purpose; use base64::Engine as _; -use borsh::{BorshDeserialize, BorshSerialize}; +use rkyv::bytecheck::{self, CheckBytes}; use solana_program::log::sol_log_data; use solana_program::program_error::ProgramError; use solana_program::pubkey::Pubkey; -use crate::commands::OwnedCommand; use crate::error::GatewayError; use crate::hasher_impl; -#[derive(Debug, Clone, PartialEq, BorshDeserialize, BorshSerialize)] /// Logged when the Gateway receives an outbound message. +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Debug, PartialEq, Eq)] +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug, PartialEq, Eq, CheckBytes))] pub struct CallContract { /// Message sender. - pub sender: Pubkey, + pub sender: [u8; 32], /// The name of the target blockchain. - pub destination_chain: Vec, + pub destination_chain: String, /// The address of the target contract in the destination blockchain. - pub destination_address: Vec, + pub destination_address: String, /// Contract call data. pub payload: Vec, /// The payload hash. pub payload_hash: [u8; 32], } -#[derive(Debug, Clone, PartialEq, BorshDeserialize, BorshSerialize)] +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Debug, PartialEq, Eq)] +/// Event that gets emitted when a message has been executed +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug, PartialEq, Eq, CheckBytes))] +pub struct MessageExecuted { + /// The command id of the given message + pub command_id: [u8; 32], +} + /// Emitted for every approved message after the Gateway validates a command /// batch. +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Debug, PartialEq, Eq)] +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug, PartialEq, Eq, CheckBytes))] pub struct MessageApproved { /// The command ID pub command_id: [u8; 32], - /// The message id - pub message_id: Vec, /// Source chain. pub source_chain: Vec, + /// The message id + pub message_id: Vec, /// Source address. pub source_address: Vec, /// Destination address on Solana. @@ -46,62 +57,50 @@ pub struct MessageApproved { pub payload_hash: [u8; 32], } +/// Emitted when the latest signer set has been rotated +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Debug, PartialEq, Eq)] +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug, PartialEq, Eq, CheckBytes))] +pub struct RotateSignersEvent { + /// the new latet epoch + pub new_epoch: crate::state::verifier_set_tracker::Epoch, + /// the hash of the new signer set + pub new_signers_hash: [u8; 32], + /// little-endian encoded Pubkey that points to the ExecuteData PDA, which + /// contains the full information about the latest signer set + pub execute_data_pda: [u8; 32], +} + +/// Event that gets emitted when the operatorship has been transferred +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Debug, PartialEq, Eq)] +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug, PartialEq, Eq, CheckBytes))] +pub struct OperatorshipTransferred { + /// little-endian encoded Pubkey for the latest operator + pub operator: [u8; 32], +} + /// Gateway program logs. /// /// Used internally by the Gateway program to log messages. /// We are using Cow to avoid unnecessary allocations and NOT take /// ownership of the data when emitting events. #[non_exhaustive] -#[repr(u8)] -#[derive(Debug, PartialEq, Clone, BorshSerialize)] -pub enum GatewayEvent<'a> { +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Debug, PartialEq, Eq)] +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug, PartialEq, Eq, CheckBytes))] +pub enum GatewayEvent { /// Logged when the Gateway receives an outbound message. - CallContract(Cow<'a, CallContract>), + CallContract(CallContract), /// The event emitted after successful keys rotation. - SignersRotated(Cow<'a, ()>), + SignersRotated(RotateSignersEvent), /// Emitted for every approved message after the Gateway validates a command /// batch - MessageApproved(Cow<'a, MessageApproved>), -} - -// Custom deserialization implementation for `GatewayEvent`. -// Reason: Borsh does not support deserializing data that has lifetime bounds, -// so we need to handle it ourselves. -impl<'a> BorshDeserialize for GatewayEvent<'a> { - fn deserialize_reader(reader: &mut R) -> std::io::Result { - let tag = u8::deserialize_reader(reader)?; - match tag { - 0 => Ok(GatewayEvent::CallContract(Cow::Owned( - CallContract::deserialize_reader(reader)?, - ))), - 1 => Ok(GatewayEvent::SignersRotated(Cow::Owned(()))), - 2 => Ok(GatewayEvent::MessageApproved(Cow::Owned( - MessageApproved::deserialize_reader(reader)?, - ))), - _ => Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!("Invalid tag: {}", tag), - )), - } - } -} - -impl TryFrom for GatewayEvent<'_> { - type Error = GatewayError; - - fn try_from(command: OwnedCommand) -> Result { - let event = match command { - OwnedCommand::ApproveMessage(message) => { - GatewayEvent::MessageApproved(Cow::Owned(message.try_into()?)) - } - OwnedCommand::RotateSigners(_verifier_set) => { - // TODO: Replace 'RotateSignersCommand' for something more up to date with - // Axelar specification. - unimplemented!("We should not implement anything on top of 'RotateSignersCommand' because it is deprecated") - } - }; - Ok(event) - } + MessageApproved(MessageApproved), + /// Emitted when a message has been executed + MessageExecuted(MessageExecuted), + /// Emitted when a the operatorship has been transferred + OperatorshipTransferred(OperatorshipTransferred), } impl TryFrom for MessageApproved { @@ -150,24 +149,56 @@ impl TryFrom<&ArchivedMessage> for MessageApproved { } } -impl<'a> GatewayEvent<'a> { +impl GatewayEvent { /// Emits the log for this event. pub fn emit(&self) -> Result<(), ProgramError> { - let serialized = borsh::to_vec(self)?; - sol_log_data(&[&serialized]); + let item = self.encode(); + sol_log_data(&[&item]); Ok(()) } + /// Encode the [`GatewayEvent`] into a [`Vec`] which satifies rkyv + /// alignment requirements + pub fn encode(&self) -> rkyv::AlignedVec { + rkyv::to_bytes::<_, 0>(self).unwrap() + } + /// Try to parse a [`GatewayEvent`] out of a Solana program log line. - pub fn parse_log>(log: T) -> Option { - let cleaned_input = log + pub fn parse_log>(log: T) -> Option { + let buffer = log .as_ref() .trim() .trim_start_matches("Program data:") .split_whitespace() .flat_map(decode_base64) .next()?; - borsh::from_slice(&cleaned_input).ok() + + EventContainer::new(buffer) + } +} + +/// Wrapper around the rkyv encoded [`GatewayEvent`] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EventContainer { + buffer: Vec, +} + +impl EventContainer { + /// Create a new [`EventContainer`] from an rkyv enocded [`GatewayEvent`] + /// + /// The method will return `None` if the buffer cannod be deserialised into + /// a valid [`ArchivedGatewayEvent`] + pub fn new(buffer: Vec) -> Option { + // check if this is a valid gateway event + let _data = rkyv::check_archived_root::(&buffer).ok()?; + Some(Self { buffer }) + } + + /// Return a view into the buffer, deserialised + pub fn parse(&self) -> &ArchivedGatewayEvent { + // safe: we already checked that the buffer is valid when initializing it + let data = unsafe { rkyv::archived_root::(&self.buffer) }; + data } } @@ -184,12 +215,17 @@ mod tests { fn test_gateway_event_round_trip() { // Setup let call_contract = CallContract { - sender: Pubkey::new_unique(), - destination_chain: b"ethereum".to_vec(), - destination_address: b"0x123...abc".to_vec(), + sender: Pubkey::new_unique().to_bytes(), + destination_chain: "ethereum".to_owned(), + destination_address: "0x123...abc".to_owned(), payload: b"function_call()".to_vec(), payload_hash: [1; 32], }; + let rotate_signers_command = RotateSignersEvent { + new_epoch: axelar_message_primitives::U256::from_u64(55), + new_signers_hash: [42; 32], + execute_data_pda: [55; 32], + }; let message_approved = MessageApproved { command_id: [2; 32], message_id: vec![2; 32], @@ -198,29 +234,33 @@ mod tests { destination_address: [3; 32], payload_hash: [4; 32], }; - let events_owned = vec![ - GatewayEvent::CallContract(Cow::Owned(call_contract.clone())), - GatewayEvent::MessageApproved(Cow::Owned(message_approved.clone())), - ]; - let events_borrowed = vec![ - GatewayEvent::CallContract(Cow::Borrowed(&call_contract)), - GatewayEvent::MessageApproved(Cow::Borrowed(&message_approved)), + let transfer_operatorship = OperatorshipTransferred { + operator: [123; 32], + }; + let message_executed = MessageExecuted { + command_id: [255; 32], + }; + let events = vec![ + GatewayEvent::CallContract(call_contract), + GatewayEvent::SignersRotated(rotate_signers_command), + GatewayEvent::MessageApproved(message_approved), + GatewayEvent::OperatorshipTransferred(transfer_operatorship), + GatewayEvent::MessageExecuted(message_executed), ]; - for (event_owned, event_borrowed) in - events_owned.into_iter().zip(events_borrowed.into_iter()) - { + for event in events.into_iter() { // Action - let serialized_borrowed = borsh::to_vec(&event_borrowed).unwrap(); - let deserialized_borrowed = borsh::from_slice(&serialized_borrowed).unwrap(); - let serialized_owned = borsh::to_vec(&event_owned).unwrap(); - let deserialized_owned = borsh::from_slice(&serialized_owned).unwrap(); - - // Assert - every combination should be equal - assert_eq!(event_owned, deserialized_borrowed); - assert_eq!(event_borrowed, deserialized_borrowed); - assert_eq!(event_owned, deserialized_owned); - assert_eq!(event_borrowed, deserialized_owned); + let event_encoded = event.encode(); + let event_encoded = general_purpose::STANDARD.encode(event_encoded.as_slice()); + let log = format!("Program log: {event_encoded}"); + + let decoded_event_container = GatewayEvent::parse_log(log).unwrap(); + let decoded_event = decoded_event_container.parse(); + + assert_eq!( + &event, decoded_event, + "pre-encoded and post-encoded events don't match" + ); } } } diff --git a/solana/programs/gateway/src/instructions.rs b/solana/programs/gateway/src/instructions.rs index 5cbedf6b..9593a994 100644 --- a/solana/programs/gateway/src/instructions.rs +++ b/solana/programs/gateway/src/instructions.rs @@ -36,8 +36,6 @@ pub enum GatewayInstruction { /// Rotate signers for the Gateway Root Config PDA account. /// - /// Accounts expected by this instruction: - /// 0. [WRITE] Gateway Root Config PDA account /// 1. [] Gateway ExecuteData PDA account /// 2. [] Verifier Setr Tracker PDA account (the one that signed the /// ExecuteData) @@ -55,9 +53,9 @@ pub enum GatewayInstruction { /// 1. [] Gateway Root Config PDA account CallContract { /// The name of the target blockchain. - destination_chain: Vec, + destination_chain: String, /// The address of the target contract in the destination blockchain. - destination_contract_address: Vec, + destination_contract_address: String, /// Contract call data. payload: Vec, }, @@ -341,8 +339,8 @@ pub fn handle_execute_data( pub fn call_contract( gateway_root_pda: Pubkey, sender: Pubkey, - destination_chain: Vec, - destination_contract_address: Vec, + destination_chain: String, + destination_contract_address: String, payload: Vec, ) -> Result { let data = to_vec(&GatewayInstruction::CallContract { @@ -578,9 +576,8 @@ pub mod tests { #[test] fn round_trip_call_contract() { - let destination_chain = "ethereum".as_bytes().to_vec(); - let destination_contract_address = - hex::decode("2F43DDFf564Fb260dbD783D55fc6E4c70Be18862").unwrap(); + let destination_chain = "ethereum".to_owned(); + let destination_contract_address = "2F43DDFf564Fb260dbD783D55fc6E4c70Be18862".to_owned(); let payload = vec![5; 100]; let instruction = GatewayInstruction::CallContract { @@ -598,9 +595,8 @@ pub mod tests { #[test] fn round_trip_call_contract_function() { let sender = Keypair::new().pubkey(); - let destination_chain = "ethereum".as_bytes().to_vec(); - let destination_contract_address = - hex::decode("2F43DDFf564Fb260dbD783D55fc6E4c70Be18862").unwrap(); + let destination_chain = "ethereum".to_owned(); + let destination_contract_address = "2F43DDFf564Fb260dbD783D55fc6E4c70Be18862".to_owned(); let payload = vec![5; 100]; let instruction = call_contract( diff --git a/solana/programs/gateway/src/processor/approve_messages.rs b/solana/programs/gateway/src/processor/approve_messages.rs index 9b0f9c6e..c06dc8cc 100644 --- a/solana/programs/gateway/src/processor/approve_messages.rs +++ b/solana/programs/gateway/src/processor/approve_messages.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use itertools::*; use program_utils::ValidPDA; @@ -100,7 +99,7 @@ where approved_command_account.set_ready_for_validate_message()?; let message_approved = axelar_message.try_into()?; - let event = GatewayEvent::MessageApproved(Cow::Borrowed(&message_approved)); + let event = GatewayEvent::MessageApproved(message_approved); event.emit()?; // Save the updated approved message account diff --git a/solana/programs/gateway/src/processor/call_contract.rs b/solana/programs/gateway/src/processor/call_contract.rs index fa2cb5bf..f7905281 100644 --- a/solana/programs/gateway/src/processor/call_contract.rs +++ b/solana/programs/gateway/src/processor/call_contract.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use program_utils::ValidPDA; use solana_program::account_info::{next_account_info, AccountInfo}; @@ -14,8 +13,8 @@ impl Processor { pub fn process_call_contract( program_id: &Pubkey, accounts: &[AccountInfo<'_>], - destination_chain: Vec, - destination_contract_address: Vec, + destination_chain: String, + destination_contract_address: String, payload: Vec, ) -> ProgramResult { let accounts_iter = &mut accounts.iter(); @@ -31,10 +30,10 @@ impl Processor { destination_chain, payload, payload_hash, - sender: *sender.key, + sender: sender.key.to_bytes(), destination_address: destination_contract_address, }; - let event = GatewayEvent::CallContract(Cow::Borrowed(&call_contract)); + let event = GatewayEvent::CallContract(call_contract); event.emit()?; Ok(()) } diff --git a/solana/programs/gateway/src/processor/initialize_config.rs b/solana/programs/gateway/src/processor/initialize_config.rs index de5025e0..1f63447a 100644 --- a/solana/programs/gateway/src/processor/initialize_config.rs +++ b/solana/programs/gateway/src/processor/initialize_config.rs @@ -1,4 +1,4 @@ -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use itertools::Itertools; use program_utils::ValidPDA; use solana_program::account_info::{next_account_info, AccountInfo}; diff --git a/solana/programs/gateway/src/processor/rotate_signers.rs b/solana/programs/gateway/src/processor/rotate_signers.rs index 579579f7..bca9e944 100644 --- a/solana/programs/gateway/src/processor/rotate_signers.rs +++ b/solana/programs/gateway/src/processor/rotate_signers.rs @@ -1,6 +1,3 @@ -use std::borrow::Cow; - -use axelar_rkyv_encoding::types::ArchivedVerifierSet; use program_utils::ValidPDA; use solana_program::account_info::{next_account_info, AccountInfo}; use solana_program::entrypoint::ProgramResult; @@ -12,10 +9,10 @@ use solana_program::sysvar::Sysvar; use super::Processor; use crate::axelar_auth_weighted::SignerSetMetadata; -use crate::events::GatewayEvent; +use crate::events::{GatewayEvent, RotateSignersEvent}; use crate::state::execute_data::{ArchivedGatewayExecuteData, RotateSignersVariant}; use crate::state::verifier_set_tracker::VerifierSetTracker; -use crate::state::{GatewayConfig}; +use crate::state::GatewayConfig; use crate::{assert_valid_verifier_set_tracker_pda, seed_prefixes}; impl Processor { @@ -139,7 +136,12 @@ impl Processor { )?; // Emit event if the signers were rotated - emit_signers_rotated_event(new_verifier_set)?; + GatewayEvent::SignersRotated(RotateSignersEvent { + new_epoch: new_verifier_set_tracker.epoch, + new_signers_hash: new_verifier_set_tracker.verifier_set_hash, + execute_data_pda: gateway_approve_messages_execute_data_pda.key.to_bytes(), + }) + .emit()?; // Store the gateway data back to the account. let mut data = gateway_root_pda.try_borrow_mut_data()?; @@ -158,11 +160,3 @@ impl Processor { Ok(secs_since_last_rotation >= config.auth_weighted.minimum_rotation_delay) } } - -/// FIXME: Temporary workaround to emit a 'SignersRotated' event without -/// breaking the public API (currently used by AMPD and the Relayer). Once we -/// use 'axelar-rkyv-encoding' types across all APIs this can be revisited and -/// adjusted. -fn emit_signers_rotated_event(_verifier_set: &ArchivedVerifierSet) -> Result<(), ProgramError> { - GatewayEvent::SignersRotated(Cow::Owned(())).emit() -} diff --git a/solana/programs/gateway/src/processor/transfer_operatorship.rs b/solana/programs/gateway/src/processor/transfer_operatorship.rs index 8459c485..09ec72e3 100644 --- a/solana/programs/gateway/src/processor/transfer_operatorship.rs +++ b/solana/programs/gateway/src/processor/transfer_operatorship.rs @@ -8,6 +8,7 @@ use solana_program::program_pack::Pack; use solana_program::pubkey::Pubkey; use super::Processor; +use crate::events::GatewayEvent; use crate::state::GatewayConfig; impl Processor { @@ -83,6 +84,12 @@ impl Processor { let mut data = gateway_root_pda.try_borrow_mut_data()?; gateway_config.pack_into_slice(&mut data); + // Emit an event + GatewayEvent::OperatorshipTransferred(crate::events::OperatorshipTransferred { + operator: new_operator.key.to_bytes(), + }) + .emit()?; + Ok(()) } } diff --git a/solana/programs/gateway/src/processor/validate_message.rs b/solana/programs/gateway/src/processor/validate_message.rs index 1db97cf0..4e467915 100644 --- a/solana/programs/gateway/src/processor/validate_message.rs +++ b/solana/programs/gateway/src/processor/validate_message.rs @@ -7,6 +7,7 @@ use solana_program::pubkey::Pubkey; use super::Processor; use crate::commands::{AxelarMessage, Command}; +use crate::events::GatewayEvent; use crate::state::{GatewayApprovedCommand, GatewayConfig}; impl Processor { @@ -43,6 +44,9 @@ impl Processor { let mut data = approved_message_pda.try_borrow_mut_data()?; approved_message.pack_into_slice(&mut data); + // Emit an event + GatewayEvent::MessageExecuted(crate::events::MessageExecuted { command_id }).emit()?; + Ok(()) } } diff --git a/solana/programs/gateway/src/state/verifier_set_tracker.rs b/solana/programs/gateway/src/state/verifier_set_tracker.rs index a100d3f7..16067341 100644 --- a/solana/programs/gateway/src/state/verifier_set_tracker.rs +++ b/solana/programs/gateway/src/state/verifier_set_tracker.rs @@ -1,7 +1,7 @@ //! Module for the `VerifierSetTracker` account type. use std::mem::size_of; -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::msg; use solana_program::program_error::ProgramError; diff --git a/solana/programs/gateway/tests/module/approve_messages.rs b/solana/programs/gateway/tests/module/approve_messages.rs index edbb2a9d..ff27134f 100644 --- a/solana/programs/gateway/tests/module/approve_messages.rs +++ b/solana/programs/gateway/tests/module/approve_messages.rs @@ -1,4 +1,4 @@ -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use axelar_rkyv_encoding::types::{ArchivedExecuteData, ExecuteData, Payload}; use gmp_gateway::state::GatewayApprovedCommand; use itertools::Itertools; @@ -9,9 +9,9 @@ use test_fixtures::test_setup::{ }; use crate::{ - get_approved_command, get_gateway_events, get_gateway_events_from_execute_data, make_message, - make_messages, make_payload_and_commands, payload_and_commands, - prepare_questionable_execute_data, + get_approve_messages_gateway_events_from_execute_data, get_approved_command, + get_gateway_events, make_message, make_messages, make_payload_and_commands, + payload_and_commands, prepare_questionable_execute_data, }; #[tokio::test] @@ -69,8 +69,8 @@ async fn successfully_approves_commands_when_there_are_3_validate_message_comman .await; let (payload, commands) = make_payload_and_commands(3); - let (execute_data_pda, _) = fixture - .init_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) + let (execute_data_pda, _, execute_data_pda_contents) = fixture + .init_approve_messages_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) .await; let gateway_approved_command_pdas = fixture @@ -91,11 +91,14 @@ async fn successfully_approves_commands_when_there_are_3_validate_message_comman assert!(tx.result.is_ok()); // - events get emitted let emitted_events = get_gateway_events(&tx); - let expected_approved_command_logs = get_gateway_events_from_execute_data(&commands); + let expected_approved_command_logs = + get_approve_messages_gateway_events_from_execute_data(&execute_data_pda_contents); for (actual, expected) in emitted_events .iter() .zip(expected_approved_command_logs.iter()) { + let actual = actual.parse(); + let expected = expected.parse(); assert_eq!(actual, expected); } @@ -574,13 +577,11 @@ async fn fail_if_signer_set_epoch_is_older_than_4() { // to register the next latest signer set. We iterate over all signer sets, // calling "rotate signer" with the last known signer set to register // the next latest signer set. - dbg!("rotating singers"); for (idx, (current_signers, new_signers)) in ([&initial_signers].into_iter().chain(new_signer_sets.iter())) .tuple_windows::<(_, _)>() .enumerate() { - dbg!("rotate idx", &idx); let new_epoch = U256::from((idx + 1) as u128); let root_pda_data = fixture .get_account::(&gateway_root_pda, &gmp_gateway::ID) @@ -596,7 +597,6 @@ async fn fail_if_signer_set_epoch_is_older_than_4() { ) .await; } - dbg!("signers rotated"); // Now we have registered 5 sets in total (1 initial signer set + 4 that we // generated). The "epoch" is an incremental counter. But the data structure diff --git a/solana/programs/gateway/tests/module/initialize_execute_data.rs b/solana/programs/gateway/tests/module/initialize_execute_data.rs index c191374a..de378828 100644 --- a/solana/programs/gateway/tests/module/initialize_execute_data.rs +++ b/solana/programs/gateway/tests/module/initialize_execute_data.rs @@ -34,7 +34,7 @@ async fn test_successfylly_initialize_execute_data() { let payload = Payload::new_messages(make_messages(1)); let (raw_execute_data, _) = prepare_execute_data(payload, &signers, &domain_separator); let gateway_execute_data = GatewayExecuteData::::new( - &raw_execute_data, + &raw_execute_data.to_bytes::<0>().unwrap(), &gateway_root_pda, &domain_separator, ) @@ -53,7 +53,7 @@ async fn test_successfylly_initialize_execute_data() { fixture.payer.pubkey(), gateway_root_pda, &domain_separator, - &raw_execute_data, + &raw_execute_data.to_bytes::<0>().unwrap(), ) .unwrap() .0, @@ -96,7 +96,7 @@ async fn test_succesfully_initialize_rotate_signers() { let (raw_execute_data, _) = prepare_execute_data(payload, &signers, &domain_separator); let gateway_execute_data = GatewayExecuteData::::new( - &raw_execute_data, + &raw_execute_data.to_bytes::<0>().unwrap(), &gateway_root_pda, &domain_separator, ) @@ -111,7 +111,7 @@ async fn test_succesfully_initialize_rotate_signers() { fixture.payer.pubkey(), gateway_root_pda, &domain_separator, - &raw_execute_data, + &raw_execute_data.to_bytes::<0>().unwrap(), ) .expect("failed to create initialize_execute_data instruction"); @@ -171,7 +171,7 @@ async fn test_fail_on_invalid_root_pda() { fixture.payer.pubkey(), fake_gateway_root_pda, &domain_separator, - &raw_execute_data, + &raw_execute_data.to_bytes::<0>().unwrap(), ) .expect("failed to create initialize_execute_data instruction"); @@ -223,7 +223,7 @@ async fn test_fail_on_invalid_root_pda_owned_by_system_program() { fake_gateway_root_pda, // gateway_root_pda, &domain_separator, - &raw_execute_data, + &raw_execute_data.to_bytes::<0>().unwrap(), ) .expect("failed to create initialize_execute_data instruction"); let BanksTransactionResultWithMetadata { metadata, result } = fixture @@ -260,7 +260,7 @@ async fn test_fail_on_uninitialized_root_pda() { fixture.payer.pubkey(), uninitialized_gateway_config_pda, &domain_separator, - &raw_execute_data, + &raw_execute_data.to_bytes::<0>().unwrap(), ) .expect("failed to create initialize_execute_data instruction"); diff --git a/solana/programs/gateway/tests/module/main.rs b/solana/programs/gateway/tests/module/main.rs index 05375920..96b754d7 100644 --- a/solana/programs/gateway/tests/module/main.rs +++ b/solana/programs/gateway/tests/module/main.rs @@ -10,16 +10,18 @@ mod rotate_signers; mod transfer_operatorship; use std::collections::BTreeMap; +use std::str::FromStr; -use axelar_message_primitives::{DataPayload, EncodingScheme}; +use axelar_message_primitives::{DataPayload, EncodingScheme, U256}; use axelar_rkyv_encoding::hash_payload; use axelar_rkyv_encoding::types::{ ExecuteData, Message, Payload, Proof, PublicKey, WeightedSigner, }; use gmp_gateway::commands::OwnedCommand; -use gmp_gateway::events::GatewayEvent; +use gmp_gateway::events::{EventContainer, GatewayEvent}; use gmp_gateway::hasher_impl; -use gmp_gateway::state::GatewayApprovedCommand; +use gmp_gateway::state::execute_data::{ApproveMessagesVariant, RotateSignersVariant}; +use gmp_gateway::state::{GatewayApprovedCommand, GatewayExecuteData}; use solana_program_test::{processor, ProgramTest}; use solana_sdk::instruction::AccountMeta; use solana_sdk::pubkey::Pubkey; @@ -70,18 +72,51 @@ pub fn gateway_approved_command_ixs( ixs } -fn get_gateway_events_from_execute_data(commands: &[OwnedCommand]) -> Vec> { - commands +fn get_approve_messages_gateway_events_from_execute_data( + execute_data: &GatewayExecuteData, +) -> Vec { + execute_data + .data .iter() - .cloned() - .map(gmp_gateway::events::GatewayEvent::try_from) - .collect::, _>>() - .expect("failed to parse events from execute_data") + .map(|x| { + let event = GatewayEvent::MessageApproved(gmp_gateway::events::MessageApproved { + command_id: x.cc_id().command_id(hasher_impl()), + source_chain: x.cc_id().chain().to_owned().into_bytes(), + message_id: x.cc_id().id().to_owned().into_bytes(), + source_address: x.source_address().into(), + destination_address: Pubkey::from_str(x.destination_address()) + .unwrap() + .to_bytes(), + payload_hash: *x.payload_hash(), + }); + let vec = event.encode(); + EventContainer::new(vec.to_vec()).unwrap() + }) + .collect() +} + +fn get_rotate_signers_gateway_events_from_execute_data( + execute_data: GatewayExecuteData, + gateway_root_pda: &Pubkey, + expected_epoch: U256, +) -> EventContainer { + let event = GatewayEvent::SignersRotated(gmp_gateway::events::RotateSignersEvent { + new_epoch: expected_epoch, + new_signers_hash: execute_data.data.hash(hasher_impl()), + execute_data_pda: gmp_gateway::get_execute_data_pda( + gateway_root_pda, + &execute_data.hash_decoded_contents(), + ) + .0 + .to_bytes(), + }); + let vec = event.encode(); + EventContainer::new(vec.to_vec()).unwrap() } fn get_gateway_events( tx: &solana_program_test::BanksTransactionResultWithMetadata, -) -> Vec> { +) -> Vec { tx.metadata .as_ref() .unwrap() diff --git a/solana/programs/gateway/tests/module/rotate_signers.rs b/solana/programs/gateway/tests/module/rotate_signers.rs index fa855763..a460384c 100644 --- a/solana/programs/gateway/tests/module/rotate_signers.rs +++ b/solana/programs/gateway/tests/module/rotate_signers.rs @@ -12,7 +12,7 @@ use test_fixtures::test_setup::{ }; use crate::{ - get_gateway_events, get_gateway_events_from_execute_data, make_messages, + get_gateway_events, get_rotate_signers_gateway_events_from_execute_data, make_messages, make_payload_and_commands, }; @@ -39,10 +39,10 @@ async fn successfully_rotates_signers() { .setup() .await; let new_signer_set = make_signers(&[500, 200], 1); - let (payload, command) = payload_and_command(&new_signer_set.verifier_set()); + let (payload, _command) = payload_and_command(&new_signer_set.verifier_set()); - let (execute_data_pda, _) = fixture - .init_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) + let (execute_data_pda, _execute_data, pda_execute_data) = fixture + .init_rotate_signers_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) .await; // Action @@ -57,22 +57,25 @@ async fn successfully_rotates_signers() { // Assert assert!(tx.result.is_ok()); + let new_epoch: U256 = 2u128.into(); + // - expected events - let emitted_events = get_gateway_events(&tx); - let expected_approved_command_logs = get_gateway_events_from_execute_data(&command); - for (actual, expected) in emitted_events - .iter() - .zip(expected_approved_command_logs.iter()) - { - assert_eq!(actual, expected); - } + let emitted_event = get_gateway_events(&tx).pop().unwrap(); + let expected_event = get_rotate_signers_gateway_events_from_execute_data( + pda_execute_data, + &gateway_root_pda, + new_epoch, + ); + assert_eq!(emitted_event, expected_event); // - signers have been updated let root_pda_data = fixture .get_account::(&gateway_root_pda, &gmp_gateway::ID) .await; - let new_epoch: U256 = 2u128.into(); - assert_eq!(root_pda_data.auth_weighted.current_epoch(), new_epoch); + assert_eq!( + root_pda_data.auth_weighted.current_epoch(), + new_epoch.clone() + ); // todo -- assert that the signer tracker pda has been initialized // - test that both signer sets can sign new messages @@ -182,10 +185,10 @@ async fn succeed_if_signer_set_signed_by_old_signer_set_and_submitted_by_the_ope .await; let newer_signer_set = make_signers(&[500, 200], 2); - let (payload, command) = payload_and_command(&new_signer_set.verifier_set()); + let (payload, _command) = payload_and_command(&new_signer_set.verifier_set()); // we stil use the initial signer set to sign the data (the `signers` variable) - let (execute_data_pda, _) = fixture - .init_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) + let (execute_data_pda, _, pda_execute_data) = fixture + .init_rotate_signers_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) .await; // Action @@ -207,21 +210,23 @@ async fn succeed_if_signer_set_signed_by_old_signer_set_and_submitted_by_the_ope // Assert assert!(tx.result.is_ok()); - let emitted_events = get_gateway_events(&tx); - let expected_approved_command_logs = get_gateway_events_from_execute_data(&command); - for (actual, expected) in emitted_events - .iter() - .zip(expected_approved_command_logs.iter()) - { - assert_eq!(actual, expected); - } + let new_epoch: U256 = 3_u128.into(); + let emitted_event = get_gateway_events(&tx).pop().unwrap(); + let expected_event = get_rotate_signers_gateway_events_from_execute_data( + pda_execute_data, + &gateway_root_pda, + new_epoch, + ); + assert_eq!(emitted_event, expected_event); // - signers have been updated let root_pda_data = fixture .get_account::(&gateway_root_pda, &gmp_gateway::ID) .await; - let new_epoch: U256 = 3u128.into(); - assert_eq!(root_pda_data.auth_weighted.current_epoch(), new_epoch); + assert_eq!( + root_pda_data.auth_weighted.current_epoch(), + new_epoch.clone() + ); // todo -- assert verifier set pda } diff --git a/solana/xtask/src/cli/cmd/solana.rs b/solana/xtask/src/cli/cmd/solana.rs index 21b5b1d5..06837904 100644 --- a/solana/xtask/src/cli/cmd/solana.rs +++ b/solana/xtask/src/cli/cmd/solana.rs @@ -5,7 +5,7 @@ use std::fmt::Display; use std::path::{Path, PathBuf}; use std::str::FromStr; -use axelar_message_primitives::command::U256; +use axelar_message_primitives::U256; use axelar_rkyv_encoding::types::{PublicKey, VerifierSet, U128}; use gmp_gateway::axelar_auth_weighted::RotationDelaySecs; use gmp_gateway::instructions::{InitializeConfig, VerifierSetWraper}; diff --git a/solana/xtask/src/cli/cmd/solana/message_limits.rs b/solana/xtask/src/cli/cmd/solana/message_limits.rs index 16b4a515..527159a1 100644 --- a/solana/xtask/src/cli/cmd/solana/message_limits.rs +++ b/solana/xtask/src/cli/cmd/solana/message_limits.rs @@ -4,8 +4,7 @@ use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use axelar_message_primitives::command::U256; -use axelar_message_primitives::{DataPayload, EncodingScheme}; +use axelar_message_primitives::{DataPayload, EncodingScheme, U256}; use axelar_rkyv_encoding::test_fixtures::random_weight; use axelar_rkyv_encoding::types::{HasheableMessageVec, Message, Payload}; use derive_builder::Builder; @@ -295,6 +294,7 @@ async fn do_init_approve_messages_execute_data( ) -> Result<(Pubkey, u64), Error> { let (raw_execute_data, _) = prepare_execute_data(payload, inputs.signers.as_ref(), &DOMAIN_SEPARATOR); + let raw_execute_data = raw_execute_data.to_bytes::<0>().unwrap(); let execute_data = GatewayExecuteData::::new( &raw_execute_data, inputs.gateway_config_pda.as_ref(), diff --git a/solana/xtask/src/cli/cmd/testnet/solana_interactions.rs b/solana/xtask/src/cli/cmd/testnet/solana_interactions.rs index 6375444e..4b66a772 100644 --- a/solana/xtask/src/cli/cmd/testnet/solana_interactions.rs +++ b/solana/xtask/src/cli/cmd/testnet/solana_interactions.rs @@ -1,9 +1,9 @@ -use std::borrow::Cow; use std::str::FromStr; use axelar_message_primitives::DataPayload; use axelar_wasm_std::nonempty; use gmp_gateway::commands::OwnedCommand; +use gmp_gateway::events::ArchivedGatewayEvent; use gmp_gateway::state::GatewayApprovedCommand; use router_api::{Address, ChainName, CrossChainId}; use solana_sdk::commitment_config::CommitmentConfig; @@ -31,8 +31,8 @@ pub(crate) fn send_memo_from_solana( gateway_root_pda, &solana_keypair.pubkey(), memo.to_string(), - destination_chain.id.clone().into_bytes(), - ethers::utils::to_checksum(&destination_memo_contract, None).into_bytes(), + destination_chain.id.clone(), + ethers::utils::to_checksum(&destination_memo_contract, None), ) .unwrap(), ], @@ -64,26 +64,24 @@ pub(crate) fn send_memo_from_solana( .enumerate() .find_map(|(idx, log)| gmp_gateway::events::GatewayEvent::parse_log(log).map(|x| (idx, x))) .expect("Gateway event was not emitted (or we couldn't parse it)?"); - let gmp_gateway::events::GatewayEvent::CallContract(Cow::Owned(call_contract)) = gateway_event - else { + let ArchivedGatewayEvent::CallContract(call_contract) = gateway_event.parse() else { panic!("Expected CallContract event, got {gateway_event:?}"); }; - let payload = call_contract.payload; + let payload = call_contract.payload.to_vec(); let signature = signature.to_string(); let message = router_api::Message { cc_id: CrossChainId { chain: ChainName::from_str(solana_chain_id).unwrap(), id: nonempty::String::from_str(&format!("{signature}-{event_idx}")).unwrap(), }, - source_address: Address::from_str(call_contract.sender.to_string().as_str()).unwrap(), - destination_chain: ChainName::from_str( - String::from_utf8_lossy(&call_contract.destination_chain).as_ref(), - ) - .unwrap(), - destination_address: Address::from_str( - String::from_utf8_lossy(&call_contract.destination_address).as_ref(), + source_address: Address::from_str( + solana_sdk::pubkey::Pubkey::from(call_contract.sender) + .to_string() + .as_str(), ) .unwrap(), + destination_chain: ChainName::from_str(call_contract.destination_chain.as_str()).unwrap(), + destination_address: Address::from_str(call_contract.destination_address.as_str()).unwrap(), payload_hash: call_contract.payload_hash, };