FILL UP!
centrifuge / axelar-cgp-substrate Goto Github PK
View Code? Open in Web Editor NEWLicense: GNU General Public License v3.0
License: GNU General Public License v3.0
In the PoC the axelar execution happens in the same chain as the destination call. Of course, this was only intended to show case the end to end flow with the integration with the axelar blockchain and the external relayers.
The more interesting approach is that a common good parachain (BridgeHub) has this pallet deployed in its runtime the core purpose is to store approved calls & forward them to the correspondent parachain for execution.
Every Batch is indexed by the destination ChainId, this value is a uint256 and unique to the axelar network. Ideally the ChainId is a composite UUID in the form of a String, that can describe chain types and other internal identifiers. But for now, we would need a map of Axelar ChainIds to Polkadot Parachain ids. This is important to be able to build the XCM Multilocation (universal format of identifying parachains/accounts/etc) when interacting with XCM. It seems that the most lightweight solution is to have a built in storage mapping in the Axelar Pallet that maintains the ParachainId associated to the AxelarChainId.
#[pallet::storage]
#[pallet::getter(fn chain_id_mapping)]
pub(super) type ChainIdMapping<T: Config> = StorageMap<
_,
Blake2_128Concat,
// AxelarChainId
u128,
// Parachain Id
u32,
ValueQuery,
>;
This is a bit unfortunate since there has to be an admin function that has to maintain this mapping. I hope we can avoid this.
Lets ask Axelar how hard would it be to encode in the ChainId a composite identifier without collisions
To avoid spamming, the execute function will check if the destination chainID is a well known parachain authorized in the BridgeHub, otherwise it should reject the execute call.
We should give each parachain the power to add or remove themselves through their own governance mechanisms from the bridge forwarding logic. We suggest to create a custom EnsureParachainOrigin
:
#[pallet::storage]
#[pallet::getter(fn authorized_parachain)]
pub(super) type AutorizedParachain<T: Config> = StorageMap<
_,
Blake2_128Concat,
// Parachain Id
ParaId,
// Empty
(),
ValueQuery,
>;
fn set_auth_para(origin, call_id: CallId) {
// Only callable by sovereign para_id
let who = EnsureParachainOrigin(origin)?;
ParaCallId<T>::set(who.id, ());
Self::deposit_event(Event::ParaIdAuthorized{who.id})
}
When a call has been approved by the Axelar validator network, then the next step is to forward the call to the correspondent parachain. The initial approach is to prepend proper identification to the XCM Sender so the receiving parachain can perform any authorization needed before executing the final encoded call.
Here is a rough schema of how the call can be formed:
let call = payload;
let msg = vec![
// Provides the receiving chain with info
// that this is coming over a bridge
UniversalOrigin(
GlobalConsensus(ETHEREUM)
),
// Provides the address of the called contract.
// I.e. this will be the "msg.sender" from the Ethereum side
DescendOrigin(
Junctions::X1(AccountKey20 {network: Some(ETHEREUM), key: sender})
),
WithdrawAsset(fee_asset.clone().into()),
BuyExecution(asset, &dest, dest_weight),
RefundSurplus,
// This will be the call that parachains provide to further process messages
// from the Ethereum side comming from axelar.
//
// The `origin_kind` should also be "set-able" by the receiving chain.
// The `require_weight_at_most` should also be "set-able" by the receiving chain.
// The `call` can NOT have a sensible generic param set in the gateway.
Transact { origin_kind, require_weight_at_most, call: DoubleEncoded<()>::from(call)}
];
// Use the associated type to forward the message. Usually, this would be done using `trait SendXcm`
T::Sender::deliver(msg).map_err(|e| Error::<T>::SendXcm{error: e})
A few pieces are worth making emphasis about:
UniversalOrigin - This is a newly introduced instruction in XCMv3 that allows an xcm origin in the context of the execution to change to an allowed alias for the next instructions to be executed. This way we can identify and propagate the ultimate origin outside of the polkadot system. Works in combination with Junction::GlobalConsensus(NetworkId)
where NetworkId, can be a well known network like kusama, bitcoin, or ethereum or an arbitrary one identified with their genesis block hash.
DescendOrigin - Ensures that the origin dapp is captured and propagated along so further authentorization logic can be executed in the destination chain & pallet.
The payload should have all the information needed to be executed in the target chain and formatted accordingly. The Axelar pallet should not need to "understand" its contents.
The Axelar gateway pallet has an extrinsic called CallContract
that initiates the Axelar message validation and consensus. This extrinsic should only be callable from a parachain through XCM; the BridgeHub should only take care of forwarding calls not initiating them since it should not have any business logic role in the underlaying content of a remote call.
To achieve this a custom origin trait that identifies the "sender" as a Multilocation is needed: Parachain(5000) || (Parachain(5000), AccountId(x)) || ...
In addition to having an EnsureParachainOrigin
authorizing the call.
#[pallet::weight(<T as pallet::Config>::WeightInfo::call_contract())]
pub fn call_contract(
origin: OriginFor<T>,
destination_chain: String,
destination_contract_address: String,
payload: Vec<u8>,
) -> DispatchResult {
// Only a parachain can call this function
let para_id = EnsureParachainOnly()?;
Self::deposit_event(Event::ContractCall {
// Potentially concatenate here the BridgeHub ChainId in a format like `bridgehub_id/source_para_id`
sender: para_id.0.to_string(),
destination_chain,
destination_contract_address,
payload_hash: H256::from_slice(keccak_256(&payload).as_slice()),
payload,
});
Ok(())
}
A comprehensive integration test that verifies the XCM requirements above described is required to ensure that the assumptions are working as expected. Using xcm-emulator.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.