mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 04:41:02 +00:00
Custom RPC methods for DummyOrdered pallet (#317)
* RPC for DummyOrdered * add test for RPC * proof returned by RPC is Vec<<Vec<u8>>>.encode() * retrieval -> receiving * bp-runtime crate * bp-runtime supports no_std * cargo fmt --all * jsonrpc_core::BoxFuture * Update modules/message-lane/rpc/Cargo.toml Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * Update modules/message-lane/rpc/src/lib.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * messageLane_ prefix for RPC methods * Update primitives/runtime/Cargo.toml Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * Update primitives/runtime/src/lib.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * Update modules/message-lane/rpc/src/lib.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * Update modules/message-lane/rpc/src/lib.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * Update modules/message-lane/rpc/src/lib.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
This commit is contained in:
committed by
Bastian Köcher
parent
f6d45a38da
commit
c7437c7d91
@@ -16,6 +16,7 @@ bp-eth-poa = { version = "0.1.0", path = "../../../primitives/ethereum-poa" }
|
|||||||
futures = "0.3.5"
|
futures = "0.3.5"
|
||||||
jsonrpc-core = "14.2.0"
|
jsonrpc-core = "14.2.0"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
|
pallet-message-lane-rpc = { version = "0.1.0", path = "../../../modules/message-lane/rpc" }
|
||||||
structopt = "0.3.17"
|
structopt = "0.3.17"
|
||||||
|
|
||||||
[dependencies.bridge-node-runtime]
|
[dependencies.bridge-node-runtime]
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
[package]
|
||||||
|
name = "pallet-message-lane-rpc"
|
||||||
|
description = "Module that provides RPC methods specific to message-lane pallet."
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bp-runtime = { path = "../../../primitives/runtime" }
|
||||||
|
bp-message-lane = { path = "../../../primitives/message-lane" }
|
||||||
|
derive_more = "0.99.2"
|
||||||
|
futures = { version = "0.3.5", features = ["compat"] }
|
||||||
|
jsonrpc-core = "14.2.0"
|
||||||
|
jsonrpc-core-client = "14.2.0"
|
||||||
|
jsonrpc-derive = "14.2.1"
|
||||||
|
|
||||||
|
# Substrate Based Dependencies
|
||||||
|
|
||||||
|
[dependencies.sc-client-api]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
git = "https://github.com/paritytech/substrate/"
|
||||||
|
|
||||||
|
[dependencies.sp-blockchain]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
git = "https://github.com/paritytech/substrate/"
|
||||||
|
|
||||||
|
[dependencies.sp-core]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
git = "https://github.com/paritytech/substrate/"
|
||||||
|
|
||||||
|
[dependencies.sp-runtime]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
git = "https://github.com/paritytech/substrate/"
|
||||||
|
|
||||||
|
[dependencies.sp-state-machine]
|
||||||
|
version = "0.8.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
git = "https://github.com/paritytech/substrate/"
|
||||||
|
|
||||||
|
[dependencies.sp-trie]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
git = "https://github.com/paritytech/substrate/"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
async-std = "1.6.2"
|
||||||
|
|
||||||
|
[dev-dependencies.substrate-test-runtime-client]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
git = "https://github.com/paritytech/substrate/"
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Possible errors and results of message-lane RPC calls.
|
||||||
|
|
||||||
|
/// Future Result type.
|
||||||
|
pub type FutureResult<T> = jsonrpc_core::BoxFuture<T>;
|
||||||
|
|
||||||
|
/// State RPC errors.
|
||||||
|
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||||
|
pub enum Error {
|
||||||
|
/// When unknown instance id is passed.
|
||||||
|
#[display(fmt = "Message lane instance is unknown")]
|
||||||
|
UnknownInstance,
|
||||||
|
/// Client error.
|
||||||
|
#[display(fmt = "Client error: {}", _0)]
|
||||||
|
Client(Box<dyn std::error::Error + Send>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Error::UnknownInstance => None,
|
||||||
|
Error::Client(ref err) => Some(&**err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for jsonrpc_core::Error {
|
||||||
|
fn from(e: Error) -> Self {
|
||||||
|
const UNKNOW_INSTANCE_CODE: i64 = 1;
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Error::UnknownInstance => jsonrpc_core::Error {
|
||||||
|
code: jsonrpc_core::ErrorCode::ServerError(UNKNOW_INSTANCE_CODE),
|
||||||
|
message: "Unknown instance passed".into(),
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
Error::Client(e) => jsonrpc_core::Error {
|
||||||
|
code: jsonrpc_core::ErrorCode::InternalError,
|
||||||
|
message: format!("Unknown error occured: {}", e),
|
||||||
|
data: Some(format!("{:?}", e).into()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Module that provides RPC methods specific to message-lane pallet.
|
||||||
|
|
||||||
|
use crate::error::{Error, FutureResult};
|
||||||
|
|
||||||
|
use bp_message_lane::{LaneId, MessageNonce};
|
||||||
|
use bp_runtime::InstanceId;
|
||||||
|
use futures::{FutureExt, TryFutureExt};
|
||||||
|
use jsonrpc_core::futures::Future as _;
|
||||||
|
use jsonrpc_derive::rpc;
|
||||||
|
use sc_client_api::Backend as BackendT;
|
||||||
|
use sp_blockchain::{Error as BlockchainError, HeaderBackend};
|
||||||
|
use sp_core::{storage::StorageKey, Bytes};
|
||||||
|
use sp_runtime::{codec::Encode, generic::BlockId, traits::Block as BlockT};
|
||||||
|
use sp_state_machine::prove_read;
|
||||||
|
use sp_trie::StorageProof;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
/// Trie-based storage proof that the message(s) with given key(s) have been sent by the bridged chain.
|
||||||
|
/// SCALE-encoded trie nodes array `Vec<Vec<u8>>`.
|
||||||
|
pub type MessagesProof = Bytes;
|
||||||
|
|
||||||
|
/// Trie-based storage proof that the message(s) with given key(s) have been received by the bridged chain.
|
||||||
|
/// SCALE-encoded trie nodes array `Vec<Vec<u8>>`.
|
||||||
|
pub type MessagesReceivingProof = Bytes;
|
||||||
|
|
||||||
|
/// Trie-based storage proof that the message(s) with given key(s) have been processed by the bridged chain.
|
||||||
|
/// SCALE-encoded trie nodes array `Vec<Vec<u8>>`.
|
||||||
|
pub type MessagesProcessingProof = Bytes;
|
||||||
|
|
||||||
|
/// Runtime adapter.
|
||||||
|
pub trait Runtime: Send + Sync + 'static {
|
||||||
|
/// Return runtime storage key for given message. May return None if instance is unknown.
|
||||||
|
fn message_key(&self, instance: &InstanceId, lane: &LaneId, nonce: MessageNonce) -> Option<StorageKey>;
|
||||||
|
/// Return runtime storage key for inbound lane state. May return None if instance is unknown.
|
||||||
|
fn inbound_lane_data_key(&self, instance: &InstanceId, lane: &LaneId) -> Option<StorageKey>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides RPC methods for interacting with message-lane pallet.
|
||||||
|
#[rpc]
|
||||||
|
pub trait MessageLaneApi<BlockHash> {
|
||||||
|
/// Returns proof-of-message(s) in given inclusive range.
|
||||||
|
#[rpc(name = "messageLane_proveMessages")]
|
||||||
|
fn prove_messages(
|
||||||
|
&self,
|
||||||
|
instance: InstanceId,
|
||||||
|
lane: LaneId,
|
||||||
|
begin: MessageNonce,
|
||||||
|
end: MessageNonce,
|
||||||
|
block: Option<BlockHash>,
|
||||||
|
) -> FutureResult<MessagesProof>;
|
||||||
|
|
||||||
|
/// Returns proof-of-message(s) receiving.
|
||||||
|
#[rpc(name = "messageLane_proveMessagesReceiving")]
|
||||||
|
fn prove_messages_receiving(
|
||||||
|
&self,
|
||||||
|
instance: InstanceId,
|
||||||
|
lane: LaneId,
|
||||||
|
block: Option<BlockHash>,
|
||||||
|
) -> FutureResult<MessagesReceivingProof>;
|
||||||
|
|
||||||
|
/// Returns proof-of-message(s) processing.
|
||||||
|
#[rpc(name = "messageLane_proveMessagesProcessing")]
|
||||||
|
fn prove_messages_processing(
|
||||||
|
&self,
|
||||||
|
instance: InstanceId,
|
||||||
|
lane: LaneId,
|
||||||
|
block: Option<BlockHash>,
|
||||||
|
) -> FutureResult<MessagesProcessingProof>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the MessageLaneApi trait for interacting with message lanes.
|
||||||
|
pub struct MessageLaneRpcHandler<Block, Backend, R> {
|
||||||
|
backend: Arc<Backend>,
|
||||||
|
runtime: Arc<R>,
|
||||||
|
_phantom: std::marker::PhantomData<Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Block, Backend, R> MessageLaneRpcHandler<Block, Backend, R> {
|
||||||
|
/// Creates new mesage lane RPC handler.
|
||||||
|
pub fn new(backend: Arc<Backend>, runtime: Arc<R>) -> Self {
|
||||||
|
Self {
|
||||||
|
backend,
|
||||||
|
runtime,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Block, Backend, R> MessageLaneApi<Block::Hash> for MessageLaneRpcHandler<Block, Backend, R>
|
||||||
|
where
|
||||||
|
Block: BlockT,
|
||||||
|
Backend: BackendT<Block> + 'static,
|
||||||
|
R: Runtime,
|
||||||
|
{
|
||||||
|
fn prove_messages(
|
||||||
|
&self,
|
||||||
|
instance: InstanceId,
|
||||||
|
lane: LaneId,
|
||||||
|
begin: MessageNonce,
|
||||||
|
end: MessageNonce,
|
||||||
|
block: Option<Block::Hash>,
|
||||||
|
) -> FutureResult<MessagesProof> {
|
||||||
|
let runtime = self.runtime.clone();
|
||||||
|
Box::new(
|
||||||
|
prove_keys_read(
|
||||||
|
self.backend.clone(),
|
||||||
|
block,
|
||||||
|
(begin..=end).map(move |nonce| runtime.message_key(&instance, &lane, nonce)),
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.compat()
|
||||||
|
.map(serialize_storage_proof)
|
||||||
|
.map_err(Into::into),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prove_messages_receiving(
|
||||||
|
&self,
|
||||||
|
instance: InstanceId,
|
||||||
|
lane: LaneId,
|
||||||
|
block: Option<Block::Hash>,
|
||||||
|
) -> FutureResult<MessagesReceivingProof> {
|
||||||
|
Box::new(
|
||||||
|
prove_keys_read(
|
||||||
|
self.backend.clone(),
|
||||||
|
block,
|
||||||
|
vec![self.runtime.inbound_lane_data_key(&instance, &lane)],
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.compat()
|
||||||
|
.map(serialize_storage_proof)
|
||||||
|
.map_err(Into::into),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prove_messages_processing(
|
||||||
|
&self,
|
||||||
|
instance: InstanceId,
|
||||||
|
lane: LaneId,
|
||||||
|
block: Option<Block::Hash>,
|
||||||
|
) -> FutureResult<MessagesProcessingProof> {
|
||||||
|
Box::new(
|
||||||
|
prove_keys_read(
|
||||||
|
self.backend.clone(),
|
||||||
|
block,
|
||||||
|
vec![self.runtime.inbound_lane_data_key(&instance, &lane)],
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.compat()
|
||||||
|
.map(serialize_storage_proof)
|
||||||
|
.map_err(Into::into),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prove_keys_read<Block, Backend>(
|
||||||
|
backend: Arc<Backend>,
|
||||||
|
block: Option<Block::Hash>,
|
||||||
|
keys: impl IntoIterator<Item = Option<StorageKey>>,
|
||||||
|
) -> Result<StorageProof, Error>
|
||||||
|
where
|
||||||
|
Block: BlockT,
|
||||||
|
Backend: BackendT<Block> + 'static,
|
||||||
|
{
|
||||||
|
let block = unwrap_or_best(&*backend, block);
|
||||||
|
let state = backend.state_at(BlockId::Hash(block)).map_err(blockchain_err)?;
|
||||||
|
let keys = keys
|
||||||
|
.into_iter()
|
||||||
|
.map(|key| key.ok_or(Error::UnknownInstance).map(|key| key.0))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let storage_proof = prove_read(state, keys)
|
||||||
|
.map_err(BlockchainError::Execution)
|
||||||
|
.map_err(blockchain_err)?;
|
||||||
|
Ok(storage_proof)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_storage_proof(proof: StorageProof) -> Bytes {
|
||||||
|
let raw_nodes: Vec<Vec<_>> = proof.iter_nodes().map(Into::into).collect();
|
||||||
|
raw_nodes.encode().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwrap_or_best<Block: BlockT>(backend: &impl BackendT<Block>, block: Option<Block::Hash>) -> Block::Hash {
|
||||||
|
match block {
|
||||||
|
Some(block) => block,
|
||||||
|
None => backend.blockchain().info().best_hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blockchain_err(err: BlockchainError) -> Error {
|
||||||
|
Error::Client(Box::new(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use sp_core::Blake2Hasher;
|
||||||
|
use sp_runtime::{codec::Decode, traits::Header as HeaderT};
|
||||||
|
use substrate_test_runtime_client::{
|
||||||
|
runtime::Block, Backend, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEST_INSTANCE: InstanceId = [0, 0, 0, 1];
|
||||||
|
const TEST_LANE: LaneId = [0, 0, 0, 1];
|
||||||
|
|
||||||
|
fn test_key() -> StorageKey {
|
||||||
|
StorageKey(sp_core::storage::well_known_keys::CODE.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestRuntimeAdapter;
|
||||||
|
|
||||||
|
impl Runtime for TestRuntimeAdapter {
|
||||||
|
fn message_key(&self, instance: &InstanceId, _lane: &LaneId, _nonce: MessageNonce) -> Option<StorageKey> {
|
||||||
|
if *instance == TEST_INSTANCE {
|
||||||
|
Some(test_key())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inbound_lane_data_key(&self, instance: &InstanceId, _lane: &LaneId) -> Option<StorageKey> {
|
||||||
|
if *instance == TEST_INSTANCE {
|
||||||
|
Some(test_key())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_handler() -> MessageLaneRpcHandler<Block, Backend, TestRuntimeAdapter> {
|
||||||
|
let builder = TestClientBuilder::new();
|
||||||
|
let (_, backend) = builder.build_with_backend();
|
||||||
|
|
||||||
|
MessageLaneRpcHandler::new(backend, Arc::new(TestRuntimeAdapter))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn storage_proof_is_actually_generated() {
|
||||||
|
// the only thing we actually care here is that RPC actually generates storage proof
|
||||||
|
// that can be verified from runtime
|
||||||
|
|
||||||
|
// proof is generated by RPC
|
||||||
|
let handler = test_handler();
|
||||||
|
let proof = handler
|
||||||
|
.prove_messages(TEST_INSTANCE, TEST_LANE, 1, 3, None)
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// proof is then relayed + checked by runtime (sp_trie supports no_std)
|
||||||
|
// (storage root is known to underlying bridge pallet)
|
||||||
|
let root = *handler
|
||||||
|
.backend
|
||||||
|
.blockchain()
|
||||||
|
.header(BlockId::Number(0))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.state_root();
|
||||||
|
let proof = StorageProof::new(Decode::decode(&mut &proof[..]).unwrap());
|
||||||
|
let trie_db = proof.into_memory_db::<Blake2Hasher>();
|
||||||
|
let checked_storage_value =
|
||||||
|
sp_trie::read_trie_value::<sp_trie::Layout<_>, _>(&trie_db, &root, &test_key().0).unwrap();
|
||||||
|
assert!(checked_storage_value.is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "bp-runtime"
|
||||||
|
description = "Primitives that may be used at (bridges) runtime level."
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = []
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Primitives that may be used at (bridges) runtime level.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
/// Id of deployed module instance. We have a bunch of pallets that may be used in
|
||||||
|
/// different bridges. E.g. message-lane pallet may be deployed twice in the same
|
||||||
|
/// runtime to bridge ThisChain with Chain1 and Chain2. Sometimes we need to be able
|
||||||
|
/// to identify deployed instance dynamically. This type is used for that.
|
||||||
|
pub type InstanceId = [u8; 4];
|
||||||
Reference in New Issue
Block a user