Skip to content

Commit

Permalink
add mock for axelar-router
Browse files Browse the repository at this point in the history
  • Loading branch information
lemunozm committed Aug 12, 2024
1 parent bdd02e0 commit 329c861
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 90 deletions.
37 changes: 37 additions & 0 deletions libs/mocks/src/ethereum_transactor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use cfg_traits::ethereum::EthereumTransactor;
use frame_support::pallet_prelude::*;
use mock_builder::{execute_call, register_call};
use sp_core::{H160, U256};

#[pallet::config]
pub trait Config: frame_system::Config {}

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::storage]
type CallIds<T: Config> = StorageMap<_, _, String, mock_builder::CallId>;

impl<T: Config> Pallet<T> {
pub fn mock_call(
func: impl Fn(H160, H160, &[u8], U256, U256, U256) -> DispatchResult + 'static,
) {
register_call!(move |(a, b, c, d, e, f)| func(a, b, c, d, e, f));
}
}

impl<T: Config> EthereumTransactor for Pallet<T> {
fn call(
a: H160,
b: H160,
c: &[u8],
d: U256,
e: U256,
f: U256,
) -> DispatchResultWithPostInfo {
execute_call!((a, b, c, d, e, f))
}
}
}
2 changes: 2 additions & 0 deletions libs/mocks/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod change_guard;
pub mod converter;
pub mod currency_conversion;
pub mod data;
pub mod ethereum_transactor;
pub mod fees;
pub mod foreign_investment;
pub mod foreign_investment_hooks;
Expand All @@ -11,6 +12,7 @@ pub mod liquidity_pools;
pub mod liquidity_pools_gateway;
pub mod liquidity_pools_gateway_queue;
pub mod liquidity_pools_gateway_routers;
pub mod message_receiver;
pub mod pay_fee;
pub mod permissions;
pub mod pools;
Expand Down
35 changes: 35 additions & 0 deletions libs/mocks/src/message_receiver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use cfg_traits::liquidity_pools::MessageReceiver;
use frame_support::pallet_prelude::*;
use mock_builder::{execute_call, register_call};

#[pallet::config]
pub trait Config: frame_system::Config {
type Middleware;
type Origin;
}

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::storage]
type CallIds<T: Config> = StorageMap<_, _, String, mock_builder::CallId>;

impl<T: Config> Pallet<T> {
pub fn mock_receive(
f: impl Fn(T::Middleware, T::Origin, Vec<u8>) -> DispatchResult + 'static,
) {
register_call!(move |(a, b, c)| f(a, b, c));
}
}

impl<T: Config> MessageReceiver for Pallet<T> {
type Middleware = T::Middleware;
type Origin = T::Origin;

fn receive(a: Self::Middleware, b: Self::Origin, c: Vec<u8>) -> DispatchResult {
execute_call!((a, b, c))
}
}
}
7 changes: 1 addition & 6 deletions libs/traits/src/liquidity_pools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@

use frame_support::{
dispatch::{DispatchResult, DispatchResultWithPostInfo},
traits::Get,
weights::Weight,
BoundedVec,
};
use sp_runtime::DispatchError;
use sp_std::vec::Vec;
Expand Down Expand Up @@ -80,14 +78,11 @@ pub trait MessageReceiver {
/// The originator of the received message
type Origin;

/// The maximum lenght for a message the implementor is able to receive.
type MaxEncodedLen: Get<u32>;

/// Sends a message for origin to destination
fn receive(
middleware: Self::Middleware,
origin: Self::Origin,
message: BoundedVec<u8, Self::MaxEncodedLen>,
message: Vec<u8>,
) -> DispatchResult;
}

Expand Down
165 changes: 81 additions & 84 deletions pallets/axelar-router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cfg_traits::{
};
use cfg_types::{domain_address::DomainAddress, EVMChainId};
use ethabi::{Contract, Function, Param, ParamType, Token};
use fp_evm::{ExitError, PrecompileFailure, PrecompileHandle};
use fp_evm::{ExitError, PrecompileHandle};
use frame_support::{
pallet_prelude::*,
weights::{constants::RocksDbWeight, Weight},
Expand All @@ -17,6 +17,12 @@ use precompile_utils::prelude::*;
use sp_core::{H160, H256, U256};
use sp_std::collections::btree_map::BTreeMap;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

/// Maximum size allowed for a byte representation of an Axelar EVM chain
/// string, as found below:
/// <https://docs.axelar.dev/dev/reference/mainnet-chain-names>
Expand Down Expand Up @@ -86,77 +92,6 @@ pub struct FeeValues {
pub gas_limit: U256,
}

/// Encodes the provided message into the format required for submitting it
/// to the Axelar contract which in turn calls the LiquidityPools
/// contract with the serialized LP message as `payload`.
///
/// Axelar contract call:
/// <https://github.com/axelarnetwork/axelar-cgp-solidity/blob/v4.3.2/contracts/AxelarGateway.sol#L78>
///
/// LiquidityPools contract call:
/// <https://github.com/centrifuge/liquidity-pools/blob/383d279f809a01ab979faf45f31bf9dc3ce6a74a/src/routers/Gateway.sol#L276>
fn wrap_into_axelar_msg(
serialized_msg: Vec<u8>,
target_chain: Vec<u8>,
target_contract: H160,
) -> Result<Vec<u8>, &'static str> {
const AXELAR_FUNCTION_NAME: &str = "callContract";
const AXELAR_DESTINATION_CHAIN_PARAM: &str = "destinationChain";
const AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM: &str = "destinationContractAddress";
const AXELAR_PAYLOAD_PARAM: &str = "payload";

#[allow(deprecated)]
let encoded_axelar_contract = Contract {
constructor: None,
functions: BTreeMap::<String, Vec<Function>>::from([(
AXELAR_FUNCTION_NAME.into(),
vec![Function {
name: AXELAR_FUNCTION_NAME.into(),
inputs: vec![
Param {
name: AXELAR_DESTINATION_CHAIN_PARAM.into(),
kind: ParamType::String,
internal_type: None,
},
Param {
name: AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM.into(),
kind: ParamType::String,
internal_type: None,
},
Param {
name: AXELAR_PAYLOAD_PARAM.into(),
kind: ParamType::Bytes,
internal_type: None,
},
],
outputs: vec![],
constant: Some(false),
state_mutability: Default::default(),
}],
)]),
events: Default::default(),
errors: Default::default(),
receive: false,
fallback: false,
}
.function(AXELAR_FUNCTION_NAME)
.map_err(|_| "cannot retrieve Axelar contract function")?
.encode_input(&[
Token::String(
String::from_utf8(target_chain).map_err(|_| "target chain conversion error")?,
),
// Ensure that the target contract is correctly converted to hex.
//
// The `to_string` method on the H160 is returning a string containing an ellipsis, such
// as: 0x1234…7890
Token::String(format!("0x{}", hex::encode(target_contract.0))),
Token::Bytes(serialized_msg),
])
.map_err(|_| "cannot encode input for Axelar contract function")?;

Ok(encoded_axelar_contract)
}

#[frame_support::pallet]
pub mod pallet {
use super::*;
Expand All @@ -173,7 +108,7 @@ pub mod pallet {
type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// The target of the messages comming from other chains
type Gateway: MessageReceiver<Middleware = Self::Middleware, Origin = DomainAddress>;
type Receiver: MessageReceiver<Middleware = Self::Middleware, Origin = DomainAddress>;

/// Middleware used by the gateway
type Middleware: From<AxelarId>;
Expand Down Expand Up @@ -277,9 +212,6 @@ pub mod pallet {
//
// _execute(sourceChain, sourceAddress, payload);
// }
//
// Note: The _execute logic in this case will forward all calls to the
// liquidity-pools-gateway with a special runtime local origin
#[precompile::public("execute(bytes32,string,string,bytes)")]
fn execute(
handle: &mut impl PrecompileHandle,
Expand All @@ -302,22 +234,16 @@ pub mod pallet {
ExitError::Other("gateway contract address mismatch".into()),
);

let msg = BoundedVec::<u8, <T::Gateway as MessageReceiver>::MaxEncodedLen>::try_from(
payload.as_bytes().to_vec(),
)
.map_err(|_| PrecompileFailure::Error {
exit_status: ExitError::Other("payload conversion".into()),
})?;

match config.domain {
DomainConfig::Evm(EvmConfig { chain_id, .. }) => {
let source_address_bytes =
cfg_utils::decode_var_source::<EVM_ADDRESS_LEN>(source_address.as_bytes())
.ok_or(ExitError::Other("invalid source address".into()))?;

let origin = DomainAddress::EVM(chain_id, source_address_bytes);
let message = payload.as_bytes().to_vec();

T::Gateway::receive(AxelarId::Evm(chain_id).into(), origin, msg)
T::Receiver::receive(AxelarId::Evm(chain_id).into(), origin, message)
.map_err(|e| TryDispatchError::Substrate(e).into())
}
}
Expand Down Expand Up @@ -389,3 +315,74 @@ pub mod pallet {
}
}
}

/// Encodes the provided message into the format required for submitting it
/// to the Axelar contract which in turn calls the LiquidityPools
/// contract with the serialized LP message as `payload`.
///
/// Axelar contract call:
/// <https://github.com/axelarnetwork/axelar-cgp-solidity/blob/v4.3.2/contracts/AxelarGateway.sol#L78>
///
/// LiquidityPools contract call:
/// <https://github.com/centrifuge/liquidity-pools/blob/383d279f809a01ab979faf45f31bf9dc3ce6a74a/src/routers/Gateway.sol#L276>
fn wrap_into_axelar_msg(
serialized_msg: Vec<u8>,
target_chain: Vec<u8>,
target_contract: H160,
) -> Result<Vec<u8>, &'static str> {
const AXELAR_FUNCTION_NAME: &str = "callContract";
const AXELAR_DESTINATION_CHAIN_PARAM: &str = "destinationChain";
const AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM: &str = "destinationContractAddress";
const AXELAR_PAYLOAD_PARAM: &str = "payload";

#[allow(deprecated)]
let encoded_axelar_contract = Contract {
constructor: None,
functions: BTreeMap::<String, Vec<Function>>::from([(
AXELAR_FUNCTION_NAME.into(),
vec![Function {
name: AXELAR_FUNCTION_NAME.into(),
inputs: vec![
Param {
name: AXELAR_DESTINATION_CHAIN_PARAM.into(),
kind: ParamType::String,
internal_type: None,
},
Param {
name: AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM.into(),
kind: ParamType::String,
internal_type: None,
},
Param {
name: AXELAR_PAYLOAD_PARAM.into(),
kind: ParamType::Bytes,
internal_type: None,
},
],
outputs: vec![],
constant: Some(false),
state_mutability: Default::default(),
}],
)]),
events: Default::default(),
errors: Default::default(),
receive: false,
fallback: false,
}
.function(AXELAR_FUNCTION_NAME)
.map_err(|_| "cannot retrieve Axelar contract function")?
.encode_input(&[
Token::String(
String::from_utf8(target_chain).map_err(|_| "target chain conversion error")?,
),
// Ensure that the target contract is correctly converted to hex.
//
// The `to_string` method on the H160 is returning a string containing an ellipsis, such
// as: 0x1234…7890
Token::String(format!("0x{}", hex::encode(target_contract.0))),
Token::Bytes(serialized_msg),
])
.map_err(|_| "cannot encode input for Axelar contract function")?;

Ok(encoded_axelar_contract)
}
57 changes: 57 additions & 0 deletions pallets/axelar-router/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use cfg_types::domain_address::DomainAddress;
use frame_support::{derive_impl, traits::EitherOfDiverse};
use frame_system::{EnsureRoot, EnsureSigned};
use sp_core::{H160, H256};
use sp_io::TestExternalities;

use crate::{pallet as pallet_axelar_router, AxelarId};

type AccountId = u64;

pub struct Middleware(AxelarId);

impl From<AxelarId> for Middleware {
fn from(id: AxelarId) -> Self {
Middleware(id)
}
}

frame_support::construct_runtime!(
pub enum Runtime {
System: frame_system,
Receiver: cfg_mocks::message_receiver::pallet,
Transactor: cfg_mocks::ethereum_transactor::pallet,
AccountCodeChecker: cfg_mocks::pre_conditions::pallet,
AxelarRouter: pallet_axelar_router,
}
);

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
type Block = frame_system::mocking::MockBlock<Runtime>;
}

impl cfg_mocks::message_receiver::pallet::Config for Runtime {
type Middleware = Middleware;
type Origin = DomainAddress;
}

impl cfg_mocks::ethereum_transactor::pallet::Config for Runtime {}

impl cfg_mocks::pre_conditions::pallet::Config for Runtime {
type Conditions = (H160, H256);
type Result = bool;
}

impl pallet_axelar_router::Config for Runtime {
type AdminOrigin = EitherOfDiverse<EnsureRoot<AccountId>, EnsureSigned<AccountId>>;
type EvmAccountCodeChecker = AccountCodeChecker;
type Middleware = Middleware;
type Receiver = Receiver;
type RuntimeEvent = RuntimeEvent;
type Transactor = Transactor;
}

pub fn new_test_ext() -> TestExternalities {
System::externalities()
}
Loading

0 comments on commit 329c861

Please sign in to comment.