Introduce rpc client for relay chain full node (#963)

* Initial network interface preparations

* Implement get_storage_by_key

* Implement `validators` and `session_index_for_child`

* Implement persisted_validation_data and candidate_pending_availability

* Fix method name for persisted_validation_data and add encoded params

* Implement `retrieve_dmq_contents` and `retrieve_all_inbound_hrmp_channel_contents`

* Implement `prove_read`

* Introduce separate RPC client, expose JsonRpSee errors

* Simplify closure in call_remote_runtime_function

* Implement import stream, upgrade JsonRpSee

* Implement finality stream

* Remove unused method from interface

* Implement `is_major_syncing`

* Implement `wait_on_block`

* Fix tests

* Unify error handling `ApiError`

* Replace WaitError with RelayChainError

* Wrap BlockChainError in RelayChainError

* Unify error handling in relay chain intefaces

* Fix return type of proof method

* Improve error handling of new methods

* Improve error handling and move logging outside of interface

* Clean up

* Remove unwanted changes, clean up

* Remove unused import

* Add format for StatemachineError and remove nused From trait

* Use 'thiserror' crate to simplify error handling

* Expose error for overseer, further simplify error handling

* Reintroduce network interface

* Implement cli option

* Adjust call_state method to use hashes

* Disable PoV recovery when RPC is used

* Add integration test for network full node

* Use Hash instead of BlockId to ensure compatibility with RPC interface

* Fix cargo check warnings

* Implement retries

* Remove `expect` statements from code

* Update jsonrpsee to 0.8.0 and make collator keys optional

* Make cli arguments conflicting

* Remove unused `block_status` method

* Add clippy fixes

* Cargo fmt

* Validate relay chain rpc url

* Clean up dependencies and add one more integration test

* Clean up

* Clean up dependencies of relay-chain-network

* Use hash instead of blockid for rpc methods

* Fix tests

* Update client/cli/src/lib.rs

Co-authored-by: Koute <koute@users.noreply.github.com>

* Improve error message of cli validation

* Add rpc client constructor

* Do not use debug formatting for errors

* Improve logging for remote runtime methods

* Only retry on transport problems

* Use PHash by value, rename test

* Improve tracing, return error  on relay-chain-interface build

* Fix naming, use generics instead of deserializing manually

* Rename RelayChainLocal and RelayChainNetwork

* lock

* Format

* Use impl trait for encodable runtime payload

* Only instantiate full node in tests when we need it

* Upgrade scale-codec to 3.0.0

* Improve expect log

Co-authored-by: Koute <koute@users.noreply.github.com>
This commit is contained in:
Sebastian Kunert
2022-03-01 12:37:51 +01:00
committed by GitHub
parent 58e02c8497
commit 234c42c2df
34 changed files with 1109 additions and 271 deletions
Generated
+124 -26
View File
@@ -387,6 +387,20 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backoff"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
dependencies = [
"futures-core",
"getrandom 0.2.3",
"instant",
"pin-project-lite 0.2.7",
"rand 0.8.5",
"tokio",
]
[[package]]
name = "backtrace"
version = "0.3.63"
@@ -1575,6 +1589,7 @@ dependencies = [
"clap 3.1.3",
"sc-cli",
"sc-service",
"url 2.2.2",
]
[[package]]
@@ -1687,8 +1702,8 @@ version = "0.1.0"
dependencies = [
"async-trait",
"cumulus-primitives-core",
"cumulus-relay-chain-inprocess-interface",
"cumulus-relay-chain-interface",
"cumulus-relay-chain-local",
"cumulus-test-service",
"derive_more",
"futures 0.3.21",
@@ -1701,6 +1716,7 @@ dependencies = [
"polkadot-primitives",
"polkadot-service",
"polkadot-test-client",
"portpicker",
"sc-cli",
"sc-client-api",
"sc-service",
@@ -1715,6 +1731,7 @@ dependencies = [
"substrate-test-utils",
"tokio",
"tracing",
"url 2.2.2",
]
[[package]]
@@ -1749,6 +1766,7 @@ dependencies = [
name = "cumulus-client-service"
version = "0.1.0"
dependencies = [
"cumulus-client-cli",
"cumulus-client-collator",
"cumulus-client-consensus-common",
"cumulus-client-pov-recovery",
@@ -2010,27 +2028,7 @@ dependencies = [
]
[[package]]
name = "cumulus-relay-chain-interface"
version = "0.1.0"
dependencies = [
"async-trait",
"cumulus-primitives-core",
"derive_more",
"futures 0.3.21",
"parking_lot 0.12.0",
"polkadot-overseer",
"sc-client-api",
"sc-service",
"sp-api",
"sp-blockchain",
"sp-core",
"sp-runtime",
"sp-state-machine",
"thiserror",
]
[[package]]
name = "cumulus-relay-chain-local"
name = "cumulus-relay-chain-inprocess-interface"
version = "0.1.0"
dependencies = [
"async-trait",
@@ -2060,6 +2058,54 @@ dependencies = [
"tracing",
]
[[package]]
name = "cumulus-relay-chain-interface"
version = "0.1.0"
dependencies = [
"async-trait",
"cumulus-primitives-core",
"derive_more",
"futures 0.3.21",
"jsonrpsee-core",
"parity-scale-codec",
"parking_lot 0.12.0",
"polkadot-overseer",
"polkadot-service",
"sc-client-api",
"sc-service",
"sp-api",
"sp-blockchain",
"sp-core",
"sp-runtime",
"sp-state-machine",
"thiserror",
]
[[package]]
name = "cumulus-relay-chain-rpc-interface"
version = "0.1.0"
dependencies = [
"async-trait",
"backoff",
"cumulus-primitives-core",
"cumulus-relay-chain-interface",
"futures 0.3.21",
"futures-timer",
"jsonrpsee 0.8.0",
"parity-scale-codec",
"parking_lot 0.11.2",
"polkadot-service",
"sc-client-api",
"sc-rpc-api",
"sp-api",
"sp-core",
"sp-runtime",
"sp-state-machine",
"sp-storage",
"tracing",
"url 2.2.2",
]
[[package]]
name = "cumulus-test-client"
version = "0.1.0"
@@ -2148,13 +2194,16 @@ version = "0.1.0"
dependencies = [
"async-trait",
"criterion",
"cumulus-client-cli",
"cumulus-client-consensus-common",
"cumulus-client-consensus-relay-chain",
"cumulus-client-network",
"cumulus-client-service",
"cumulus-primitives-core",
"cumulus-primitives-parachain-inherent",
"cumulus-relay-chain-local",
"cumulus-relay-chain-inprocess-interface",
"cumulus-relay-chain-interface",
"cumulus-relay-chain-rpc-interface",
"cumulus-test-relay-validation-worker-provider",
"cumulus-test-runtime",
"frame-system",
@@ -2167,6 +2216,7 @@ dependencies = [
"polkadot-primitives",
"polkadot-service",
"polkadot-test-service",
"portpicker",
"rand 0.8.5",
"sc-basic-authorship",
"sc-chain-spec",
@@ -2194,6 +2244,7 @@ dependencies = [
"substrate-test-client",
"substrate-test-utils",
"tokio",
"url 2.2.2",
]
[[package]]
@@ -3458,6 +3509,22 @@ dependencies = [
"webpki 0.21.4",
]
[[package]]
name = "hyper-rustls"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
dependencies = [
"http",
"hyper",
"log",
"rustls 0.20.2",
"rustls-native-certs 0.6.1",
"tokio",
"tokio-rustls 0.23.2",
"webpki-roots 0.22.2",
]
[[package]]
name = "idna"
version = "0.1.5"
@@ -3812,6 +3879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05fd8cd6c6b1bbd06881d2cf88f1fc83cc36c98f2219090f839115fb4a956cb9"
dependencies = [
"jsonrpsee-core",
"jsonrpsee-http-client",
"jsonrpsee-proc-macros",
"jsonrpsee-types 0.8.0",
"jsonrpsee-ws-client 0.8.0",
@@ -3861,6 +3929,25 @@ dependencies = [
"tracing",
]
[[package]]
name = "jsonrpsee-http-client"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dce69e96aa236cc2e3a20467420b31cbc8464703aa95bc33a163d25b0f56023"
dependencies = [
"async-trait",
"hyper",
"hyper-rustls 0.23.0",
"jsonrpsee-core",
"jsonrpsee-types 0.8.0",
"rustc-hash",
"serde",
"serde_json",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "jsonrpsee-proc-macros"
version = "0.8.0"
@@ -6463,8 +6550,9 @@ dependencies = [
"cumulus-client-service",
"cumulus-primitives-core",
"cumulus-primitives-parachain-inherent",
"cumulus-relay-chain-inprocess-interface",
"cumulus-relay-chain-interface",
"cumulus-relay-chain-local",
"cumulus-relay-chain-rpc-interface",
"derive_more",
"frame-benchmarking",
"frame-benchmarking-cli",
@@ -7119,8 +7207,9 @@ dependencies = [
"cumulus-client-service",
"cumulus-primitives-core",
"cumulus-primitives-parachain-inherent",
"cumulus-relay-chain-inprocess-interface",
"cumulus-relay-chain-interface",
"cumulus-relay-chain-local",
"cumulus-relay-chain-rpc-interface",
"frame-benchmarking",
"frame-benchmarking-cli",
"futures 0.3.21",
@@ -8352,6 +8441,15 @@ dependencies = [
"universal-hash",
]
[[package]]
name = "portpicker"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9"
dependencies = [
"rand 0.8.5",
]
[[package]]
name = "ppv-lite86"
version = "0.2.15"
@@ -9793,7 +9891,7 @@ dependencies = [
"futures-timer",
"hex",
"hyper",
"hyper-rustls",
"hyper-rustls 0.22.1",
"num_cpus",
"once_cell",
"parity-scale-codec",
+2 -1
View File
@@ -8,7 +8,8 @@ members = [
"client/pov-recovery",
"client/service",
"client/relay-chain-interface",
"client/relay-chain-local",
"client/relay-chain-inprocess-interface",
"client/relay-chain-rpc-interface",
"pallets/aura-ext",
"pallets/collator-selection",
"pallets/dmp-queue",
+1
View File
@@ -10,3 +10,4 @@ clap = { version = "3.1", features = ["derive"] }
# Substrate dependencies
sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" }
url = "2.2.2"
+36 -1
View File
@@ -19,7 +19,6 @@
#![warn(missing_docs)]
use clap::Parser;
use sc_cli;
use sc_service::{
config::{PrometheusConfig, TelemetryEndpoints},
BasePath, TransactionPoolOptions,
@@ -29,6 +28,7 @@ use std::{
io::{self, Write},
net::SocketAddr,
};
use url::Url;
/// The `purge-chain` command used to remove the whole chain: the parachain and the relay chain.
#[derive(Debug, Parser)]
@@ -119,6 +119,19 @@ impl sc_cli::CliConfiguration for PurgeChainCmd {
}
}
fn validate_relay_chain_url(arg: &str) -> Result<(), String> {
let url = Url::parse(arg).map_err(|e| e.to_string())?;
if url.scheme() == "ws" {
Ok(())
} else {
Err(format!(
"'{}' URL scheme not supported. Only websocket RPC is currently supported",
url.scheme()
))
}
}
/// The `run` command used to run a node.
#[derive(Debug, Parser)]
pub struct RunCmd {
@@ -131,6 +144,23 @@ pub struct RunCmd {
/// Note that this is the same as running with `--validator`.
#[clap(long, conflicts_with = "validator")]
pub collator: bool,
/// EXPERIMENTAL: Specify an URL to a relay chain full node to communicate with.
#[clap(
long,
parse(try_from_str),
validator = validate_relay_chain_url,
conflicts_with = "collator",
conflicts_with = "validator"
)]
pub relay_chain_rpc_url: Option<Url>,
}
/// Options only relevant for collator nodes
#[derive(Clone, Debug)]
pub struct CollatorOptions {
/// Location of relay chain full node
pub relay_chain_rpc_url: Option<Url>,
}
/// A non-redundant version of the `RunCmd` that sets the `validator` field when the
@@ -150,6 +180,11 @@ impl RunCmd {
NormalizedRunCmd { base: new_base }
}
/// Create [`CollatorOptions`] representing options only relevant to parachain collator nodes
pub fn collator_options(&self) -> CollatorOptions {
CollatorOptions { relay_chain_rpc_url: self.relay_chain_rpc_url.clone() }
}
}
impl sc_cli::CliConfiguration for NormalizedRunCmd {
@@ -27,7 +27,7 @@ use sp_runtime::{
traits::{Block as BlockT, Header as HeaderT},
};
use polkadot_primitives::v1::{Block as PBlock, Id as ParaId, OccupiedCoreAssumption};
use polkadot_primitives::v1::{Hash as PHash, Id as ParaId, OccupiedCoreAssumption};
use codec::Decode;
use futures::{select, FutureExt, Stream, StreamExt};
@@ -54,7 +54,7 @@ pub trait RelaychainClient: Clone + 'static {
/// Returns the parachain head for the given `para_id` at the given block id.
async fn parachain_head_at(
&self,
at: &BlockId<PBlock>,
at: PHash,
para_id: ParaId,
) -> RelayChainResult<Option<Vec<u8>>>;
}
@@ -402,13 +402,7 @@ where
.await?
.filter_map(move |n| {
let relay_chain = relay_chain.clone();
async move {
relay_chain
.parachain_head_at(&BlockId::hash(n.hash()), para_id)
.await
.ok()
.flatten()
}
async move { relay_chain.parachain_head_at(n.hash(), para_id).await.ok().flatten() }
})
.boxed();
Ok(new_best_notification_stream)
@@ -422,13 +416,7 @@ where
.await?
.filter_map(move |n| {
let relay_chain = relay_chain.clone();
async move {
relay_chain
.parachain_head_at(&BlockId::hash(n.hash()), para_id)
.await
.ok()
.flatten()
}
async move { relay_chain.parachain_head_at(n.hash(), para_id).await.ok().flatten() }
})
.boxed();
Ok(finality_notification_stream)
@@ -436,7 +424,7 @@ where
async fn parachain_head_at(
&self,
at: &BlockId<PBlock>,
at: PHash,
para_id: ParaId,
) -> RelayChainResult<Option<Vec<u8>>> {
self.persisted_validation_data(at, para_id, OccupiedCoreAssumption::TimedOut)
+2 -6
View File
@@ -25,7 +25,7 @@ use cumulus_test_client::{
};
use futures::{channel::mpsc, executor::block_on, select, FutureExt, Stream, StreamExt};
use futures_timer::Delay;
use polkadot_primitives::v1::{Block as PBlock, Id as ParaId};
use polkadot_primitives::v1::Id as ParaId;
use sc_client_api::UsageProvider;
use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy};
use sp_blockchain::Error as ClientError;
@@ -98,11 +98,7 @@ impl crate::parachain_consensus::RelaychainClient for Relaychain {
Ok(Box::new(stream.map(|v| v.encode())))
}
async fn parachain_head_at(
&self,
_: &BlockId<PBlock>,
_: ParaId,
) -> RelayChainResult<Option<Vec<u8>>> {
async fn parachain_head_at(&self, _: PHash, _: ParaId) -> RelayChainResult<Option<Vec<u8>>> {
unimplemented!("Not required for tests")
}
}
+3 -1
View File
@@ -32,12 +32,14 @@ derive_more = "0.99.2"
async-trait = "0.1.52"
[dev-dependencies]
portpicker = "0.1.1"
url = "2.2.2"
tokio = { version = "1.17.0", features = ["macros"] }
# Cumulus deps
cumulus-test-service = { path = "../../test/service" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-relay-chain-local = { path = "../relay-chain-local" }
cumulus-relay-chain-inprocess-interface = { path = "../relay-chain-inprocess-interface" }
# Polkadot deps
polkadot-test-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
+13 -22
View File
@@ -24,17 +24,14 @@ use sp_consensus::block_validation::{
BlockAnnounceValidator as BlockAnnounceValidatorT, Validation,
};
use sp_core::traits::SpawnNamed;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT},
};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use cumulus_relay_chain_interface::RelayChainInterface;
use polkadot_node_primitives::{CollationSecondedSignal, Statement};
use polkadot_parachain::primitives::HeadData;
use polkadot_primitives::v1::{
Block as PBlock, CandidateReceipt, CompactStatement, Hash as PHash, Id as ParaId,
OccupiedCoreAssumption, SigningContext, UncheckedSigned,
CandidateReceipt, CompactStatement, Hash as PHash, Id as ParaId, OccupiedCoreAssumption,
SigningContext, UncheckedSigned,
};
use codec::{Decode, DecodeAll, Encode};
@@ -133,9 +130,8 @@ impl BlockAnnounceData {
{
let validator_index = self.statement.unchecked_validator_index();
let runtime_api_block_id = BlockId::Hash(self.relay_parent);
let session_index =
match relay_chain_client.session_index_for_child(&runtime_api_block_id).await {
match relay_chain_client.session_index_for_child(self.relay_parent).await {
Ok(r) => r,
Err(e) => return Err(BlockAnnounceError(format!("{:?}", e))),
};
@@ -143,7 +139,7 @@ impl BlockAnnounceData {
let signing_context = SigningContext { parent_hash: self.relay_parent, session_index };
// Check that the signer is a legit validator.
let authorities = match relay_chain_client.validators(&runtime_api_block_id).await {
let authorities = match relay_chain_client.validators(self.relay_parent).await {
Ok(r) => r,
Err(e) => return Err(BlockAnnounceError(format!("{:?}", e))),
};
@@ -160,7 +156,7 @@ impl BlockAnnounceData {
};
// Check statement is correctly signed.
if self.statement.try_into_checked(&signing_context, &signer).is_err() {
if self.statement.try_into_checked(&signing_context, signer).is_err() {
tracing::debug!(
target: LOG_TARGET,
"Block announcement justification signature is invalid.",
@@ -231,11 +227,7 @@ where
{
/// Create a new [`BlockAnnounceValidator`].
pub fn new(relay_chain_interface: RCInterface, para_id: ParaId) -> Self {
Self {
phantom: Default::default(),
relay_chain_interface: relay_chain_interface.clone(),
para_id,
}
Self { phantom: Default::default(), relay_chain_interface, para_id }
}
}
@@ -246,11 +238,11 @@ where
/// Get the included block of the given parachain in the relay chain.
async fn included_block(
relay_chain_interface: &RCInterface,
block_id: &BlockId<PBlock>,
hash: PHash,
para_id: ParaId,
) -> Result<Block::Header, BoxedError> {
let validation_data = relay_chain_interface
.persisted_validation_data(block_id, para_id, OccupiedCoreAssumption::TimedOut)
.persisted_validation_data(hash, para_id, OccupiedCoreAssumption::TimedOut)
.await
.map_err(|e| Box::new(BlockAnnounceError(format!("{:?}", e))) as Box<_>)?
.ok_or_else(|| {
@@ -269,11 +261,11 @@ where
/// Get the backed block hash of the given parachain in the relay chain.
async fn backed_block_hash(
relay_chain_interface: &RCInterface,
block_id: &BlockId<PBlock>,
hash: PHash,
para_id: ParaId,
) -> Result<Option<PHash>, BoxedError> {
let candidate_receipt = relay_chain_interface
.candidate_pending_availability(block_id, para_id)
.candidate_pending_availability(hash, para_id)
.await
.map_err(|e| Box::new(BlockAnnounceError(format!("{:?}", e))) as Box<_>)?;
@@ -293,14 +285,13 @@ where
.best_block_hash()
.await
.map_err(|e| Box::new(e) as Box<_>)?;
let runtime_api_block_id = BlockId::Hash(relay_chain_best_hash);
let block_number = header.number();
let best_head =
Self::included_block(&relay_chain_interface, &runtime_api_block_id, para_id).await?;
Self::included_block(&relay_chain_interface, relay_chain_best_hash, para_id).await?;
let known_best_number = best_head.number();
let backed_block = || async {
Self::backed_block_hash(&relay_chain_interface, &runtime_api_block_id, para_id).await
Self::backed_block_hash(&relay_chain_interface, relay_chain_best_hash, para_id).await
};
if best_head == header {
+9 -31
View File
@@ -16,8 +16,8 @@
use super::*;
use async_trait::async_trait;
use cumulus_relay_chain_inprocess_interface::{check_block_in_chain, BlockCheckStatus};
use cumulus_relay_chain_interface::{RelayChainError, RelayChainResult};
use cumulus_relay_chain_local::{check_block_in_chain, BlockCheckStatus};
use cumulus_test_service::runtime::{Block, Hash, Header};
use futures::{executor::block_on, poll, task::Poll, FutureExt, Stream, StreamExt};
use parking_lot::Mutex;
@@ -77,23 +77,10 @@ impl DummyRelayChainInterface {
#[async_trait]
impl RelayChainInterface for DummyRelayChainInterface {
async fn validators(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
) -> RelayChainResult<Vec<ValidatorId>> {
async fn validators(&self, _: PHash) -> RelayChainResult<Vec<ValidatorId>> {
Ok(self.data.lock().validators.clone())
}
async fn block_status(
&self,
block_id: cumulus_primitives_core::relay_chain::BlockId,
) -> RelayChainResult<sp_blockchain::BlockStatus> {
self.relay_backend
.blockchain()
.status(block_id)
.map_err(RelayChainError::BlockchainError)
}
async fn best_block_hash(&self) -> RelayChainResult<PHash> {
Ok(self.relay_backend.blockchain().info().best_hash)
}
@@ -116,7 +103,7 @@ impl RelayChainInterface for DummyRelayChainInterface {
async fn persisted_validation_data(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
_: PHash,
_: ParaId,
_: OccupiedCoreAssumption,
) -> RelayChainResult<Option<PersistedValidationData>> {
@@ -128,7 +115,7 @@ impl RelayChainInterface for DummyRelayChainInterface {
async fn candidate_pending_availability(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
_: PHash,
_: ParaId,
) -> RelayChainResult<Option<CommittedCandidateReceipt>> {
if self.data.lock().has_pending_availability {
@@ -159,10 +146,7 @@ impl RelayChainInterface for DummyRelayChainInterface {
}
}
async fn session_index_for_child(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
) -> RelayChainResult<SessionIndex> {
async fn session_index_for_child(&self, _: PHash) -> RelayChainResult<SessionIndex> {
Ok(0)
}
@@ -196,7 +180,7 @@ impl RelayChainInterface for DummyRelayChainInterface {
async fn get_storage_by_key(
&self,
_: &polkadot_service::BlockId,
_: PHash,
_: &[u8],
) -> RelayChainResult<Option<StorageValue>> {
unimplemented!("Not needed for test")
@@ -204,7 +188,7 @@ impl RelayChainInterface for DummyRelayChainInterface {
async fn prove_read(
&self,
_: &polkadot_service::BlockId,
_: PHash,
_: &Vec<Vec<u8>>,
) -> RelayChainResult<sc_client_api::StorageProof> {
unimplemented!("Not needed for test")
@@ -293,10 +277,7 @@ async fn make_gossip_message_and_header(
Some(&Sr25519Keyring::Alice.to_seed()),
)
.unwrap();
let session_index = relay_chain_interface
.session_index_for_child(&BlockId::Hash(relay_parent))
.await
.unwrap();
let session_index = relay_chain_interface.session_index_for_child(relay_parent).await.unwrap();
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let header = default_header();
@@ -477,10 +458,7 @@ async fn check_statement_seconded() {
Some(&Sr25519Keyring::Alice.to_seed()),
)
.unwrap();
let session_index = relay_chain_interface
.session_index_for_child(&BlockId::Hash(relay_parent))
.await
.unwrap();
let session_index = relay_chain_interface.session_index_for_child(relay_parent).await.unwrap();
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let statement = Statement::Valid(Default::default());
@@ -16,6 +16,7 @@
use cumulus_primitives_core::ParaId;
use cumulus_test_service::{initial_head_data, run_relay_chain_validator_node, Keyring::*};
use futures::join;
#[substrate_test_utils::test]
#[ignore]
@@ -27,12 +28,24 @@ async fn sync_blocks_from_tip_without_being_connected_to_a_collator() {
let para_id = ParaId::from(100);
let tokio_handle = tokio::runtime::Handle::current();
let ws_port = portpicker::pick_unused_port().expect("No free ports");
// start alice
let alice = run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, Vec::new());
let alice = run_relay_chain_validator_node(
tokio_handle.clone(),
Alice,
|| {},
Vec::new(),
Some(ws_port),
);
// start bob
let bob =
run_relay_chain_validator_node(tokio_handle.clone(), Bob, || {}, vec![alice.addr.clone()]);
let bob = run_relay_chain_validator_node(
tokio_handle.clone(),
Bob,
|| {},
vec![alice.addr.clone()],
None,
);
// register parachain
alice
@@ -62,12 +75,21 @@ async fn sync_blocks_from_tip_without_being_connected_to_a_collator() {
.await;
// run eve as parachain full node that is only connected to dave
let eve = cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle, Eve)
let eve = cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Eve)
.connect_to_parachain_node(&dave)
.exclusively_connect_to_registered_parachain_nodes()
.connect_to_relay_chain_nodes(vec![&alice, &bob])
.build()
.await;
eve.wait_for_blocks(7).await;
// run eve as parachain full node that is only connected to dave
let ferdie = cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle, Ferdie)
.connect_to_parachain_node(&dave)
.exclusively_connect_to_registered_parachain_nodes()
.connect_to_relay_chain_nodes(vec![&alice, &bob])
.use_external_relay_chain_node_at_port(ws_port)
.build()
.await;
join!(ferdie.wait_for_blocks(7), eve.wait_for_blocks(7));
}
+3 -3
View File
@@ -451,9 +451,9 @@ async fn pending_candidates(
let filtered_stream = import_notification_stream.filter_map(move |n| {
let client_for_closure = relay_chain_client.clone();
async move {
let block_id = BlockId::hash(n.hash());
let hash = n.hash();
let pending_availability_result = client_for_closure
.candidate_pending_availability(&block_id, para_id)
.candidate_pending_availability(hash, para_id)
.await
.map_err(|e| {
tracing::error!(
@@ -463,7 +463,7 @@ async fn pending_candidates(
)
});
let session_index_result =
client_for_closure.session_index_for_child(&block_id).await.map_err(|e| {
client_for_closure.session_index_for_child(hash).await.map_err(|e| {
tracing::error!(
target: LOG_TARGET,
error = ?e,
@@ -39,6 +39,7 @@ async fn pov_recovery() {
Alice,
|| {},
Vec::new(),
None,
);
// Start bob
@@ -47,6 +48,7 @@ async fn pov_recovery() {
Bob,
|| {},
vec![alice.addr.clone()],
None,
);
// Register parachain
@@ -1,6 +1,6 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
name = "cumulus-relay-chain-local"
name = "cumulus-relay-chain-inprocess-interface"
version = "0.1.0"
edition = "2021"
@@ -28,7 +28,7 @@ sc-tracing = { git = "https://github.com/paritytech/substrate", branch = "master
parking_lot = "0.12.0"
tracing = "0.1.31"
async-trait = "0.1.52"
futures = { version = "0.3.1", features = ["compat"] }
futures = "0.3.21"
futures-timer = "3.0.2"
[dev-dependencies]
@@ -46,15 +46,15 @@ use sp_state_machine::{Backend as StateBackend, StorageValue};
const TIMEOUT_IN_SECONDS: u64 = 6;
/// Provides an implementation of the [`RelayChainInterface`] using a local in-process relay chain node.
pub struct RelayChainLocal<Client> {
pub struct RelayChainInProcessInterface<Client> {
full_client: Arc<Client>,
backend: Arc<FullBackend>,
sync_oracle: Arc<Mutex<Box<dyn SyncOracle + Send + Sync>>>,
overseer_handle: Option<Handle>,
}
impl<Client> RelayChainLocal<Client> {
/// Create a new instance of [`RelayChainLocal`]
impl<Client> RelayChainInProcessInterface<Client> {
/// Create a new instance of [`RelayChainInProcessInterface`]
pub fn new(
full_client: Arc<Client>,
backend: Arc<FullBackend>,
@@ -65,7 +65,7 @@ impl<Client> RelayChainLocal<Client> {
}
}
impl<T> Clone for RelayChainLocal<T> {
impl<T> Clone for RelayChainInProcessInterface<T> {
fn clone(&self) -> Self {
Self {
full_client: self.full_client.clone(),
@@ -77,7 +77,7 @@ impl<T> Clone for RelayChainLocal<T> {
}
#[async_trait]
impl<Client> RelayChainInterface for RelayChainLocal<Client>
impl<Client> RelayChainInterface for RelayChainInProcessInterface<Client>
where
Client: ProvideRuntimeApi<PBlock>
+ BlockchainEvents<PBlock>
@@ -113,12 +113,12 @@ where
async fn persisted_validation_data(
&self,
block_id: &BlockId,
hash: PHash,
para_id: ParaId,
occupied_core_assumption: OccupiedCoreAssumption,
) -> RelayChainResult<Option<PersistedValidationData>> {
Ok(self.full_client.runtime_api().persisted_validation_data(
block_id,
&BlockId::Hash(hash),
para_id,
occupied_core_assumption,
)?)
@@ -126,21 +126,21 @@ where
async fn candidate_pending_availability(
&self,
block_id: &BlockId,
hash: PHash,
para_id: ParaId,
) -> RelayChainResult<Option<CommittedCandidateReceipt>> {
Ok(self
.full_client
.runtime_api()
.candidate_pending_availability(block_id, para_id)?)
.candidate_pending_availability(&BlockId::Hash(hash), para_id)?)
}
async fn session_index_for_child(&self, block_id: &BlockId) -> RelayChainResult<SessionIndex> {
Ok(self.full_client.runtime_api().session_index_for_child(block_id)?)
async fn session_index_for_child(&self, hash: PHash) -> RelayChainResult<SessionIndex> {
Ok(self.full_client.runtime_api().session_index_for_child(&BlockId::Hash(hash))?)
}
async fn validators(&self, block_id: &BlockId) -> RelayChainResult<Vec<ValidatorId>> {
Ok(self.full_client.runtime_api().validators(block_id)?)
async fn validators(&self, hash: PHash) -> RelayChainResult<Vec<ValidatorId>> {
Ok(self.full_client.runtime_api().validators(&BlockId::Hash(hash))?)
}
async fn import_notification_stream(
@@ -167,10 +167,6 @@ where
Ok(self.backend.blockchain().info().best_hash)
}
async fn block_status(&self, block_id: BlockId) -> RelayChainResult<BlockStatus> {
Ok(self.backend.blockchain().status(block_id)?)
}
async fn is_major_syncing(&self) -> RelayChainResult<bool> {
let mut network = self.sync_oracle.lock();
Ok(network.is_major_syncing())
@@ -182,19 +178,21 @@ where
async fn get_storage_by_key(
&self,
block_id: &BlockId,
relay_parent: PHash,
key: &[u8],
) -> RelayChainResult<Option<StorageValue>> {
let state = self.backend.state_at(*block_id)?;
let block_id = BlockId::Hash(relay_parent);
let state = self.backend.state_at(block_id)?;
state.storage(key).map_err(RelayChainError::GenericError)
}
async fn prove_read(
&self,
block_id: &BlockId,
relay_parent: PHash,
relevant_keys: &Vec<Vec<u8>>,
) -> RelayChainResult<StorageProof> {
let state_backend = self.backend.state_at(*block_id)?;
let block_id = BlockId::Hash(relay_parent);
let state_backend = self.backend.state_at(block_id)?;
sp_state_machine::prove_read(state_backend, relevant_keys)
.map_err(RelayChainError::StateMachineError)
@@ -271,9 +269,9 @@ where
let _lock = backend.get_import_lock().read();
let block_id = BlockId::Hash(hash);
match backend.blockchain().status(block_id)? {
BlockStatus::InChain => return Ok(BlockCheckStatus::InChain),
_ => {},
if backend.blockchain().status(block_id)? == BlockStatus::InChain {
return Ok(BlockCheckStatus::InChain)
}
let listener = client.import_notification_stream();
@@ -282,25 +280,25 @@ where
}
/// Builder for a concrete relay chain interface, created from a full node. Builds
/// a [`RelayChainLocal`] to access relay chain data necessary for parachain operation.
/// a [`RelayChainInProcessInterface`] to access relay chain data necessary for parachain operation.
///
/// The builder takes a [`polkadot_client::Client`]
/// that wraps a concrete instance. By using [`polkadot_client::ExecuteWithClient`]
/// the builder gets access to this concrete instance and instantiates a [`RelayChainLocal`] with it.
struct RelayChainLocalBuilder {
/// the builder gets access to this concrete instance and instantiates a [`RelayChainInProcessInterface`] with it.
struct RelayChainInProcessInterfaceBuilder {
polkadot_client: polkadot_client::Client,
backend: Arc<FullBackend>,
sync_oracle: Arc<Mutex<Box<dyn SyncOracle + Send + Sync>>>,
overseer_handle: Option<Handle>,
}
impl RelayChainLocalBuilder {
impl RelayChainInProcessInterfaceBuilder {
pub fn build(self) -> Arc<dyn RelayChainInterface> {
self.polkadot_client.clone().execute_with(self)
}
}
impl ExecuteWithClient for RelayChainLocalBuilder {
impl ExecuteWithClient for RelayChainInProcessInterfaceBuilder {
type Output = Arc<dyn RelayChainInterface>;
fn execute_with_client<Client, Api, Backend>(self, client: Arc<Client>) -> Self::Output
@@ -314,7 +312,12 @@ impl ExecuteWithClient for RelayChainLocalBuilder {
+ Send,
Client::Api: ParachainHost<PBlock> + BabeApi<PBlock>,
{
Arc::new(RelayChainLocal::new(client, self.backend, self.sync_oracle, self.overseer_handle))
Arc::new(RelayChainInProcessInterface::new(
client,
self.backend,
self.sync_oracle,
self.overseer_handle,
))
}
}
@@ -346,7 +349,7 @@ fn build_polkadot_full_node(
}
/// Builds a relay chain interface by constructing a full relay chain node
pub fn build_relay_chain_interface(
pub fn build_inprocess_relay_chain(
polkadot_config: Configuration,
telemetry_worker_handle: Option<TelemetryWorkerHandle>,
task_manager: &mut TaskManager,
@@ -361,7 +364,7 @@ pub fn build_relay_chain_interface(
let sync_oracle: Box<dyn SyncOracle + Send + Sync> = Box::new(full_node.network.clone());
let sync_oracle = Arc::new(Mutex::new(sync_oracle));
let relay_chain_interface_builder = RelayChainLocalBuilder {
let relay_chain_interface_builder = RelayChainInProcessInterfaceBuilder {
polkadot_client: full_node.client.clone(),
backend: full_node.backend.clone(),
sync_oracle,
@@ -402,7 +405,8 @@ mod tests {
}
}
fn build_client_backend_and_block() -> (Arc<Client>, PBlock, RelayChainLocal<Client>) {
fn build_client_backend_and_block(
) -> (Arc<Client>, PBlock, RelayChainInProcessInterface<Client>) {
let builder =
TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::NativeWhenPossible);
let backend = builder.backend();
@@ -415,7 +419,7 @@ mod tests {
(
client.clone(),
block,
RelayChainLocal::new(
RelayChainInProcessInterface::new(
client,
backend.clone(),
Arc::new(Mutex::new(dummy_network)),
+4 -1
View File
@@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
polkadot-overseer = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
cumulus-primitives-core = { path = "../../primitives/core" }
@@ -17,8 +18,10 @@ sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" }
futures = "0.3.1"
futures = "0.3.21"
parking_lot = "0.12.0"
derive_more = "0.99.2"
async-trait = "0.1.52"
thiserror = "1.0.30"
jsonrpsee-core = "0.8.0"
parity-scale-codec = "3.0.0"
+37 -28
View File
@@ -19,16 +19,18 @@ use std::{collections::BTreeMap, pin::Pin, sync::Arc};
use cumulus_primitives_core::{
relay_chain::{
v1::{CommittedCandidateReceipt, OccupiedCoreAssumption, SessionIndex, ValidatorId},
BlockId, Hash as PHash, Header as PHeader, InboundHrmpMessage,
Hash as PHash, Header as PHeader, InboundHrmpMessage,
},
InboundDownwardMessage, ParaId, PersistedValidationData,
};
use polkadot_overseer::Handle as OverseerHandle;
use sc_client_api::{blockchain::BlockStatus, StorageProof};
use sc_client_api::StorageProof;
use futures::Stream;
use async_trait::async_trait;
use jsonrpsee_core::Error as JsonRPSeeError;
use parity_scale_codec::Error as CodecError;
use sp_api::ApiError;
use sp_state_machine::StorageValue;
@@ -36,37 +38,48 @@ pub type RelayChainResult<T> = Result<T, RelayChainError>;
#[derive(thiserror::Error, Debug)]
pub enum RelayChainError {
#[error("Error occurred while calling relay chain runtime: {0:?}")]
#[error("Error occured while calling relay chain runtime: {0}")]
ApiError(#[from] ApiError),
#[error("Timeout while waiting for relay-chain block `{0}` to be imported.")]
WaitTimeout(PHash),
#[error("Import listener closed while waiting for relay-chain block `{0}` to be imported.")]
ImportListenerClosed(PHash),
#[error("Blockchain returned an error while waiting for relay-chain block `{0}` to be imported: {1:?}")]
#[error("Blockchain returned an error while waiting for relay-chain block `{0}` to be imported: {1}")]
WaitBlockchainError(PHash, sp_blockchain::Error),
#[error("Blockchain returned an error: {0:?}")]
#[error("Blockchain returned an error: {0}")]
BlockchainError(#[from] sp_blockchain::Error),
#[error("State machine error occurred: {0:?}")]
#[error("State machine error occured: {0}")]
StateMachineError(Box<dyn sp_state_machine::Error>),
#[error("Unspecified error occurred: {0:?}")]
#[error("Unable to call RPC method '{0}' due to error: {1}")]
RPCCallError(String, JsonRPSeeError),
#[error("RPC Error: '{0}'")]
JsonRPCError(#[from] JsonRPSeeError),
#[error("Scale codec deserialization error: {0}")]
DeserializationError(CodecError),
#[error("Scale codec deserialization error: {0}")]
ServiceError(#[from] polkadot_service::Error),
#[error("Unspecified error occured: {0}")]
GenericError(String),
}
impl From<CodecError> for RelayChainError {
fn from(e: CodecError) -> Self {
RelayChainError::DeserializationError(e)
}
}
/// Trait that provides all necessary methods for interaction between collator and relay chain.
#[async_trait]
pub trait RelayChainInterface: Send + Sync {
/// Fetch a storage item by key.
async fn get_storage_by_key(
&self,
block_id: &BlockId,
relay_parent: PHash,
key: &[u8],
) -> RelayChainResult<Option<StorageValue>>;
/// Fetch a vector of current validators.
async fn validators(&self, block_id: &BlockId) -> RelayChainResult<Vec<ValidatorId>>;
/// Get the status of a given block.
async fn block_status(&self, block_id: BlockId) -> RelayChainResult<BlockStatus>;
async fn validators(&self, block_id: PHash) -> RelayChainResult<Vec<ValidatorId>>;
/// Get the hash of the current best block.
async fn best_block_hash(&self) -> RelayChainResult<PHash>;
@@ -98,7 +111,7 @@ pub trait RelayChainInterface: Send + Sync {
/// and the para already occupies a core.
async fn persisted_validation_data(
&self,
block_id: &BlockId,
block_id: PHash,
para_id: ParaId,
_: OccupiedCoreAssumption,
) -> RelayChainResult<Option<PersistedValidationData>>;
@@ -107,12 +120,12 @@ pub trait RelayChainInterface: Send + Sync {
/// assigned to occupied cores in `availability_cores` and `None` otherwise.
async fn candidate_pending_availability(
&self,
block_id: &BlockId,
block_id: PHash,
para_id: ParaId,
) -> RelayChainResult<Option<CommittedCandidateReceipt>>;
/// Returns the session index expected at a child of the block.
async fn session_index_for_child(&self, block_id: &BlockId) -> RelayChainResult<SessionIndex>;
async fn session_index_for_child(&self, block_id: PHash) -> RelayChainResult<SessionIndex>;
/// Get a stream of import block notifications.
async fn import_notification_stream(
@@ -145,7 +158,7 @@ pub trait RelayChainInterface: Send + Sync {
/// Generate a storage read proof.
async fn prove_read(
&self,
block_id: &BlockId,
relay_parent: PHash,
relevant_keys: &Vec<Vec<u8>>,
) -> RelayChainResult<StorageProof>;
}
@@ -173,7 +186,7 @@ where
async fn persisted_validation_data(
&self,
block_id: &BlockId,
block_id: PHash,
para_id: ParaId,
occupied_core_assumption: OccupiedCoreAssumption,
) -> RelayChainResult<Option<PersistedValidationData>> {
@@ -184,17 +197,17 @@ where
async fn candidate_pending_availability(
&self,
block_id: &BlockId,
block_id: PHash,
para_id: ParaId,
) -> RelayChainResult<Option<CommittedCandidateReceipt>> {
(**self).candidate_pending_availability(block_id, para_id).await
}
async fn session_index_for_child(&self, block_id: &BlockId) -> RelayChainResult<SessionIndex> {
async fn session_index_for_child(&self, block_id: PHash) -> RelayChainResult<SessionIndex> {
(**self).session_index_for_child(block_id).await
}
async fn validators(&self, block_id: &BlockId) -> RelayChainResult<Vec<ValidatorId>> {
async fn validators(&self, block_id: PHash) -> RelayChainResult<Vec<ValidatorId>> {
(**self).validators(block_id).await
}
@@ -214,10 +227,6 @@ where
(**self).best_block_hash().await
}
async fn block_status(&self, block_id: BlockId) -> RelayChainResult<BlockStatus> {
(**self).block_status(block_id).await
}
async fn is_major_syncing(&self) -> RelayChainResult<bool> {
(**self).is_major_syncing().await
}
@@ -228,18 +237,18 @@ where
async fn get_storage_by_key(
&self,
block_id: &BlockId,
relay_parent: PHash,
key: &[u8],
) -> RelayChainResult<Option<StorageValue>> {
(**self).get_storage_by_key(block_id, key).await
(**self).get_storage_by_key(relay_parent, key).await
}
async fn prove_read(
&self,
block_id: &BlockId,
relay_parent: PHash,
relevant_keys: &Vec<Vec<u8>>,
) -> RelayChainResult<StorageProof> {
(**self).prove_read(block_id, relevant_keys).await
(**self).prove_read(relay_parent, relevant_keys).await
}
async fn wait_for_block(&self, hash: PHash) -> RelayChainResult<()> {
@@ -0,0 +1,30 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
name = "cumulus-relay-chain-rpc-interface"
version = "0.1.0"
edition = "2021"
[dependencies]
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-relay-chain-interface = { path = "../relay-chain-interface" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
futures = "0.3.21"
futures-timer = "3.0.2"
parity-scale-codec = "3.0.0"
parking_lot = "0.11.1"
jsonrpsee = { version = "0.8.0", features = ["client"] }
tracing = "0.1.25"
async-trait = "0.1.52"
url = "2.2.2"
backoff = { version = "0.4.0", features = ["tokio"] }
+472
View File
@@ -0,0 +1,472 @@
// Copyright 2022 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus 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.
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
use async_trait::async_trait;
use backoff::{future::retry_notify, ExponentialBackoff};
use core::time::Duration;
use cumulus_primitives_core::{
relay_chain::{
v1::{CommittedCandidateReceipt, OccupiedCoreAssumption, SessionIndex, ValidatorId},
Hash as PHash, Header as PHeader, InboundHrmpMessage,
},
InboundDownwardMessage, ParaId, PersistedValidationData,
};
use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult};
use futures::{FutureExt, Stream, StreamExt};
use jsonrpsee::{
core::{
client::{Client as JsonRPCClient, ClientT, Subscription, SubscriptionClientT},
Error as JsonRpseeError,
},
rpc_params,
types::ParamsSer,
ws_client::WsClientBuilder,
};
use parity_scale_codec::{Decode, Encode};
use polkadot_service::Handle;
use sc_client_api::{StorageData, StorageProof};
use sc_rpc_api::{state::ReadProof, system::Health};
use sp_core::sp_std::collections::btree_map::BTreeMap;
use sp_runtime::DeserializeOwned;
use sp_state_machine::StorageValue;
use sp_storage::StorageKey;
use std::{pin::Pin, sync::Arc};
pub use url::Url;
const LOG_TARGET: &str = "relay-chain-rpc-interface";
const TIMEOUT_IN_SECONDS: u64 = 6;
/// Client that maps RPC methods and deserializes results
#[derive(Clone)]
struct RelayChainRPCClient {
/// Websocket client to make calls
ws_client: Arc<JsonRPCClient>,
/// Retry strategy that should be used for requests and subscriptions
retry_strategy: ExponentialBackoff,
}
impl RelayChainRPCClient {
pub async fn new(url: Url) -> RelayChainResult<Self> {
tracing::info!(target: LOG_TARGET, url = %url.to_string(), "Initializing RPC Client");
let ws_client = WsClientBuilder::default().build(url.as_str()).await?;
Ok(RelayChainRPCClient {
ws_client: Arc::new(ws_client),
retry_strategy: ExponentialBackoff::default(),
})
}
/// Call a call to `state_call` rpc method.
async fn call_remote_runtime_function<R: Decode>(
&self,
method_name: &str,
hash: PHash,
payload: Option<impl Encode>,
) -> RelayChainResult<R> {
let payload_bytes =
payload.map_or(sp_core::Bytes(Vec::new()), |v| sp_core::Bytes(v.encode()));
let params = rpc_params! {
method_name,
payload_bytes,
hash
};
let res = self
.request_tracing::<sp_core::Bytes, _>("state_call", params, |err| {
tracing::trace!(
target: LOG_TARGET,
%method_name,
%hash,
error = %err,
"Error during call to 'state_call'.",
);
})
.await?;
Decode::decode(&mut &*res.0).map_err(Into::into)
}
/// Subscribe to a notification stream via RPC
async fn subscribe<'a, R>(
&self,
sub_name: &'a str,
unsub_name: &'a str,
params: Option<ParamsSer<'a>>,
) -> RelayChainResult<Subscription<R>>
where
R: DeserializeOwned,
{
self.ws_client
.subscribe::<R>(sub_name, params, unsub_name)
.await
.map_err(|err| RelayChainError::RPCCallError(sub_name.to_string(), err))
}
/// Perform RPC request
async fn request<'a, R>(
&self,
method: &'a str,
params: Option<ParamsSer<'a>>,
) -> Result<R, RelayChainError>
where
R: DeserializeOwned + std::fmt::Debug,
{
self.request_tracing(
method,
params,
|e| tracing::trace!(target:LOG_TARGET, error = %e, %method, "Unable to complete RPC request"),
)
.await
}
/// Perform RPC request
async fn request_tracing<'a, R, OR>(
&self,
method: &'a str,
params: Option<ParamsSer<'a>>,
trace_error: OR,
) -> Result<R, RelayChainError>
where
R: DeserializeOwned + std::fmt::Debug,
OR: Fn(&jsonrpsee::core::Error),
{
retry_notify(
self.retry_strategy.clone(),
|| async {
self.ws_client.request(method, params.clone()).await.map_err(|err| match err {
JsonRpseeError::Transport(_) =>
backoff::Error::Transient { err, retry_after: None },
_ => backoff::Error::Permanent(err),
})
},
|error, dur| tracing::trace!(target: LOG_TARGET, %error, ?dur, "Encountered transport error, retrying."),
)
.await
.map_err(|err| {
trace_error(&err);
RelayChainError::RPCCallError(method.to_string(), err)})
}
async fn system_health(&self) -> Result<Health, RelayChainError> {
self.request("system_health", None).await
}
async fn state_get_read_proof(
&self,
storage_keys: Vec<StorageKey>,
at: Option<PHash>,
) -> Result<ReadProof<PHash>, RelayChainError> {
let params = rpc_params!(storage_keys, at);
self.request("state_getReadProof", params).await
}
async fn state_get_storage(
&self,
storage_key: StorageKey,
at: Option<PHash>,
) -> Result<Option<StorageData>, RelayChainError> {
let params = rpc_params!(storage_key, at);
self.request("state_getStorage", params).await
}
async fn chain_get_head(&self) -> Result<PHash, RelayChainError> {
self.request("chain_getHead", None).await
}
async fn chain_get_header(
&self,
hash: Option<PHash>,
) -> Result<Option<PHeader>, RelayChainError> {
let params = rpc_params!(hash);
self.request("chain_getHeader", params).await
}
async fn parachain_host_candidate_pending_availability(
&self,
at: PHash,
para_id: ParaId,
) -> Result<Option<CommittedCandidateReceipt>, RelayChainError> {
self.call_remote_runtime_function(
"ParachainHost_candidate_pending_availability",
at,
Some(para_id),
)
.await
}
async fn parachain_host_session_index_for_child(
&self,
at: PHash,
) -> Result<SessionIndex, RelayChainError> {
self.call_remote_runtime_function("ParachainHost_session_index_for_child", at, None::<()>)
.await
}
async fn parachain_host_validators(
&self,
at: PHash,
) -> Result<Vec<ValidatorId>, RelayChainError> {
self.call_remote_runtime_function("ParachainHost_validators", at, None::<()>)
.await
}
async fn parachain_host_persisted_validation_data(
&self,
at: PHash,
para_id: ParaId,
occupied_core_assumption: OccupiedCoreAssumption,
) -> Result<Option<PersistedValidationData>, RelayChainError> {
self.call_remote_runtime_function(
"ParachainHost_persisted_validation_data",
at,
Some((para_id, occupied_core_assumption)),
)
.await
}
async fn parachain_host_inbound_hrmp_channels_contents(
&self,
para_id: ParaId,
at: PHash,
) -> Result<BTreeMap<ParaId, Vec<InboundHrmpMessage>>, RelayChainError> {
self.call_remote_runtime_function(
"ParachainHost_inbound_hrmp_channels_contents",
at,
Some(para_id),
)
.await
}
async fn parachain_host_dmq_contents(
&self,
para_id: ParaId,
at: PHash,
) -> Result<Vec<InboundDownwardMessage>, RelayChainError> {
self.call_remote_runtime_function("ParachainHost_dmq_contents", at, Some(para_id))
.await
}
async fn subscribe_all_heads(&self) -> Result<Subscription<PHeader>, RelayChainError> {
self.subscribe::<PHeader>("chain_subscribeAllHeads", "chain_unsubscribeAllHeads", None)
.await
}
async fn subscribe_new_best_heads(&self) -> Result<Subscription<PHeader>, RelayChainError> {
self.subscribe::<PHeader>("chain_subscribeNewHeads", "chain_unsubscribeNewHeads", None)
.await
}
async fn subscribe_finalized_heads(&self) -> Result<Subscription<PHeader>, RelayChainError> {
self.subscribe::<PHeader>(
"chain_subscribeFinalizedHeads",
"chain_unsubscribeFinalizedHeads",
None,
)
.await
}
}
/// RelayChainRPCInterface is used to interact with a full node that is running locally
/// in the same process.
#[derive(Clone)]
pub struct RelayChainRPCInterface {
rpc_client: RelayChainRPCClient,
}
impl RelayChainRPCInterface {
pub async fn new(url: Url) -> RelayChainResult<Self> {
Ok(Self { rpc_client: RelayChainRPCClient::new(url).await? })
}
}
#[async_trait]
impl RelayChainInterface for RelayChainRPCInterface {
async fn retrieve_dmq_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> RelayChainResult<Vec<InboundDownwardMessage>> {
self.rpc_client.parachain_host_dmq_contents(para_id, relay_parent).await
}
async fn retrieve_all_inbound_hrmp_channel_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> RelayChainResult<BTreeMap<ParaId, Vec<InboundHrmpMessage>>> {
self.rpc_client
.parachain_host_inbound_hrmp_channels_contents(para_id, relay_parent)
.await
}
async fn persisted_validation_data(
&self,
hash: PHash,
para_id: ParaId,
occupied_core_assumption: OccupiedCoreAssumption,
) -> RelayChainResult<Option<PersistedValidationData>> {
self.rpc_client
.parachain_host_persisted_validation_data(hash, para_id, occupied_core_assumption)
.await
}
async fn candidate_pending_availability(
&self,
hash: PHash,
para_id: ParaId,
) -> RelayChainResult<Option<CommittedCandidateReceipt>> {
self.rpc_client
.parachain_host_candidate_pending_availability(hash, para_id)
.await
}
async fn session_index_for_child(&self, hash: PHash) -> RelayChainResult<SessionIndex> {
self.rpc_client.parachain_host_session_index_for_child(hash).await
}
async fn validators(&self, block_id: PHash) -> RelayChainResult<Vec<ValidatorId>> {
self.rpc_client.parachain_host_validators(block_id).await
}
async fn import_notification_stream(
&self,
) -> RelayChainResult<Pin<Box<dyn Stream<Item = PHeader> + Send>>> {
let imported_headers_stream =
self.rpc_client.subscribe_all_heads().await?.filter_map(|item| async move {
item.map_err(|err| {
tracing::error!(
target: LOG_TARGET,
"Encountered error in import notification stream: {}",
err
)
})
.ok()
});
Ok(imported_headers_stream.boxed())
}
async fn finality_notification_stream(
&self,
) -> RelayChainResult<Pin<Box<dyn Stream<Item = PHeader> + Send>>> {
let imported_headers_stream = self
.rpc_client
.subscribe_finalized_heads()
.await?
.filter_map(|item| async move {
item.map_err(|err| {
tracing::error!(
target: LOG_TARGET,
"Encountered error in finality notification stream: {}",
err
)
})
.ok()
});
Ok(imported_headers_stream.boxed())
}
async fn best_block_hash(&self) -> RelayChainResult<PHash> {
self.rpc_client.chain_get_head().await
}
async fn is_major_syncing(&self) -> RelayChainResult<bool> {
self.rpc_client.system_health().await.map(|h| h.is_syncing)
}
fn overseer_handle(&self) -> RelayChainResult<Option<Handle>> {
unimplemented!("Overseer handle is not available on relay-chain-rpc-interface");
}
async fn get_storage_by_key(
&self,
relay_parent: PHash,
key: &[u8],
) -> RelayChainResult<Option<StorageValue>> {
let storage_key = StorageKey(key.to_vec());
self.rpc_client
.state_get_storage(storage_key, Some(relay_parent))
.await
.map(|storage_data| storage_data.map(|sv| sv.0))
}
async fn prove_read(
&self,
relay_parent: PHash,
relevant_keys: &Vec<Vec<u8>>,
) -> RelayChainResult<StorageProof> {
let cloned = relevant_keys.clone();
let storage_keys: Vec<StorageKey> = cloned.into_iter().map(StorageKey).collect();
self.rpc_client
.state_get_read_proof(storage_keys, Some(relay_parent))
.await
.map(|read_proof| {
let bytes = read_proof.proof.into_iter().map(|bytes| bytes.to_vec()).collect();
StorageProof::new(bytes)
})
}
/// Wait for a given relay chain block
///
/// The hash of the block to wait for is passed. We wait for the block to arrive or return after a timeout.
///
/// Implementation:
/// 1. Register a listener to all new blocks.
/// 2. Check if the block is already in chain. If yes, succeed early.
/// 3. Wait for the block to be imported via subscription.
/// 4. If timeout is reached, we return an error.
async fn wait_for_block(&self, wait_for_hash: PHash) -> RelayChainResult<()> {
let mut head_stream = self.rpc_client.subscribe_all_heads().await?;
if self.rpc_client.chain_get_header(Some(wait_for_hash)).await?.is_some() {
return Ok(())
}
let mut timeout = futures_timer::Delay::new(Duration::from_secs(TIMEOUT_IN_SECONDS)).fuse();
loop {
futures::select! {
_ = timeout => return Err(RelayChainError::WaitTimeout(wait_for_hash)),
evt = head_stream.next().fuse() => match evt {
Some(Ok(evt)) if evt.hash() == wait_for_hash => return Ok(()),
// Not the event we waited on.
Some(_) => continue,
None => return Err(RelayChainError::ImportListenerClosed(wait_for_hash)),
}
}
}
}
async fn new_best_notification_stream(
&self,
) -> RelayChainResult<Pin<Box<dyn Stream<Item = PHeader> + Send>>> {
let imported_headers_stream =
self.rpc_client.subscribe_new_best_heads().await?.filter_map(|item| async move {
item.map_err(|err| {
tracing::error!(
target: LOG_TARGET,
"Error in best block notification stream: {}",
err
)
})
.ok()
});
Ok(imported_headers_stream.boxed())
}
}
+1
View File
@@ -9,6 +9,7 @@ edition = "2021"
cumulus-client-consensus-common = { path = "../consensus/common" }
cumulus-client-collator = { path = "../collator" }
cumulus-client-pov-recovery = { path = "../pov-recovery" }
cumulus-client-cli = { path = "../cli" }
cumulus-relay-chain-interface = { path = "../relay-chain-interface" }
cumulus-primitives-core = { path = "../../primitives/core" }
+11
View File
@@ -18,6 +18,7 @@
//!
//! Provides functions for starting a collator node or a normal full node.
use cumulus_client_cli::CollatorOptions;
use cumulus_client_consensus_common::ParachainConsensus;
use cumulus_primitives_core::{CollectCollationInfo, ParaId};
use cumulus_relay_chain_interface::RelayChainInterface;
@@ -151,6 +152,7 @@ pub struct StartFullNodeParams<'a, Block: BlockT, Client, RCInterface, IQ> {
pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
pub relay_chain_slot_duration: Duration,
pub import_queue: IQ,
pub collator_options: CollatorOptions,
}
/// Start a full node for a parachain.
@@ -166,6 +168,7 @@ pub fn start_full_node<Block, Client, Backend, RCInterface, IQ>(
para_id,
relay_chain_slot_duration,
import_queue,
collator_options,
}: StartFullNodeParams<Block, Client, RCInterface, IQ>,
) -> sc_service::error::Result<()>
where
@@ -193,6 +196,14 @@ where
.spawn_essential_handle()
.spawn("cumulus-consensus", None, consensus);
// PoV Recovery is currently not supported when we connect to the
// relay chain via RPC, so we return early. The node will work, but not be able to recover PoVs from the
// relay chain if blocks are not announced on parachain. This will be enabled again once
// https://github.com/paritytech/cumulus/issues/545 is finished.
if collator_options.relay_chain_rpc_url.is_some() {
return Ok(())
}
let overseer_handle = relay_chain_interface
.overseer_handle()
.map_err(|e| sc_service::Error::Application(Box::new(e)))?
+2 -1
View File
@@ -92,7 +92,8 @@ cumulus-client-service = { path = "../../client/service" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" }
cumulus-relay-chain-interface = { path = "../../client/relay-chain-interface" }
cumulus-relay-chain-local = { path = "../../client/relay-chain-local" }
cumulus-relay-chain-inprocess-interface = { path = "../../client/relay-chain-inprocess-interface" }
cumulus-relay-chain-rpc-interface = { path = "../../client/relay-chain-rpc-interface" }
# Polkadot dependencies
polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "master" }
+2 -1
View File
@@ -260,6 +260,7 @@ pub fn run() -> Result<()> {
},
None => {
let runner = cli.create_runner(&cli.run.normalize())?;
let collator_options = cli.run.collator_options();
runner.run_node_until_exit(|config| async move {
let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
@@ -292,7 +293,7 @@ pub fn run() -> Result<()> {
info!("Parachain genesis state: {}", genesis_state);
info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
crate::service::start_parachain_node(config, polkadot_config, id)
crate::service::start_parachain_node(config, polkadot_config, collator_options, id)
.await
.map(|r| r.0)
.map_err(Into::into)
+42 -9
View File
@@ -3,6 +3,7 @@
// std
use std::{sync::Arc, time::Duration};
use cumulus_client_cli::CollatorOptions;
// Local Runtime Types
use parachain_template_runtime::{
opaque::Block, AccountId, Balance, Hash, Index as Nonce, RuntimeApi,
@@ -16,8 +17,9 @@ use cumulus_client_service::{
prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams,
};
use cumulus_primitives_core::ParaId;
use cumulus_relay_chain_interface::RelayChainInterface;
use cumulus_relay_chain_local::build_relay_chain_interface;
use cumulus_relay_chain_inprocess_interface::build_inprocess_relay_chain;
use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult};
use cumulus_relay_chain_rpc_interface::RelayChainRPCInterface;
// Substrate Imports
use sc_client_api::ExecutorProvider;
@@ -30,6 +32,8 @@ use sp_keystore::SyncCryptoStorePtr;
use sp_runtime::traits::BlakeTwo256;
use substrate_prometheus_endpoint::Registry;
use polkadot_service::CollatorPair;
/// Native executor instance.
pub struct TemplateRuntimeExecutor;
@@ -160,6 +164,26 @@ where
Ok(params)
}
async fn build_relay_chain_interface(
polkadot_config: Configuration,
telemetry_worker_handle: Option<TelemetryWorkerHandle>,
task_manager: &mut TaskManager,
collator_options: CollatorOptions,
) -> RelayChainResult<(Arc<(dyn RelayChainInterface + 'static)>, Option<CollatorPair>)> {
match collator_options.relay_chain_rpc_url {
Some(relay_chain_url) =>
Ok((Arc::new(RelayChainRPCInterface::new(relay_chain_url).await?) as Arc<_>, None)),
None => {
let relay_chain_local = build_inprocess_relay_chain(
polkadot_config,
telemetry_worker_handle,
task_manager,
)?;
Ok((relay_chain_local.0, Some(relay_chain_local.1)))
},
}
}
/// Start a node with the given parachain `Configuration` and relay chain `Configuration`.
///
/// This is the actual implementation that is abstract over the executor and the runtime api.
@@ -167,6 +191,7 @@ where
async fn start_node_impl<RuntimeApi, Executor, RB, BIQ, BIC>(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
_rpc_ext_builder: RB,
build_import_queue: BIQ,
@@ -240,12 +265,17 @@ where
let backend = params.backend.clone();
let mut task_manager = params.task_manager;
let (relay_chain_interface, collator_key) =
build_relay_chain_interface(polkadot_config, telemetry_worker_handle, &mut task_manager)
.map_err(|e| match e {
polkadot_service::Error::Sub(x) => x,
s => format!("{}", s).into(),
})?;
let (relay_chain_interface, collator_key) = build_relay_chain_interface(
polkadot_config,
telemetry_worker_handle,
&mut task_manager,
collator_options.clone(),
)
.await
.map_err(|e| match e {
RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x,
s => s.to_string().into(),
})?;
let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id);
@@ -327,7 +357,7 @@ where
spawner,
parachain_consensus,
import_queue,
collator_key,
collator_key: collator_key.expect("Command line arguments do not allow this. qed"),
relay_chain_slot_duration,
};
@@ -341,6 +371,7 @@ where
relay_chain_interface,
relay_chain_slot_duration,
import_queue,
collator_options,
};
start_full_node(params)?;
@@ -401,6 +432,7 @@ pub fn parachain_build_import_queue(
pub async fn start_parachain_node(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
) -> sc_service::error::Result<(
TaskManager,
@@ -409,6 +441,7 @@ pub async fn start_parachain_node(
start_node_impl::<RuntimeApi, TemplateRuntimeExecutor, _, _, _>(
parachain_config,
polkadot_config,
collator_options,
id,
|_| Ok(Default::default()),
parachain_build_import_queue,
+3 -1
View File
@@ -79,7 +79,9 @@ cumulus-client-network = { path = "../client/network" }
cumulus-primitives-core = { path = "../primitives/core" }
cumulus-primitives-parachain-inherent = { path = "../primitives/parachain-inherent" }
cumulus-relay-chain-interface = { path = "../client/relay-chain-interface" }
cumulus-relay-chain-local = { path = "../client/relay-chain-local" }
cumulus-relay-chain-inprocess-interface = { path = "../client/relay-chain-inprocess-interface" }
cumulus-relay-chain-rpc-interface = { path = "../client/relay-chain-rpc-interface" }
# Polkadot dependencies
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
+1 -1
View File
@@ -109,7 +109,7 @@ pub struct Cli {
pub run: cumulus_client_cli::RunCmd,
/// Relay chain arguments
#[clap(raw = true)]
#[clap(raw = true, conflicts_with = "relay-chain-rpc-url")]
pub relaychain_args: Vec<String>,
}
+24 -13
View File
@@ -493,6 +493,7 @@ pub fn run() -> Result<()> {
Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
None => {
let runner = cli.create_runner(&cli.run.normalize())?;
let collator_options = cli.run.collator_options();
runner.run_node_until_exit(|config| async move {
let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
@@ -534,7 +535,7 @@ pub fn run() -> Result<()> {
statemint_runtime::RuntimeApi,
StatemintRuntimeExecutor,
StatemintAuraId,
>(config, polkadot_config, id)
>(config, polkadot_config, collator_options, id)
.await
.map(|r| r.0)
.map_err(Into::into)
@@ -543,7 +544,7 @@ pub fn run() -> Result<()> {
statemine_runtime::RuntimeApi,
StatemineRuntimeExecutor,
AuraId,
>(config, polkadot_config, id)
>(config, polkadot_config, collator_options, id)
.await
.map(|r| r.0)
.map_err(Into::into)
@@ -552,7 +553,7 @@ pub fn run() -> Result<()> {
westmint_runtime::RuntimeApi,
WestmintRuntimeExecutor,
AuraId,
>(config, polkadot_config, id)
>(config, polkadot_config, collator_options, id)
.await
.map(|r| r.0)
.map_err(Into::into)
@@ -560,7 +561,7 @@ pub fn run() -> Result<()> {
crate::service::start_shell_node::<
shell_runtime::RuntimeApi,
ShellRuntimeExecutor,
>(config, polkadot_config, id)
>(config, polkadot_config, collator_options, id)
.await
.map(|r| r.0)
.map_err(Into::into)
@@ -568,20 +569,30 @@ pub fn run() -> Result<()> {
crate::service::start_shell_node::<
seedling_runtime::RuntimeApi,
SeedlingRuntimeExecutor,
>(config, polkadot_config, id)
>(config, polkadot_config, collator_options, id)
.await
.map(|r| r.0)
.map_err(Into::into)
} else if config.chain_spec.is_canvas_kusama() {
crate::service::start_canvas_kusama_node(config, polkadot_config, id)
.await
.map(|r| r.0)
.map_err(Into::into)
crate::service::start_canvas_kusama_node(
config,
polkadot_config,
collator_options,
id,
)
.await
.map(|r| r.0)
.map_err(Into::into)
} else {
crate::service::start_rococo_parachain_node(config, polkadot_config, id)
.await
.map(|r| r.0)
.map_err(Into::into)
crate::service::start_rococo_parachain_node(
config,
polkadot_config,
collator_options,
id,
)
.await
.map(|r| r.0)
.map_err(Into::into)
}
})
},
+76 -25
View File
@@ -15,6 +15,7 @@
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
use codec::Codec;
use cumulus_client_cli::CollatorOptions;
use cumulus_client_consensus_aura::{AuraConsensus, BuildAuraConsensusParams, SlotProportion};
use cumulus_client_consensus_common::{
ParachainBlockImport, ParachainCandidate, ParachainConsensus,
@@ -27,9 +28,11 @@ use cumulus_primitives_core::{
relay_chain::v1::{Hash as PHash, PersistedValidationData},
ParaId,
};
use cumulus_relay_chain_interface::RelayChainInterface;
use cumulus_relay_chain_local::build_relay_chain_interface;
use polkadot_service::NativeExecutionDispatch;
use cumulus_relay_chain_inprocess_interface::build_inprocess_relay_chain;
use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult};
use cumulus_relay_chain_rpc_interface::RelayChainRPCInterface;
use polkadot_service::{CollatorPair, NativeExecutionDispatch};
use sp_core::Pair;
use crate::rpc;
pub use parachains_common::{AccountId, Balance, Block, BlockNumber, Hash, Header, Index as Nonce};
@@ -48,7 +51,6 @@ use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerH
use sp_api::{ApiExt, ConstructRuntimeApi};
use sp_consensus::CacheKeyId;
use sp_consensus_aura::AuraApi;
use sp_core::crypto::Pair;
use sp_keystore::SyncCryptoStorePtr;
use sp_runtime::{
app_crypto::AppKey,
@@ -277,6 +279,26 @@ where
Ok(params)
}
async fn build_relay_chain_interface(
polkadot_config: Configuration,
telemetry_worker_handle: Option<TelemetryWorkerHandle>,
task_manager: &mut TaskManager,
collator_options: CollatorOptions,
) -> RelayChainResult<(Arc<(dyn RelayChainInterface + 'static)>, Option<CollatorPair>)> {
match collator_options.relay_chain_rpc_url {
Some(relay_chain_url) =>
Ok((Arc::new(RelayChainRPCInterface::new(relay_chain_url).await?) as Arc<_>, None)),
None => {
let relay_chain_local = build_inprocess_relay_chain(
polkadot_config,
telemetry_worker_handle,
task_manager,
)?;
Ok((relay_chain_local.0, Some(relay_chain_local.1)))
},
}
}
/// Start a shell node with the given parachain `Configuration` and relay chain `Configuration`.
///
/// This is the actual implementation that is abstract over the executor and the runtime api for shell nodes.
@@ -284,6 +306,7 @@ where
async fn start_shell_node_impl<RuntimeApi, Executor, RB, BIQ, BIC>(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
rpc_ext_builder: RB,
build_import_queue: BIQ,
@@ -356,12 +379,17 @@ where
let mut task_manager = params.task_manager;
let (relay_chain_interface, collator_key) =
build_relay_chain_interface(polkadot_config, telemetry_worker_handle, &mut task_manager)
.map_err(|e| match e {
polkadot_service::Error::Sub(x) => x,
s => format!("{}", s).into(),
})?;
let (relay_chain_interface, collator_key) = build_relay_chain_interface(
polkadot_config,
telemetry_worker_handle,
&mut task_manager,
collator_options.clone(),
)
.await
.map_err(|e| match e {
RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x,
s => s.to_string().into(),
})?;
let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id);
@@ -431,7 +459,7 @@ where
spawner,
parachain_consensus,
import_queue,
collator_key,
collator_key: collator_key.expect("Command line arguments do not allow this. qed"),
relay_chain_slot_duration,
};
@@ -445,6 +473,7 @@ where
relay_chain_interface,
relay_chain_slot_duration,
import_queue,
collator_options,
};
start_full_node(params)?;
@@ -462,6 +491,7 @@ where
async fn start_node_impl<RuntimeApi, Executor, RB, BIQ, BIC>(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
_rpc_ext_builder: RB,
build_import_queue: BIQ,
@@ -535,12 +565,17 @@ where
let backend = params.backend.clone();
let mut task_manager = params.task_manager;
let (relay_chain_interface, collator_key) =
build_relay_chain_interface(polkadot_config, telemetry_worker_handle, &mut task_manager)
.map_err(|e| match e {
polkadot_service::Error::Sub(x) => x,
s => format!("{}", s).into(),
})?;
let (relay_chain_interface, collator_key) = build_relay_chain_interface(
polkadot_config,
telemetry_worker_handle,
&mut task_manager,
collator_options.clone(),
)
.await
.map_err(|e| match e {
RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x,
s => s.to_string().into(),
})?;
let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id);
@@ -622,7 +657,7 @@ where
spawner,
parachain_consensus,
import_queue,
collator_key,
collator_key: collator_key.expect("Command line arguments do not allow this. qed"),
relay_chain_slot_duration,
};
@@ -636,6 +671,7 @@ where
relay_chain_interface,
relay_chain_slot_duration,
import_queue,
collator_options,
};
start_full_node(params)?;
@@ -705,6 +741,7 @@ pub fn rococo_parachain_build_import_queue(
pub async fn start_rococo_parachain_node(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
) -> sc_service::error::Result<(
TaskManager,
@@ -719,6 +756,7 @@ pub async fn start_rococo_parachain_node(
start_node_impl::<rococo_parachain_runtime::RuntimeApi, RococoParachainRuntimeExecutor, _, _, _>(
parachain_config,
polkadot_config,
collator_options,
id,
|_| Ok(Default::default()),
rococo_parachain_build_import_queue,
@@ -842,6 +880,7 @@ where
pub async fn start_shell_node<RuntimeApi, Executor>(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
) -> sc_service::error::Result<(
TaskManager,
@@ -867,6 +906,7 @@ where
start_shell_node_impl::<RuntimeApi, Executor, _, _, _>(
parachain_config,
polkadot_config,
collator_options,
id,
|_| Ok(Default::default()),
shell_build_import_queue,
@@ -1112,6 +1152,7 @@ where
pub async fn start_statemint_node<RuntimeApi, Executor, AuraId: AppKey>(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
) -> sc_service::error::Result<(
TaskManager,
@@ -1142,6 +1183,7 @@ where
start_node_impl::<RuntimeApi, Executor, _, _, _>(
parachain_config,
polkadot_config,
collator_options,
id,
|_| Ok(Default::default()),
statemint_build_import_queue::<_, _, AuraId>,
@@ -1277,6 +1319,7 @@ where
async fn start_canvas_kusama_node_impl<RuntimeApi, Executor, RB, BIQ, BIC>(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
_rpc_ext_builder: RB,
build_import_queue: BIQ,
@@ -1351,12 +1394,17 @@ where
let backend = params.backend.clone();
let mut task_manager = params.task_manager;
let (relay_chain_interface, collator_key) =
build_relay_chain_interface(polkadot_config, telemetry_worker_handle, &mut task_manager)
.map_err(|e| match e {
polkadot_service::Error::Sub(x) => x,
s => format!("{}", s).into(),
})?;
let (relay_chain_interface, collator_key) = build_relay_chain_interface(
polkadot_config,
telemetry_worker_handle,
&mut task_manager,
collator_options.clone(),
)
.await
.map_err(|e| match e {
RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x,
s => s.to_string().into(),
})?;
let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id);
@@ -1438,7 +1486,7 @@ where
spawner,
parachain_consensus,
import_queue,
collator_key,
collator_key: collator_key.expect("Command line arguments do not allow this. qed"),
relay_chain_slot_duration,
};
@@ -1452,6 +1500,7 @@ where
relay_chain_interface,
relay_chain_slot_duration,
import_queue,
collator_options,
};
start_full_node(params)?;
@@ -1521,6 +1570,7 @@ pub fn canvas_kusama_build_import_queue(
pub async fn start_canvas_kusama_node(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
id: ParaId,
) -> sc_service::error::Result<(
TaskManager,
@@ -1541,6 +1591,7 @@ pub async fn start_canvas_kusama_node(
>(
parachain_config,
polkadot_config,
collator_options,
id,
|_| Ok(Default::default()),
canvas_kusama_build_import_queue,
@@ -23,7 +23,6 @@ use cumulus_primitives_core::{
ParaId, PersistedValidationData,
};
use cumulus_relay_chain_interface::RelayChainInterface;
use sp_runtime::generic::BlockId;
const LOG_TARGET: &str = "parachain-inherent";
@@ -36,10 +35,9 @@ async fn collect_relay_storage_proof(
) -> Option<sp_state_machine::StorageProof> {
use relay_chain::well_known_keys as relay_well_known_keys;
let relay_parent_block_id = BlockId::Hash(relay_parent);
let ingress_channels = relay_chain_interface
.get_storage_by_key(
&relay_parent_block_id,
relay_parent,
&relay_well_known_keys::hrmp_ingress_channel_index(para_id),
)
.await
@@ -68,7 +66,7 @@ async fn collect_relay_storage_proof(
let egress_channels = relay_chain_interface
.get_storage_by_key(
&relay_parent_block_id,
relay_parent,
&relay_well_known_keys::hrmp_egress_channel_index(para_id),
)
.await
@@ -111,12 +109,12 @@ async fn collect_relay_storage_proof(
}));
relay_chain_interface
.prove_read(&relay_parent_block_id, &relevant_keys)
.prove_read(relay_parent, &relevant_keys)
.await
.map_err(|e| {
tracing::error!(
target: LOG_TARGET,
relay_parent = ?relay_parent_block_id,
relay_parent = ?relay_parent,
error = ?e,
"Cannot obtain read proof from relay chain.",
);
+6 -1
View File
@@ -47,12 +47,15 @@ polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch
cumulus-client-consensus-relay-chain = { path = "../../client/consensus/relay-chain" }
cumulus-client-network = { path = "../../client/network" }
cumulus-client-service = { path = "../../client/service" }
cumulus-client-cli = { path = "../../client/cli" }
cumulus-client-consensus-common = { path = "../../client/consensus/common" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" }
cumulus-test-runtime = { path = "../runtime" }
cumulus-test-relay-validation-worker-provider = { path = "../relay-validation-worker-provider" }
cumulus-relay-chain-local = { path = "../../client/relay-chain-local" }
cumulus-relay-chain-inprocess-interface = { path = "../../client/relay-chain-inprocess-interface" }
cumulus-relay-chain-interface = { path = "../../client/relay-chain-interface" }
cumulus-relay-chain-rpc-interface = { path = "../../client/relay-chain-rpc-interface" }
criterion = { version = "0.3.5", features = [ "async_tokio" ] }
@@ -60,9 +63,11 @@ parking_lot = "0.12.0"
# RPC related dependencies
jsonrpc-core = "18.0.0"
url = "2.2.2"
[dev-dependencies]
futures = "0.3.5"
portpicker = "0.1.1"
# Polkadot dependencies
polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
@@ -151,6 +151,7 @@ fn transaction_throughput_benchmarks(c: &mut Criterion) {
Alice,
|| {},
vec![],
None,
);
// Start bob
@@ -159,6 +160,7 @@ fn transaction_throughput_benchmarks(c: &mut Criterion) {
Bob,
|| {},
vec![alice.addr.clone()],
None,
);
// Register parachain
+80 -24
View File
@@ -21,15 +21,23 @@
mod chain_spec;
mod genesis;
use std::{future::Future, time::Duration};
use std::{
future::Future,
net::{IpAddr, Ipv4Addr, SocketAddr},
time::Duration,
};
use url::Url;
use cumulus_client_cli::CollatorOptions;
use cumulus_client_consensus_common::{ParachainCandidate, ParachainConsensus};
use cumulus_client_network::BlockAnnounceValidator;
use cumulus_client_service::{
prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams,
};
use cumulus_primitives_core::ParaId;
use cumulus_relay_chain_local::RelayChainLocal;
use cumulus_relay_chain_inprocess_interface::RelayChainInProcessInterface;
use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult};
use cumulus_relay_chain_rpc_interface::RelayChainRPCInterface;
use cumulus_test_runtime::{Hash, Header, NodeBlock as Block, RuntimeApi};
use parking_lot::Mutex;
@@ -167,6 +175,35 @@ pub fn new_partial(
Ok(params)
}
async fn build_relay_chain_interface(
relay_chain_config: Configuration,
collator_key: Option<CollatorPair>,
collator_options: CollatorOptions,
task_manager: &mut TaskManager,
) -> RelayChainResult<Arc<dyn RelayChainInterface + 'static>> {
if let Some(relay_chain_url) = collator_options.relay_chain_rpc_url {
return Ok(Arc::new(RelayChainRPCInterface::new(relay_chain_url).await?) as Arc<_>)
}
let relay_chain_full_node = polkadot_test_service::new_full(
relay_chain_config,
if let Some(ref key) = collator_key {
polkadot_service::IsCollator::Yes(key.clone())
} else {
polkadot_service::IsCollator::Yes(CollatorPair::generate().0)
},
None,
)?;
task_manager.add_child(relay_chain_full_node.task_manager);
Ok(Arc::new(RelayChainInProcessInterface::new(
relay_chain_full_node.client.clone(),
relay_chain_full_node.backend.clone(),
Arc::new(Mutex::new(Box::new(relay_chain_full_node.network.clone()))),
relay_chain_full_node.overseer_handle.clone(),
)) as Arc<_>)
}
/// Start a node with the given parachain `Configuration` and relay chain `Configuration`.
///
/// This is the actual implementation that is abstract over the executor and the runtime api.
@@ -179,6 +216,7 @@ async fn start_node_impl<RB>(
wrap_announce_block: Option<Box<dyn FnOnce(AnnounceBlockFn) -> AnnounceBlockFn>>,
rpc_ext_builder: RB,
consensus: Consensus,
collator_options: CollatorOptions,
) -> sc_service::error::Result<(
TaskManager,
Arc<Client>,
@@ -202,30 +240,20 @@ where
let transaction_pool = params.transaction_pool.clone();
let mut task_manager = params.task_manager;
let relay_chain_full_node = polkadot_test_service::new_full(
relay_chain_config,
if let Some(ref key) = collator_key {
polkadot_service::IsCollator::Yes(key.clone())
} else {
polkadot_service::IsCollator::Yes(CollatorPair::generate().0)
},
None,
)
.map_err(|e| match e {
polkadot_service::Error::Sub(x) => x,
s => s.to_string().into(),
})?;
let client = params.client.clone();
let backend = params.backend.clone();
let relay_chain_interface = Arc::new(RelayChainLocal::new(
relay_chain_full_node.client.clone(),
relay_chain_full_node.backend.clone(),
Arc::new(Mutex::new(Box::new(relay_chain_full_node.network.clone()))),
relay_chain_full_node.overseer_handle.clone(),
));
task_manager.add_child(relay_chain_full_node.task_manager);
let relay_chain_interface = build_relay_chain_interface(
relay_chain_config,
collator_key.clone(),
collator_options.clone(),
&mut task_manager,
)
.await
.map_err(|e| match e {
RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x,
s => s.to_string().into(),
})?;
let block_announce_validator =
BlockAnnounceValidator::new(relay_chain_interface.clone(), para_id);
@@ -342,6 +370,7 @@ where
// the recovery delay of pov-recovery. We don't want to wait for too
// long on the full node to recover, so we reduce this time here.
relay_chain_slot_duration: Duration::from_millis(6),
collator_options,
};
start_full_node(params)?;
@@ -389,6 +418,7 @@ pub struct TestNodeBuilder {
storage_update_func_parachain: Option<Box<dyn Fn()>>,
storage_update_func_relay_chain: Option<Box<dyn Fn()>>,
consensus: Consensus,
relay_chain_full_node_url: Option<Url>,
}
impl TestNodeBuilder {
@@ -410,6 +440,7 @@ impl TestNodeBuilder {
storage_update_func_parachain: None,
storage_update_func_relay_chain: None,
consensus: Consensus::RelayChain,
relay_chain_full_node_url: None,
}
}
@@ -501,6 +532,21 @@ impl TestNodeBuilder {
self
}
/// Connect to full node via RPC.
pub fn use_external_relay_chain_node_at_url(mut self, network_address: Url) -> Self {
self.relay_chain_full_node_url = Some(network_address);
self
}
/// Connect to full node via RPC.
pub fn use_external_relay_chain_node_at_port(mut self, port: u16) -> Self {
let mut localhost_url =
Url::parse("ws://localhost").expect("Should be able to parse localhost Url");
localhost_url.set_port(Some(port)).expect("Should be able to set port");
self.relay_chain_full_node_url = Some(localhost_url);
self
}
/// Build the [`TestNode`].
pub async fn build(self) -> TestNode {
let parachain_config = node_config(
@@ -513,6 +559,7 @@ impl TestNodeBuilder {
self.collator_key.is_some(),
)
.expect("could not generate Configuration");
let mut relay_chain_config = polkadot_test_service::node_config(
self.storage_update_func_relay_chain.unwrap_or_else(|| Box::new(|| ())),
self.tokio_handle,
@@ -521,6 +568,9 @@ impl TestNodeBuilder {
false,
);
let collator_options =
CollatorOptions { relay_chain_rpc_url: self.relay_chain_full_node_url };
relay_chain_config.network.node_name =
format!("{} (relay chain)", relay_chain_config.network.node_name);
@@ -533,6 +583,7 @@ impl TestNodeBuilder {
self.wrap_announce_block,
|_| Ok(Default::default()),
self.consensus,
collator_options,
)
.await
.expect("could not create Cumulus test service");
@@ -737,8 +788,9 @@ pub fn run_relay_chain_validator_node(
key: Sr25519Keyring,
storage_update_func: impl Fn(),
boot_nodes: Vec<MultiaddrWithPeerId>,
websocket_port: Option<u16>,
) -> polkadot_test_service::PolkadotTestNode {
let config = polkadot_test_service::node_config(
let mut config = polkadot_test_service::node_config(
storage_update_func,
tokio_handle,
key,
@@ -746,6 +798,10 @@ pub fn run_relay_chain_validator_node(
true,
);
if let Some(port) = websocket_port {
config.rpc_ws = Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port));
}
polkadot_test_service::run_validator_node(
config,
Some(cumulus_test_relay_validation_worker_provider::VALIDATION_WORKER.into()),
+27 -5
View File
@@ -16,6 +16,7 @@
use cumulus_primitives_core::ParaId;
use cumulus_test_service::{initial_head_data, run_relay_chain_validator_node, Keyring::*};
use futures::join;
#[substrate_test_utils::test]
#[ignore]
@@ -28,12 +29,24 @@ async fn test_full_node_catching_up() {
let tokio_handle = tokio::runtime::Handle::current();
let ws_port = portpicker::pick_unused_port().expect("No free ports");
// start alice
let alice = run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, Vec::new());
let alice = run_relay_chain_validator_node(
tokio_handle.clone(),
Alice,
|| {},
Vec::new(),
Some(ws_port),
);
// start bob
let bob =
run_relay_chain_validator_node(tokio_handle.clone(), Bob, || {}, vec![alice.addr.clone()]);
let bob = run_relay_chain_validator_node(
tokio_handle.clone(),
Bob,
|| {},
vec![alice.addr.clone()],
None,
);
// register parachain
alice
@@ -57,10 +70,19 @@ async fn test_full_node_catching_up() {
charlie.wait_for_blocks(5).await;
// run cumulus dave (a parachain full node) and wait for it to sync some blocks
let dave = cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle, Dave)
let dave = cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Dave)
.connect_to_parachain_node(&charlie)
.connect_to_relay_chain_nodes(vec![&alice, &bob])
.build()
.await;
dave.wait_for_blocks(7).await;
// run cumulus dave (a parachain full node) and wait for it to sync some blocks
let eve = cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle, Eve)
.connect_to_parachain_node(&charlie)
.connect_to_relay_chain_nodes(vec![&alice, &bob])
.use_external_relay_chain_node_at_port(ws_port)
.build()
.await;
join!(dave.wait_for_blocks(7), eve.wait_for_blocks(7));
}
+9 -3
View File
@@ -42,11 +42,17 @@ async fn test_migrate_solo_to_para() {
let tokio_handle = tokio::runtime::Handle::current();
// start alice
let alice = run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, Vec::new());
let alice =
run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, Vec::new(), None);
// start bob
let bob =
run_relay_chain_validator_node(tokio_handle.clone(), Bob, || {}, vec![alice.addr.clone()]);
let bob = run_relay_chain_validator_node(
tokio_handle.clone(),
Bob,
|| {},
vec![alice.addr.clone()],
None,
);
// register parachain
alice
+9 -3
View File
@@ -31,11 +31,17 @@ async fn test_runtime_upgrade() {
let tokio_handle = tokio::runtime::Handle::current();
// start alice
let alice = run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, Vec::new());
let alice =
run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, Vec::new(), None);
// start bob
let bob =
run_relay_chain_validator_node(tokio_handle.clone(), Bob, || {}, vec![alice.addr.clone()]);
let bob = run_relay_chain_validator_node(
tokio_handle.clone(),
Bob,
|| {},
vec![alice.addr.clone()],
None,
);
// register parachain
alice