Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Custom RPC for Merkle Mountain Range pallet (#8137)
Browse files Browse the repository at this point in the history
* Add MMR custom RPC.

* Change RuntimeApi to avoid hardcoding leaf type.

* Properly implement the new RuntimeAPI and wire up RPC.

* Extract Offchain DB as separate execution extension.

* Enable offchain DB access for offchain calls.

* Fix offchain_election tests.

* Skip block initialisation for proof generation.

* Fix integration test setup.

* Fix offchain tests. Not sure how I missed them earlier 🤷.

* Fix long line.

* One more test missing.

* Update mock for multi-phase.

* Address review grumbbles.

* Address review grumbles.

* Fix line width of a comment
  • Loading branch information
tomusdrw authored Mar 10, 2021
1 parent ef50a44 commit 3adefdc
Show file tree
Hide file tree
Showing 37 changed files with 828 additions and 341 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ members = [
"frame/membership",
"frame/merkle-mountain-range",
"frame/merkle-mountain-range/primitives",
"frame/merkle-mountain-range/rpc",
"frame/metadata",
"frame/multisig",
"frame/nicks",
Expand Down
4 changes: 2 additions & 2 deletions bin/node-template/node/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>

if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}

Expand Down Expand Up @@ -323,7 +323,7 @@ pub fn new_light(mut config: Configuration) -> Result<TaskManager, ServiceError>

if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}

Expand Down
4 changes: 2 additions & 2 deletions bin/node/cli/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ pub fn new_full_base(

if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}

Expand Down Expand Up @@ -430,7 +430,7 @@ pub fn new_light_base(mut config: Configuration) -> Result<(

if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}

Expand Down
1 change: 1 addition & 0 deletions bin/node/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jsonrpc-core = "15.1.0"
node-primitives = { version = "2.0.0", path = "../primitives" }
node-runtime = { version = "2.0.0", path = "../runtime" }
pallet-contracts-rpc = { version = "3.0.0", path = "../../../frame/contracts/rpc/" }
pallet-mmr-rpc = { version = "3.0.0", path = "../../../frame/merkle-mountain-range/rpc/" }
pallet-transaction-payment-rpc = { version = "3.0.0", path = "../../../frame/transaction-payment/rpc/" }
sc-client-api = { version = "3.0.0", path = "../../../client/api" }
sc-consensus-babe = { version = "0.9.0", path = "../../../client/consensus/babe" }
Expand Down
5 changes: 5 additions & 0 deletions bin/node/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub fn create_full<C, P, SC, B>(
HeaderMetadata<Block, Error=BlockChainError> + Sync + Send + 'static,
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber>,
C::Api: pallet_mmr_rpc::MmrRuntimeApi<Block, <Block as sp_runtime::traits::Block>::Hash>,
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
C::Api: BabeApi<Block>,
C::Api: BlockBuilder<Block>,
Expand All @@ -126,6 +127,7 @@ pub fn create_full<C, P, SC, B>(
{
use substrate_frame_rpc_system::{FullSystem, SystemApi};
use pallet_contracts_rpc::{Contracts, ContractsApi};
use pallet_mmr_rpc::{MmrApi, Mmr};
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};

let mut io = jsonrpc_core::IoHandler::default();
Expand Down Expand Up @@ -161,6 +163,9 @@ pub fn create_full<C, P, SC, B>(
io.extend_with(
ContractsApi::to_delegate(Contracts::new(client.clone()))
);
io.extend_with(
MmrApi::to_delegate(Mmr::new(client.clone()))
);
io.extend_with(
TransactionPaymentApi::to_delegate(TransactionPayment::new(client.clone()))
);
Expand Down
18 changes: 13 additions & 5 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1377,23 +1377,31 @@ impl_runtime_apis! {

impl pallet_mmr::primitives::MmrApi<
Block,
mmr::Leaf,
mmr::Hash,
> for Runtime {
fn generate_proof(leaf_index: u64) -> Result<(mmr::Leaf, mmr::Proof<mmr::Hash>), mmr::Error> {
fn generate_proof(leaf_index: u64)
-> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof<mmr::Hash>), mmr::Error>
{
Mmr::generate_proof(leaf_index)
.map(|(leaf, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaf), proof))
}

fn verify_proof(leaf: mmr::Leaf, proof: mmr::Proof<mmr::Hash>) -> Result<(), mmr::Error> {
fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::Proof<mmr::Hash>)
-> Result<(), mmr::Error>
{
let leaf: mmr::Leaf = leaf
.into_opaque_leaf()
.try_decode()
.ok_or(mmr::Error::Verify)?;
Mmr::verify_leaf(leaf, proof)
}

fn verify_proof_stateless(
root: mmr::Hash,
leaf: Vec<u8>,
leaf: mmr::EncodableOpaqueLeaf,
proof: mmr::Proof<mmr::Hash>
) -> Result<(), mmr::Error> {
let node = mmr::DataOrHash::Data(mmr::OpaqueLeaf(leaf));
let node = mmr::DataOrHash::Data(leaf.into_opaque_leaf());
pallet_mmr::verify_leaf_proof::<mmr::Hashing, _>(root, node, proof)
}
}
Expand Down
7 changes: 4 additions & 3 deletions bin/node/testing/src/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,14 @@ impl BenchDb {
};
let task_executor = TaskExecutor::new();

let (client, backend) = sc_service::new_client(
db_config,
let backend = sc_service::new_db_backend(db_config).expect("Should not fail");
let client = sc_service::new_client(
backend.clone(),
NativeExecutor::new(WasmExecutionMethod::Compiled, None, 8),
&keyring.generate_genesis(),
None,
None,
ExecutionExtensions::new(profile.into_execution_strategies(), None),
ExecutionExtensions::new(profile.into_execution_strategies(), None, None),
Box::new(task_executor.clone()),
None,
Default::default(),
Expand Down
33 changes: 31 additions & 2 deletions client/api/src/execution_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use std::sync::{Weak, Arc};
use codec::Decode;
use sp_core::{
ExecutionContext,
offchain::{self, OffchainExt, TransactionPoolExt},
offchain::{self, OffchainWorkerExt, TransactionPoolExt, OffchainDbExt},
};
use sp_keystore::{KeystoreExt, SyncCryptoStorePtr};
use sp_runtime::{
Expand Down Expand Up @@ -76,6 +76,18 @@ impl ExtensionsFactory for () {
}
}

/// Create a Offchain DB accessor object.
pub trait DbExternalitiesFactory: Send + Sync {
/// Create [`offchain::DbExternalities`] instance.
fn create(&self) -> Box<dyn offchain::DbExternalities>;
}

impl<T: offchain::DbExternalities + Clone + Sync + Send + 'static> DbExternalitiesFactory for T {
fn create(&self) -> Box<dyn offchain::DbExternalities> {
Box::new(self.clone())
}
}

/// A producer of execution extensions for offchain calls.
///
/// This crate aggregates extensions available for the offchain calls
Expand All @@ -84,6 +96,7 @@ impl ExtensionsFactory for () {
pub struct ExecutionExtensions<Block: traits::Block> {
strategies: ExecutionStrategies,
keystore: Option<SyncCryptoStorePtr>,
offchain_db: Option<Box<dyn DbExternalitiesFactory>>,
// FIXME: these two are only RwLock because of https://github.com/paritytech/substrate/issues/4587
// remove when fixed.
// To break retain cycle between `Client` and `TransactionPool` we require this
Expand All @@ -99,6 +112,7 @@ impl<Block: traits::Block> Default for ExecutionExtensions<Block> {
Self {
strategies: Default::default(),
keystore: None,
offchain_db: None,
transaction_pool: RwLock::new(None),
extensions_factory: RwLock::new(Box::new(())),
}
Expand All @@ -110,12 +124,14 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
pub fn new(
strategies: ExecutionStrategies,
keystore: Option<SyncCryptoStorePtr>,
offchain_db: Option<Box<dyn DbExternalitiesFactory>>,
) -> Self {
let transaction_pool = RwLock::new(None);
let extensions_factory = Box::new(());
Self {
strategies,
keystore,
offchain_db,
extensions_factory: RwLock::new(extensions_factory),
transaction_pool,
}
Expand Down Expand Up @@ -164,9 +180,22 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
}
}

if capabilities.has(offchain::Capability::OffchainDbRead) ||
capabilities.has(offchain::Capability::OffchainDbWrite)
{
if let Some(offchain_db) = self.offchain_db.as_ref() {
extensions.register(
OffchainDbExt::new(offchain::LimitedExternalities::new(
capabilities,
offchain_db.create(),
))
);
}
}

if let ExecutionContext::OffchainCall(Some(ext)) = context {
extensions.register(
OffchainExt::new(offchain::LimitedExternalities::new(capabilities, ext.0)),
OffchainWorkerExt::new(offchain::LimitedExternalities::new(capabilities, ext.0)),
);
}

Expand Down
9 changes: 5 additions & 4 deletions client/executor/src/integration_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use codec::{Encode, Decode};
use hex_literal::hex;
use sp_core::{
blake2_128, blake2_256, ed25519, sr25519, map, Pair,
offchain::{OffchainExt, testing},
offchain::{OffchainWorkerExt, OffchainDbExt, testing},
traits::{Externalities, CallInWasm},
};
use sc_runtime_test::wasm_binary_unwrap;
Expand Down Expand Up @@ -468,7 +468,7 @@ test_wasm_execution!(offchain_index);
fn offchain_index(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, _state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainWorkerExt::new(offchain));
call_in_wasm(
"test_offchain_index_set",
&[0],
Expand All @@ -487,7 +487,8 @@ test_wasm_execution!(offchain_local_storage_should_work);
fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
assert_eq!(
call_in_wasm(
"test_offchain_local_storage",
Expand All @@ -504,7 +505,7 @@ test_wasm_execution!(offchain_http_should_work);
fn offchain_http_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainWorkerExt::new(offchain));
state.write().expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:12345".into(),
Expand Down
17 changes: 9 additions & 8 deletions client/offchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,24 @@ targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
bytes = "0.5"
sc-client-api = { version = "3.0.0", path = "../api" }
sp-api = { version = "3.0.0", path = "../../primitives/api" }
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"] }
hex = "0.4"
fnv = "1.0.6"
futures = "0.3.9"
futures-timer = "3.0.1"
log = "0.4.8"
threadpool = "1.7"
num_cpus = "1.10"
sp-offchain = { version = "3.0.0", path = "../../primitives/offchain" }
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"] }
parking_lot = "0.11.1"
sp-core = { version = "3.0.0", path = "../../primitives/core" }
rand = "0.7.2"
sc-client-api = { version = "3.0.0", path = "../api" }
sc-keystore = { version = "3.0.0", path = "../keystore" }
sc-network = { version = "0.9.0", path = "../network" }
sp-api = { version = "3.0.0", path = "../../primitives/api" }
sp-core = { version = "3.0.0", path = "../../primitives/core" }
sp-offchain = { version = "3.0.0", path = "../../primitives/offchain" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }
sp-utils = { version = "3.0.0", path = "../../primitives/utils" }
sc-network = { version = "0.9.0", path = "../network" }
sc-keystore = { version = "3.0.0", path = "../keystore" }
threadpool = "1.7"

[target.'cfg(not(target_os = "unknown"))'.dependencies]
hyper = "0.13.9"
Expand Down
Loading

0 comments on commit 3adefdc

Please sign in to comment.