From da684d8d0817154f09a764ae692874435c20a479 Mon Sep 17 00:00:00 2001 From: haiyizxx Date: Thu, 16 May 2024 12:56:52 -0400 Subject: [PATCH] feat(minor-ampd): confirm SignersRotated event (#405) * confirm amplifier gateway SignersRotated event * address comments --- .cargo/config | 2 +- Cargo.lock | 5 + ampd/Cargo.toml | 1 + ampd/src/evm/abi/IAxelarGateway.json | 963 --------------------- ampd/src/evm/verifier.rs | 125 ++- ampd/src/handlers/evm_verify_worker_set.rs | 15 +- packages/axelar-wasm-std/src/operators.rs | 4 + packages/evm-gateway/Cargo.toml | 4 + packages/evm-gateway/build.rs | 2 +- packages/evm-gateway/src/lib.rs | 80 +- 10 files changed, 158 insertions(+), 1043 deletions(-) delete mode 100644 ampd/src/evm/abi/IAxelarGateway.json diff --git a/.cargo/config b/.cargo/config index 3090f7044..b9f0cd28e 100644 --- a/.cargo/config +++ b/.cargo/config @@ -6,5 +6,5 @@ unit-test = "test --lib" rustflags = ["--cfg", "tracing_unstable"] [env] -SOLIDITY_GATEWAY_VERSION = "v5.8.0" +SOLIDITY_GATEWAY_VERSION = "v5.9.0" SOLIDITY_RELEASES_URL = "https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/releases/download" diff --git a/Cargo.lock b/Cargo.lock index 27acacef2..8cfc41371 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,6 +233,7 @@ dependencies = [ "ethers", "events", "events-derive", + "evm-gateway", "futures", "generic-array", "hex", @@ -3028,10 +3029,14 @@ dependencies = [ name = "evm-gateway" version = "0.1.0" dependencies = [ + "axelar-wasm-std", + "cosmwasm-std", "ethers", "reqwest 0.12.4", "serde", "serde_json", + "sha3 0.10.8", + "thiserror", "zip 1.2.1", ] diff --git a/ampd/Cargo.toml b/ampd/Cargo.toml index 94975ad65..aba6d362a 100644 --- a/ampd/Cargo.toml +++ b/ampd/Cargo.toml @@ -24,6 +24,7 @@ error-stack = { workspace = true } ethers = { version = "2.0.8", features = ["rustls"] } events = { workspace = true } events-derive = { workspace = true } +evm-gateway = { workspace = true } futures = "0.3.25" hex = { version = "0.4.3", features = ["serde"] } humantime-serde = "1.1.1" diff --git a/ampd/src/evm/abi/IAxelarGateway.json b/ampd/src/evm/abi/IAxelarGateway.json deleted file mode 100644 index 6e0e508ef..000000000 --- a/ampd/src/evm/abi/IAxelarGateway.json +++ /dev/null @@ -1,963 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "IAxelarGateway", - "sourceName": "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "BurnFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "ExceedMintLimit", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAuthModule", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidChainId", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCodeHash", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCommands", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidSetMintLimitsParams", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenDeployer", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "MintFailed", - "type": "error" - }, - { - "inputs": [], - "name": "NotProxy", - "type": "error" - }, - { - "inputs": [], - "name": "NotSelf", - "type": "error" - }, - { - "inputs": [], - "name": "SetupFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "TokenAlreadyExists", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "TokenContractDoesNotExist", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "TokenDeployFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "TokenDoesNotExist", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "destinationChain", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "destinationContractAddress", - "type": "string" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "name": "ContractCall", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "string", - "name": "sourceChain", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "sourceAddress", - "type": "string" - }, - { - "indexed": true, - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "sourceTxHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "sourceEventIndex", - "type": "uint256" - } - ], - "name": "ContractCallApproved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "string", - "name": "sourceChain", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "sourceAddress", - "type": "string" - }, - { - "indexed": true, - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "sourceTxHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "sourceEventIndex", - "type": "uint256" - } - ], - "name": "ContractCallApprovedWithMint", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "destinationChain", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "destinationContractAddress", - "type": "string" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "payload", - "type": "bytes" - }, - { - "indexed": false, - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ContractCallWithToken", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - } - ], - "name": "Executed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes", - "name": "newOperatorsData", - "type": "bytes" - } - ], - "name": "OperatorshipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "indexed": false, - "internalType": "address", - "name": "tokenAddresses", - "type": "address" - } - ], - "name": "TokenDeployed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "TokenMintLimitUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "destinationChain", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "destinationAddress", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "TokenSent", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "inputs": [], - "name": "adminEpoch", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "adminThreshold", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "admins", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "allTokensFrozen", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "authModule", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "destinationChain", - "type": "string" - }, - { - "internalType": "string", - "name": "contractAddress", - "type": "string" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "name": "callContract", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "destinationChain", - "type": "string" - }, - { - "internalType": "string", - "name": "contractAddress", - "type": "string" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "callContractWithToken", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "input", - "type": "bytes" - } - ], - "name": "execute", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "implementation", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - } - ], - "name": "isCommandExecuted", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "sourceChain", - "type": "string" - }, - { - "internalType": "string", - "name": "sourceAddress", - "type": "string" - }, - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "isContractCallAndMintApproved", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "sourceChain", - "type": "string" - }, - { - "internalType": "string", - "name": "sourceAddress", - "type": "string" - }, - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - } - ], - "name": "isContractCallApproved", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "destinationChain", - "type": "string" - }, - { - "internalType": "string", - "name": "destinationAddress", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "sendToken", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string[]", - "name": "symbols", - "type": "string[]" - }, - { - "internalType": "uint256[]", - "name": "limits", - "type": "uint256[]" - } - ], - "name": "setTokenMintLimits", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "params", - "type": "bytes" - } - ], - "name": "setup", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "tokenAddresses", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tokenDeployer", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "tokenFrozen", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "tokenMintAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "tokenMintLimit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "newImplementationCodeHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "setupParams", - "type": "bytes" - } - ], - "name": "upgrade", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "sourceChain", - "type": "string" - }, - { - "internalType": "string", - "name": "sourceAddress", - "type": "string" - }, - { - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - } - ], - "name": "validateContractCall", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "commandId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "sourceChain", - "type": "string" - }, - { - "internalType": "string", - "name": "sourceAddress", - "type": "string" - }, - { - "internalType": "bytes32", - "name": "payloadHash", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "validateContractCallAndMint", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x", - "deployedBytecode": "0x", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/ampd/src/evm/verifier.rs b/ampd/src/evm/verifier.rs index 71924e110..f5dca30a7 100644 --- a/ampd/src/evm/verifier.rs +++ b/ampd/src/evm/verifier.rs @@ -1,24 +1,21 @@ use axelar_wasm_std::voting::Vote; -use ethers::abi::{encode, Token}; use ethers::contract::EthLogDecode; -use ethers::prelude::abigen; use ethers::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_worker_set::WorkerSetConfirmation; use crate::types::EVMAddress; -abigen!(IAxelarGateway, "src/evm/abi/IAxelarGateway.json"); - -struct IAxelarGatewayEventsWithLog<'a>(&'a Log, IAxelarGatewayEvents); +struct IAxelarGatewayEventsWithLog<'a>(&'a Log, IAxelarAmplifierGatewayEvents); impl PartialEq> for &Message { fn eq(&self, other: &IAxelarGatewayEventsWithLog<'_>) -> bool { let IAxelarGatewayEventsWithLog(log, event) = other; match event { - IAxelarGatewayEvents::ContractCallFilter(event) => { + IAxelarAmplifierGatewayEvents::ContractCallFilter(event) => { log.transaction_hash == Some(self.tx_id) && event.sender == self.source_address && self.destination_chain == event.destination_chain @@ -33,28 +30,16 @@ impl PartialEq> for &Message { impl PartialEq> for &WorkerSetConfirmation { fn eq(&self, other: &IAxelarGatewayEventsWithLog<'_>) -> bool { let IAxelarGatewayEventsWithLog(log, event) = other; - match event { - IAxelarGatewayEvents::OperatorshipTransferredFilter(event) => { - let (operators, weights): (Vec<_>, Vec<_>) = self - .operators - .weights_by_addresses - .iter() - .map(|(operator, weight)| { - ( - Token::Address(operator.to_owned()), - Token::Uint(weight.as_ref().to_owned()), - ) - }) - .unzip(); + IAxelarAmplifierGatewayEvents::SignersRotatedFilter(event) => { + let weighted_signers = match WeightedSigners::try_from(&self.operators) { + Ok(signers) => signers, + Err(_) => return false, + }; log.transaction_hash == Some(self.tx_id) - && event.new_operators_data - == encode(&[ - Token::Array(operators), - Token::Array(weights), - Token::Uint(self.operators.threshold.as_ref().to_owned()), - ]) + && event.signers_hash == weighted_signers.hash() + && event.signers == weighted_signers.abi_encode() } _ => false, } @@ -77,7 +62,7 @@ fn get_event<'a>( .get(log_index) .filter(|log| log.address == *gateway_address) .and_then( - |log| match IAxelarGatewayEvents::decode_log(&log.clone().into()) { + |log| match IAxelarAmplifierGatewayEvents::decode_log(&log.clone().into()) { Ok(event) => Some(IAxelarGatewayEventsWithLog(log, event)), Err(_) => None, }, @@ -132,18 +117,23 @@ pub fn verify_worker_set( #[cfg(test)] mod tests { - use crate::evm::verifier::OperatorshipTransferredFilter; - use crate::handlers::evm_verify_msg::Message; - use crate::handlers::evm_verify_worker_set::{Operators, WorkerSetConfirmation}; - use axelar_wasm_std::voting::Vote; - use cosmwasm_std::Uint256; - use ethers::abi::{encode, Token}; - use ethers::contract::EthEvent; - use ethers::types::{Log, TransactionReceipt}; - - use super::i_axelar_gateway::ContractCallFilter; + use axelar_wasm_std::{operators::Operators, voting::Vote}; + use cosmwasm_std::{Uint128, Uint256}; + use ethers::{ + abi::{encode, Token}, + contract::EthEvent, + types::{Log, TransactionReceipt, H256}, + }; + use evm_gateway::{ + i_axelar_amplifier_gateway::{ContractCallFilter, SignersRotatedFilter}, + WeightedSigner, WeightedSigners, + }; + use super::{verify_message, verify_worker_set}; - use crate::types::{EVMAddress, Hash}; + use crate::{ + handlers::{evm_verify_msg::Message, evm_verify_worker_set::WorkerSetConfirmation}, + types::{EVMAddress, Hash}, + }; #[test] fn should_not_verify_worker_set_if_tx_id_does_not_match() { @@ -207,7 +197,7 @@ mod tests { let (gateway_address, tx_receipt, mut worker_set) = get_matching_worker_set_and_tx_receipt(); - worker_set.operators.threshold = Uint256::from(50u64).into(); + worker_set.operators.threshold = Uint256::from(50u64); assert_eq!( verify_worker_set(&gateway_address, &tx_receipt, &worker_set), Vote::NotFound @@ -305,42 +295,49 @@ mod tests { let log_index = 1; let gateway_address = EVMAddress::random(); + let operators = Operators::new( + vec![ + (EVMAddress::random().as_bytes().into(), Uint256::from(10u64)), + (EVMAddress::random().as_bytes().into(), Uint256::from(20u64)), + (EVMAddress::random().as_bytes().into(), Uint256::from(30u64)), + ], + Uint256::from(40u64), + 1u64, + ); + let worker_set = WorkerSetConfirmation { tx_id, event_index: log_index, - operators: Operators { - threshold: Uint256::from(40u64).into(), - weights_by_addresses: vec![ - (EVMAddress::random(), Uint256::from(10u64).into()), - (EVMAddress::random(), Uint256::from(20u64).into()), - (EVMAddress::random(), Uint256::from(30u64).into()), - ], - }, + operators, }; - let (operators, weights): (Vec<_>, Vec<_>) = worker_set - .operators - .weights_by_addresses - .iter() - .map(|(operator, weight)| { - ( - Token::Address(operator.to_owned()), - Token::Uint(weight.as_ref().to_owned()), - ) - }) - .unzip(); + + let weighted_signers = WeightedSigners { + threshold: 40, + nonce: Uint256::from(worker_set.operators.created_at).to_be_bytes(), + signers: worker_set + .operators + .weights_by_addresses() + .iter() + .map(|(operator, weight)| WeightedSigner { + signer: operator.to_hex().parse().unwrap(), + weight: Uint128::try_from(*weight).unwrap().u128(), + }) + .collect(), + }; + let log = Log { transaction_hash: Some(tx_id), log_index: Some(log_index.into()), address: gateway_address, - topics: vec![OperatorshipTransferredFilter::signature()], - data: encode(&[Token::Bytes(encode(&[ - Token::Array(operators), - Token::Array(weights), - Token::Uint(worker_set.operators.threshold.as_ref().to_owned()), - ]))]) - .into(), + topics: vec![ + SignersRotatedFilter::signature(), + H256::from_low_u64_be(1), + weighted_signers.hash().into(), + ], + data: encode(&[Token::Bytes(weighted_signers.abi_encode())]).into(), ..Default::default() }; + let tx_receipt = TransactionReceipt { transaction_hash: tx_id, status: Some(1u64.into()), diff --git a/ampd/src/handlers/evm_verify_worker_set.rs b/ampd/src/handlers/evm_verify_worker_set.rs index 0b4195a52..162876243 100644 --- a/ampd/src/handlers/evm_verify_worker_set.rs +++ b/ampd/src/handlers/evm_verify_worker_set.rs @@ -10,8 +10,11 @@ use tokio::sync::watch::Receiver; use tracing::{info, info_span}; use valuable::Valuable; -use axelar_wasm_std::msg_id::tx_hash_event_index::HexTxHashAndEventIndex; -use axelar_wasm_std::voting::{PollId, Vote}; +use axelar_wasm_std::{ + msg_id::tx_hash_event_index::HexTxHashAndEventIndex, + operators::Operators, + voting::{PollId, Vote}, +}; use events::Error::EventTypeMismatch; use events_derive::try_from; use router_api::ChainName; @@ -22,16 +25,10 @@ use crate::evm::finalizer::Finalization; use crate::evm::verifier::verify_worker_set; use crate::evm::{finalizer, json_rpc::EthereumClient}; use crate::handlers::errors::Error; -use crate::types::{EVMAddress, Hash, TMAddress, U256}; +use crate::types::{EVMAddress, Hash, TMAddress}; type Result = error_stack::Result; -#[derive(Deserialize, Debug)] -pub struct Operators { - pub weights_by_addresses: Vec<(EVMAddress, U256)>, - pub threshold: U256, -} - #[derive(Deserialize, Debug)] pub struct WorkerSetConfirmation { pub tx_id: Hash, diff --git a/packages/axelar-wasm-std/src/operators.rs b/packages/axelar-wasm-std/src/operators.rs index e4d88e2fd..824b7bba3 100644 --- a/packages/axelar-wasm-std/src/operators.rs +++ b/packages/axelar-wasm-std/src/operators.rs @@ -36,6 +36,10 @@ impl Operators { hasher.update(self.created_at.to_be_bytes()); hasher.finalize().into() } + + pub fn weights_by_addresses(&self) -> &Vec<(HexBinary, Uint256)> { + &self.weights_by_addresses + } } #[cfg(test)] diff --git a/packages/evm-gateway/Cargo.toml b/packages/evm-gateway/Cargo.toml index e77456f9c..b71f7beaa 100644 --- a/packages/evm-gateway/Cargo.toml +++ b/packages/evm-gateway/Cargo.toml @@ -6,7 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axelar-wasm-std = { workspace = true } +cosmwasm-std = { workspace = true } ethers = { version = "2.0.14", features = ["abigen"] } +sha3 ={ workspace = true } +thiserror = { workspace = true } [build-dependencies] reqwest = { version = "0.12.4", features = ["blocking", "json"] } diff --git a/packages/evm-gateway/build.rs b/packages/evm-gateway/build.rs index da20eeefa..080dc4b79 100644 --- a/packages/evm-gateway/build.rs +++ b/packages/evm-gateway/build.rs @@ -6,7 +6,7 @@ use std::path::{Path, PathBuf}; use reqwest::blocking::Client; use zip::ZipArchive; -const ABI_FILES: [&str; 2] = ["IAxelarAmplifierGateway.json", "IBaseWeightedMultisig.json"]; +const ABI_FILES: [&str; 1] = ["IAxelarAmplifierGateway.json"]; const OUTPUT_DIR_BASE: &str = "src/abi"; // Base output directory const VERSION: &str = env!( diff --git a/packages/evm-gateway/src/lib.rs b/packages/evm-gateway/src/lib.rs index 652b6bc63..ea21ceaa0 100644 --- a/packages/evm-gateway/src/lib.rs +++ b/packages/evm-gateway/src/lib.rs @@ -1,10 +1,80 @@ -use ethers::prelude::abigen; +use std::str::FromStr; +use cosmwasm_std::{HexBinary, Uint128, Uint256}; +use ethers::{ + abi::{encode, Token::Tuple, Tokenize}, + prelude::abigen, + types::Address, +}; +use sha3::{Digest, Keccak256}; +use thiserror::Error; + +use axelar_wasm_std::{hash::Hash, operators::Operators}; + +// Generates the bindings for the Axelar Amplifier Gateway contract. +// This includes the defined structs: Messages, WeightedSigners, WeightedSigner, and Proofs. abigen!( IAxelarAmplifierGateway, "src/abi/$SOLIDITY_GATEWAY_VERSION/IAxelarAmplifierGateway.json" ); -abigen!( - IBaseWeightedMultisig, - "src/abi/$SOLIDITY_GATEWAY_VERSION/IBaseWeightedMultisig.json" -); + +#[derive(Error, Debug)] +pub enum Error { + #[error("address is invalid: {reason}")] + InvalidAddress { reason: String }, +} + +impl TryFrom<&Operators> for WeightedSigners { + type Error = Error; + + fn try_from(operators: &Operators) -> Result { + let signers = operators + .weights_by_addresses() + .iter() + .map(WeightedSigner::try_from) + .collect::, _>>()?; + + let threshold: Uint128 = operators + .threshold + .try_into() + .expect("weight is too large to convert to Uint128"); + + Ok(WeightedSigners { + signers, + threshold: threshold.u128(), + nonce: Uint256::from(operators.created_at).to_be_bytes(), + }) + } +} + +impl TryFrom<&(HexBinary, Uint256)> for WeightedSigner { + type Error = Error; + + fn try_from((address, weight): &(HexBinary, Uint256)) -> Result { + let weight: Uint128 = (*weight) + .try_into() + .expect("weight is too large to convert to Uint128"); + + let address = + Address::from_str(&address.to_hex()).map_err(|err| Error::InvalidAddress { + reason: err.to_string(), + })?; + + Ok(WeightedSigner { + signer: address, + weight: weight.u128(), + }) + } +} + +impl WeightedSigners { + pub fn abi_encode(&self) -> Vec { + let tokens = self.clone().into_tokens(); + + encode(&[Tuple(tokens)]) + } + + pub fn hash(&self) -> Hash { + Keccak256::digest(self.abi_encode()).into() + } +}