Skip to content

Commit

Permalink
Update ismp implementation (#43)
Browse files Browse the repository at this point in the history
* update ismp implementation

* chore

* fix faulty host implementation

* fix no-std

* updated failing tests

* some changes

---------

Co-authored-by: Seun Lanlege <[email protected]>
  • Loading branch information
Wizdave97 and seunlanlege authored May 26, 2023
1 parent ad946c0 commit 3d68655
Show file tree
Hide file tree
Showing 13 changed files with 446 additions and 478 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 10 additions & 5 deletions ismp-assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ pub mod pallet {
},
};
use frame_system::pallet_prelude::*;
use ismp::host::StateMachine;
use pallet_ismp::primitives::{IsmpDispatch, IsmpMessage};
use ismp::{
host::StateMachine,
router::{DispatchPost, DispatchRequest, IsmpDispatcher},
};

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand All @@ -58,7 +60,7 @@ pub mod pallet {
/// Native currency implementation
type NativeCurrency: Mutate<Self::AccountId>;
/// Ismp message disptacher
type IsmpDispatch: IsmpDispatch;
type IsmpDispatcher: IsmpDispatcher + Default;
}

/// Pallet events
Expand Down Expand Up @@ -111,15 +113,18 @@ pub mod pallet {
) -> DispatchResult {
let origin = ensure_signed(origin)?;
let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount };
let request = IsmpMessage::Post {
let post = DispatchPost {
dest_chain: params.dest_chain,
from: PALLET_ID.0.to_vec(),
to: PALLET_ID.0.to_vec(),
timeout_timestamp: params.timeout,
data: payload.encode(),
};

T::IsmpDispatch::dispatch_message(request).map_err(|_| Error::<T>::TransferFailed)?;
let dispatcher = T::IsmpDispatcher::default();
dispatcher
.dispatch_request(DispatchRequest::Post(post))
.map_err(|_| Error::<T>::TransferFailed)?;
<T::NativeCurrency as Mutate<T::AccountId>>::burn_from(&origin, params.amount.into())?;
Self::deposit_event(Event::<T>::BalanceTransferred {
from: payload.from,
Expand Down
96 changes: 59 additions & 37 deletions pallet-ismp/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,22 @@ use frame_system::RawOrigin;
)]
pub mod benchmarks {
use super::*;
use crate::router::Receipt;
use crate::dispatcher::Receipt;
use alloc::collections::BTreeMap;
use frame_support::{traits::Hooks, PalletId};
use frame_system::EventRecord;
use ismp_rs::{
consensus::{ConsensusClient, IntermediateState, StateCommitment, StateMachineHeight},
consensus::{
ConsensusClient, IntermediateState, StateCommitment, StateMachineClient,
StateMachineHeight,
},
error::Error as IsmpError,
messaging::{Message, Proof, RequestMessage, ResponseMessage, TimeoutMessage},
messaging::{
Message, Proof, RequestMessage, ResponseMessage, StateCommitmentHeight, TimeoutMessage,
},
module::IsmpModule,
router::{Post, RequestResponse},
util::hash_request,
router::{Post, PostResponse, RequestResponse},
util::{hash_request, hash_response},
};
use sp_std::prelude::Vec;

Expand All @@ -68,14 +74,36 @@ pub mod benchmarks {
_host: &dyn IsmpHost,
_trusted_consensus_state: Vec<u8>,
_proof: Vec<u8>,
) -> Result<(Vec<u8>, Vec<IntermediateState>), IsmpError> {
) -> Result<(Vec<u8>, BTreeMap<StateMachine, StateCommitmentHeight>), IsmpError> {
Ok(Default::default())
}

fn verify_fraud_proof(
&self,
_host: &dyn IsmpHost,
_trusted_consensus_state: Vec<u8>,
_proof_1: Vec<u8>,
_proof_2: Vec<u8>,
) -> Result<(), IsmpError> {
Ok(())
}

fn unbonding_period(&self) -> Duration {
Duration::from_secs(60 * 60 * 60)
}

fn state_machine(
&self,
_id: StateMachine,
) -> Result<Box<dyn StateMachineClient>, IsmpError> {
Ok(Box::new(BenchmarkStateMachine))
}
}

/// Mock State Machine
pub struct BenchmarkStateMachine;

impl StateMachineClient for BenchmarkStateMachine {
fn verify_membership(
&self,
_host: &dyn IsmpHost,
Expand All @@ -99,10 +127,6 @@ pub mod benchmarks {
) -> Result<Vec<Option<Vec<u8>>>, IsmpError> {
Ok(Default::default())
}

fn is_frozen(&self, _trusted_consensus_state: &[u8]) -> Result<(), IsmpError> {
Ok(())
}
}

/// This module should be added to the module router in runtime for benchmarks to pass
Expand Down Expand Up @@ -134,26 +158,24 @@ pub mod benchmarks {
#[benchmark]
fn create_consensus_client() {
set_timestamp::<T>();
let intermediate_state = IntermediateState {
height: StateMachineHeight {
id: StateMachineId {
state_id: StateMachine::Polkadot(1000),
consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID,
},
height: 1,
},

commitment: StateCommitment {
timestamp: 1651280681,
ismp_root: None,
state_root: Default::default(),
},
};

let message = CreateConsensusClient {
consensus_state: Default::default(),
consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID,
state_machine_commitments: vec![intermediate_state],
state_machine_commitments: vec![(
StateMachineId {
state_id: StateMachine::Ethereum,
consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID,
},
StateCommitmentHeight {
commitment: StateCommitment {
timestamp: 1651280681,
overlay_root: None,
state_root: Default::default(),
},
height: 1,
},
)],
};

#[extrinsic_call]
Expand All @@ -176,7 +198,7 @@ pub mod benchmarks {
},
commitment: StateCommitment {
timestamp: 1000,
ismp_root: None,
overlay_root: None,
state_root: Default::default(),
},
};
Expand Down Expand Up @@ -206,7 +228,7 @@ pub mod benchmarks {
from: MODULE_ID.0.to_vec(),
to: MODULE_ID.0.to_vec(),
timeout_timestamp: 5000,
data: vec![],
data: "handle_request_message".as_bytes().to_vec(),
};

let msg = RequestMessage {
Expand All @@ -219,7 +241,7 @@ pub mod benchmarks {
handle(RawOrigin::Signed(caller), vec![Message::Request(msg)]);

let commitment = hash_request::<Host<T>>(&Request::Post(post));
assert!(RequestAcks::<T>::get(commitment.0.to_vec()).is_some());
assert!(IncomingRequestAcks::<T>::get(commitment.0.to_vec()).is_some());
}

#[benchmark]
Expand All @@ -234,15 +256,15 @@ pub mod benchmarks {
from: MODULE_ID.0.to_vec(),
to: MODULE_ID.0.to_vec(),
timeout_timestamp: 5000,
data: vec![],
data: "handle_response_message".as_bytes().to_vec(),
};
let request = Request::Post(post.clone());

let commitment = hash_request::<Host<T>>(&request);
RequestAcks::<T>::insert(commitment.0.to_vec(), Receipt::Ok);

let response = Response::Post { post, response: vec![] };
OutgoingRequestAcks::<T>::insert(commitment.0.to_vec(), Receipt::Ok);

let response = Response::Post(PostResponse { post, response: vec![] });
let response_commitment = hash_response::<Host<T>>(&response);
let msg = ResponseMessage::Post {
responses: vec![response],
proof: Proof { height: intermediate_state.height, proof: vec![] },
Expand All @@ -253,7 +275,7 @@ pub mod benchmarks {
#[extrinsic_call]
handle(RawOrigin::Signed(caller), vec![Message::Response(msg)]);

assert!(RequestAcks::<T>::get(commitment.0.to_vec()).is_none());
assert!(IncomingResponseAcks::<T>::get(response_commitment.0.to_vec()).is_some());
}

#[benchmark]
Expand All @@ -268,12 +290,12 @@ pub mod benchmarks {
from: MODULE_ID.0.to_vec(),
to: MODULE_ID.0.to_vec(),
timeout_timestamp: 500,
data: vec![],
data: "handle_timeout_message".as_bytes().to_vec(),
};
let request = Request::Post(post.clone());

let commitment = hash_request::<Host<T>>(&request);
RequestAcks::<T>::insert(commitment.0.to_vec(), Receipt::Ok);
OutgoingRequestAcks::<T>::insert(commitment.0.to_vec(), Receipt::Ok);

let msg = TimeoutMessage::Post {
requests: vec![request],
Expand All @@ -284,7 +306,7 @@ pub mod benchmarks {
#[extrinsic_call]
handle(RawOrigin::Signed(caller), vec![Message::Timeout(msg)]);

assert!(RequestAcks::<T>::get(commitment.0.to_vec()).is_none());
assert!(OutgoingRequestAcks::<T>::get(commitment.0.to_vec()).is_none());
}

#[benchmark]
Expand Down
135 changes: 135 additions & 0 deletions pallet-ismp/src/dispatcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright (C) 2023 Polytope Labs.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Implementation for the ISMP Router
use crate::{host::Host, Config, Event, OutgoingRequestAcks, OutgoingResponseAcks, Pallet};
use alloc::string::ToString;
use codec::{Decode, Encode};
use core::marker::PhantomData;
use ismp_primitives::mmr::Leaf;
use ismp_rs::{
host::IsmpHost,
router::{
DispatchError, DispatchRequest, DispatchResult, DispatchSuccess, Get, IsmpDispatcher, Post,
PostResponse, Request, Response,
},
util::{hash_request, hash_response},
};
use sp_core::H256;

/// A receipt or an outgoing or incoming request or response
#[derive(Encode, Decode, scale_info::TypeInfo)]
pub enum Receipt {
/// Ok
Ok,
}

/// The dispatcher commits outgoing requests and responses to the mmr
pub struct Dispatcher<T>(PhantomData<T>);

impl<T> Default for Dispatcher<T> {
fn default() -> Self {
Self(PhantomData)
}
}

impl<T> IsmpDispatcher for Dispatcher<T>
where
T: Config,
<T as frame_system::Config>::Hash: From<H256>,
{
fn dispatch_request(&self, request: DispatchRequest) -> DispatchResult {
let host = Host::<T>::default();
let request = match request {
DispatchRequest::Get(dispatch_get) => {
let get = Get {
source_chain: host.host_state_machine(),
dest_chain: dispatch_get.dest_chain,
nonce: host.next_nonce(),
from: dispatch_get.from,
keys: dispatch_get.keys,
height: dispatch_get.height,
timeout_timestamp: dispatch_get.timeout_timestamp,
};
Request::Get(get)
}
DispatchRequest::Post(dispatch_post) => {
let post = Post {
source_chain: host.host_state_machine(),
dest_chain: dispatch_post.dest_chain,
nonce: host.next_nonce(),
from: dispatch_post.from,
to: dispatch_post.to,
timeout_timestamp: dispatch_post.timeout_timestamp,
data: dispatch_post.data,
};
Request::Post(post)
}
};

let commitment = hash_request::<Host<T>>(&request).0.to_vec();

let (dest_chain, source_chain, nonce) =
(request.dest_chain(), request.source_chain(), request.nonce());
Pallet::<T>::mmr_push(Leaf::Request(request)).ok_or_else(|| DispatchError {
msg: "Failed to push request into mmr".to_string(),
nonce,
source: source_chain,
dest: dest_chain,
})?;
// Deposit Event
Pallet::<T>::deposit_event(Event::Request {
request_nonce: nonce,
source_chain,
dest_chain,
});
// We need this step since it's not trivial to check the mmr for commitments on chain
OutgoingRequestAcks::<T>::insert(commitment, Receipt::Ok);
Ok(DispatchSuccess { dest_chain, source_chain, nonce })
}

fn dispatch_response(&self, response: PostResponse) -> DispatchResult {
let response = Response::Post(response);

let commitment = hash_response::<Host<T>>(&response).0.to_vec();

if OutgoingResponseAcks::<T>::contains_key(commitment.clone()) {
Err(DispatchError {
msg: "Duplicate response".to_string(),
nonce: response.nonce(),
source: response.source_chain(),
dest: response.dest_chain(),
})?
}

let (dest_chain, source_chain, nonce) =
(response.dest_chain(), response.source_chain(), response.nonce());

Pallet::<T>::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError {
msg: "Failed to push response into mmr".to_string(),
nonce,
source: source_chain,
dest: dest_chain,
})?;

Pallet::<T>::deposit_event(Event::Response {
request_nonce: nonce,
dest_chain,
source_chain,
});
OutgoingResponseAcks::<T>::insert(commitment, Receipt::Ok);
Ok(DispatchSuccess { dest_chain, source_chain, nonce })
}
}
Loading

0 comments on commit 3d68655

Please sign in to comment.