From ab51fe9abe30a54554d789637d568df67cfdfefd Mon Sep 17 00:00:00 2001
From: Cecile Tonglet <cecile@parity.io>
Date: Mon, 18 May 2020 17:17:34 +0200
Subject: [PATCH] Integration test (#91)

---
 cumulus/.editorconfig                         |   7 +
 cumulus/.gitlab-ci.yml                        |   2 +
 cumulus/Cargo.lock                            | 164 +++++++-
 cumulus/test/parachain/Cargo.toml             |  17 +
 cumulus/test/parachain/src/cli.rs             |   7 +
 cumulus/test/parachain/src/command.rs         |  93 +++--
 .../test/parachain/tests/integration_test.rs  | 372 ++++++++++++++++++
 7 files changed, 615 insertions(+), 47 deletions(-)
 create mode 100644 cumulus/test/parachain/tests/integration_test.rs

diff --git a/cumulus/.editorconfig b/cumulus/.editorconfig
index d44b1ae078b..e8ff2027ca4 100644
--- a/cumulus/.editorconfig
+++ b/cumulus/.editorconfig
@@ -20,3 +20,10 @@ indent_style=space
 indent_size=4
 tab_width=8
 end_of_line=lf
+
+[*.json]
+indent_style=space
+indent_size=2
+tab_width=8
+end_of_line=lf
+
diff --git a/cumulus/.gitlab-ci.yml b/cumulus/.gitlab-ci.yml
index 9bc4151ec85..39851191339 100644
--- a/cumulus/.gitlab-ci.yml
+++ b/cumulus/.gitlab-ci.yml
@@ -63,6 +63,8 @@ test-linux-stable:
   script:
     - time cargo test --all --release --locked |
         tee output.log
+    - time cargo test --release -- --ignored integration_test |
+        tee -a output.log
     - sccache -s
   after_script:
     - echo "___Collecting warnings for check_warnings job___"
diff --git a/cumulus/Cargo.lock b/cumulus/Cargo.lock
index 249e13a9ce6..af133aaa8b5 100644
--- a/cumulus/Cargo.lock
+++ b/cumulus/Cargo.lock
@@ -59,6 +59,12 @@ dependencies = [
  "const-random",
 ]
 
+[[package]]
+name = "ahash"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c251dce3391a07b43218ca070203ecb8f9f520d35ab71312296a59dbceab154"
+
 [[package]]
 name = "aho-corasick"
 version = "0.7.10"
@@ -190,12 +196,23 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5"
 
+[[package]]
+name = "async-attributes"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423"
+dependencies = [
+ "quote 1.0.3",
+ "syn 1.0.18",
+]
+
 [[package]]
 name = "async-std"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267"
 dependencies = [
+ "async-attributes",
  "async-task",
  "broadcaster",
  "crossbeam-channel",
@@ -226,6 +243,18 @@ dependencies = [
  "winapi 0.3.8",
 ]
 
+[[package]]
+name = "async-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce6977f57fa68da77ffe5542950d47e9c23d65f5bc7cb0a9f8700996913eec7"
+dependencies = [
+ "futures 0.3.4",
+ "rustls 0.16.0",
+ "webpki",
+ "webpki-roots 0.17.0",
+]
+
 [[package]]
 name = "async-tls"
 version = "0.7.0"
@@ -233,7 +262,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95fd83426b89b034bf4e9ceb9c533c2f2386b813fd3dcae0a425ec6f1837d78a"
 dependencies = [
  "futures 0.3.4",
- "rustls",
+ "rustls 0.17.0",
  "webpki",
  "webpki-roots 0.19.0",
 ]
@@ -289,6 +318,15 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
 
+[[package]]
+name = "base64"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
+dependencies = [
+ "byteorder",
+]
+
 [[package]]
 name = "base64"
 version = "0.11.0"
@@ -949,7 +987,7 @@ dependencies = [
  "cumulus-test-client",
  "frame-executive",
  "hash-db",
- "hashbrown",
+ "hashbrown 0.6.3",
  "memory-db 0.18.1",
  "parity-scale-codec",
  "polkadot-parachain",
@@ -987,19 +1025,27 @@ name = "cumulus-test-parachain-collator"
 version = "0.1.0"
 dependencies = [
  "assert_cmd",
+ "async-std",
  "cumulus-collator",
  "cumulus-consensus",
  "cumulus-test-parachain-runtime",
  "derive_more 0.15.0",
  "exit-future 0.1.4",
+ "frame-system",
  "futures 0.3.4",
+ "hex",
+ "jsonrpsee",
  "log 0.4.8",
  "nix 0.17.0",
+ "pallet-sudo",
+ "pallet-transaction-payment",
  "parity-scale-codec",
  "parking_lot 0.9.0",
  "polkadot-cli",
  "polkadot-collator",
  "polkadot-primitives",
+ "polkadot-runtime",
+ "polkadot-runtime-common",
  "polkadot-service",
  "sc-basic-authorship",
  "sc-cli",
@@ -1009,6 +1055,8 @@ dependencies = [
  "sc-network",
  "sc-service",
  "sc-transaction-pool",
+ "serde_json",
+ "sp-arithmetic",
  "sp-consensus",
  "sp-core",
  "sp-inherents",
@@ -1016,8 +1064,11 @@ dependencies = [
  "sp-runtime",
  "sp-timestamp",
  "sp-transaction-pool",
+ "sp-version",
  "structopt",
  "substrate-build-script-utils",
+ "substrate-test-runtime-client",
+ "tempfile",
  "trie-root 0.15.2",
 ]
 
@@ -1952,10 +2003,20 @@ version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead"
 dependencies = [
- "ahash",
+ "ahash 0.2.18",
  "autocfg 0.1.7",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
+dependencies = [
+ "ahash 0.3.4",
+ "autocfg 1.0.0",
+]
+
 [[package]]
 name = "heck"
 version = "0.3.1"
@@ -2144,7 +2205,7 @@ dependencies = [
  "futures-util",
  "hyper 0.13.5",
  "log 0.4.8",
- "rustls",
+ "rustls 0.17.0",
  "rustls-native-certs",
  "tokio 0.2.20",
  "tokio-rustls",
@@ -2403,6 +2464,51 @@ dependencies = [
  "ws",
 ]
 
+[[package]]
+name = "jsonrpsee"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc8a1da5a54b417cfb7edb9f932024d833dc333de50c21c0e1b28d0e8b4f853"
+dependencies = [
+ "async-std",
+ "async-tls 0.6.0",
+ "bs58",
+ "bytes 0.5.4",
+ "fnv",
+ "futures 0.3.4",
+ "futures-timer 3.0.2",
+ "globset",
+ "hashbrown 0.7.2",
+ "hyper 0.13.5",
+ "jsonrpsee-proc-macros",
+ "lazy_static",
+ "log 0.4.8",
+ "parking_lot 0.10.2",
+ "pin-project",
+ "rand 0.7.3",
+ "serde",
+ "serde_json",
+ "smallvec 1.4.0",
+ "soketto",
+ "thiserror",
+ "tokio 0.2.20",
+ "unicase",
+ "url 2.1.1",
+ "webpki",
+]
+
+[[package]]
+name = "jsonrpsee-proc-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1ed1b5f6937dd2c6c79a9ac6e0e3f41bbc64edb5d443840bdc73e606009ed70"
+dependencies = [
+ "Inflector",
+ "proc-macro2 1.0.10",
+ "quote 1.0.3",
+ "syn 1.0.18",
+]
+
 [[package]]
 name = "keccak"
 version = "0.1.0"
@@ -2866,14 +2972,14 @@ version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6874c9069ce93d899df9dc7b29f129c706b2a0fdc048f11d878935352b580190"
 dependencies = [
- "async-tls",
+ "async-tls 0.7.0",
  "bytes 0.5.4",
  "either",
  "futures 0.3.4",
  "libp2p-core",
  "log 0.4.8",
  "quicksink",
- "rustls",
+ "rustls 0.17.0",
  "rw-stream-sink",
  "soketto",
  "url 2.1.1",
@@ -3003,7 +3109,7 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0609345ddee5badacf857d4f547e0e5a2e987db77085c24cd887f73573a04237"
 dependencies = [
- "hashbrown",
+ "hashbrown 0.6.3",
 ]
 
 [[package]]
@@ -3078,9 +3184,9 @@ version = "0.18.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "881736a0f68a6fae1b596bb066c5bd16d7b3ed645a4dd8ffaefd02f585abaf71"
 dependencies = [
- "ahash",
+ "ahash 0.2.18",
  "hash-db",
- "hashbrown",
+ "hashbrown 0.6.3",
  "parity-util-mem 0.3.0",
 ]
 
@@ -3090,9 +3196,9 @@ version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "be512cb2ccb4ecbdca937fdd4a62ea5f09f8e7195466a85e4632b3d5bcce82e6"
 dependencies = [
- "ahash",
+ "ahash 0.2.18",
  "hash-db",
- "hashbrown",
+ "hashbrown 0.6.3",
  "parity-util-mem 0.6.1",
 ]
 
@@ -5244,7 +5350,7 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
 dependencies = [
- "base64",
+ "base64 0.11.0",
  "blake2b_simd",
  "constant_time_eq",
  "crossbeam-utils",
@@ -5283,13 +5389,26 @@ dependencies = [
  "semver",
 ]
 
+[[package]]
+name = "rustls"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e"
+dependencies = [
+ "base64 0.10.1",
+ "log 0.4.8",
+ "ring",
+ "sct",
+ "webpki",
+]
+
 [[package]]
 name = "rustls"
 version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1"
 dependencies = [
- "base64",
+ "base64 0.11.0",
  "log 0.4.8",
  "ring",
  "sct",
@@ -5303,7 +5422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5"
 dependencies = [
  "openssl-probe",
- "rustls",
+ "rustls 0.17.0",
  "schannel",
  "security-framework",
 ]
@@ -6455,7 +6574,7 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1c9dab3f95c9ebdf3a88268c19af668f637a3c5039c2c56ff2d40b1b2d64a25b"
 dependencies = [
- "base64",
+ "base64 0.11.0",
  "bytes 0.5.4",
  "flate2",
  "futures 0.3.4",
@@ -7746,7 +7865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a"
 dependencies = [
  "futures-core",
- "rustls",
+ "rustls 0.17.0",
  "tokio 0.2.20",
  "webpki",
 ]
@@ -7920,7 +8039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bcc309f34008563989045a4c4dbcc5770467f3a3785ee80a9b5cc0d83362475f"
 dependencies = [
  "hash-db",
- "hashbrown",
+ "hashbrown 0.6.3",
  "log 0.4.8",
  "rustc-hex",
  "smallvec 1.4.0",
@@ -8285,7 +8404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "80f3dea0e60c076dd0da27fa10c821323903c9554c617ed32eaab8e7a7e36c89"
 dependencies = [
  "anyhow",
- "base64",
+ "base64 0.11.0",
  "bincode",
  "cranelift-codegen",
  "cranelift-entity",
@@ -8361,6 +8480,15 @@ dependencies = [
  "untrusted",
 ]
 
+[[package]]
+name = "webpki-roots"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a262ae37dd9d60f60dd473d1158f9fbebf110ba7b6a5051c8160460f6043718b"
+dependencies = [
+ "webpki",
+]
+
 [[package]]
 name = "webpki-roots"
 version = "0.18.0"
diff --git a/cumulus/test/parachain/Cargo.toml b/cumulus/test/parachain/Cargo.toml
index 65523231517..48a0bb70515 100644
--- a/cumulus/test/parachain/Cargo.toml
+++ b/cumulus/test/parachain/Cargo.toml
@@ -55,3 +55,20 @@ substrate-build-script-utils = { git = "https://github.com/paritytech/substrate"
 [dev-dependencies]
 assert_cmd = "0.12"
 nix = "0.17"
+tempfile = "3.1"
+jsonrpsee = "0.1"
+async-std = { version = "1.2.0", features = [ "attributes" ] }
+hex = "0.4"
+serde_json = "1.0"
+
+# Polkadot dependencies
+polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", branch = "cumulus-branch" }
+polkadot-runtime = { git = "https://github.com/paritytech/polkadot", branch = "cumulus-branch" }
+
+# Substrate dependencies
+substrate-test-runtime-client = { git = "https://github.com/paritytech/substrate", branch = "cumulus-branch" }
+frame-system = { git = "https://github.com/paritytech/substrate", branch = "cumulus-branch" }
+pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "cumulus-branch" }
+sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "cumulus-branch" }
+sp-version = { git = "https://github.com/paritytech/substrate", branch = "cumulus-branch" }
+pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "cumulus-branch" }
diff --git a/cumulus/test/parachain/src/cli.rs b/cumulus/test/parachain/src/cli.rs
index 8abcff9fc24..2c2450aeb66 100644
--- a/cumulus/test/parachain/src/cli.rs
+++ b/cumulus/test/parachain/src/cli.rs
@@ -24,9 +24,16 @@ use structopt::StructOpt;
 pub enum Subcommand {
 	#[structopt(flatten)]
 	Base(sc_cli::Subcommand),
+
 	/// Export the genesis state of the parachain.
 	#[structopt(name = "export-genesis-state")]
 	ExportGenesisState(ExportGenesisStateCommand),
+
+	/// Run Polkadot for testing purpose
+	Polkadot(polkadot_cli::Cli),
+
+	#[structopt(name = "validation-worker", setting = structopt::clap::AppSettings::Hidden)]
+	PolkadotValidationWorker(polkadot_cli::ValidationWorkerCommand),
 }
 
 /// Command for exporting the genesis state of the parachain
diff --git a/cumulus/test/parachain/src/command.rs b/cumulus/test/parachain/src/command.rs
index 59955074a82..326a25e4bc1 100644
--- a/cumulus/test/parachain/src/command.rs
+++ b/cumulus/test/parachain/src/command.rs
@@ -23,6 +23,7 @@ use sc_cli::{
 	CliConfiguration, Error, ImportParams, KeystoreParams, NetworkParams, Result, SharedParams,
 	SubstrateCli,
 };
+use sc_executor::NativeExecutionDispatch;
 use sc_network::config::TransportConfig;
 use sc_service::config::{NetworkConfiguration, NodeKeyConfig, PrometheusConfig};
 use sp_core::hexdisplay::HexDisplay;
@@ -111,6 +112,36 @@ impl SubstrateCli for PolkadotCli {
 	}
 }
 
+fn generate_genesis_state() -> Result<Block> {
+	let storage = (&chain_spec::get_chain_spec()).build_storage()?;
+
+	let child_roots = storage.children_default.iter().map(|(sk, child_content)| {
+		let state_root =
+			<<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
+				child_content.data.clone().into_iter().collect(),
+			);
+		(sk.clone(), state_root.encode())
+	});
+	let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
+		storage.top.clone().into_iter().chain(child_roots).collect(),
+	);
+
+	let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
+		Vec::new(),
+	);
+
+	Ok(Block::new(
+		<<Block as BlockT>::Header as HeaderT>::new(
+			Zero::zero(),
+			extrinsics_root,
+			state_root,
+			Default::default(),
+			Default::default(),
+		),
+		Default::default(),
+	))
+}
+
 /// Parse command line arguments into service configuration.
 pub fn run() -> Result<()> {
 	let cli = Cli::from_args();
@@ -124,35 +155,7 @@ pub fn run() -> Result<()> {
 		Some(Subcommand::ExportGenesisState(params)) => {
 			sc_cli::init_logger("");
 
-			let storage = (&chain_spec::get_chain_spec()).build_storage()?;
-
-			let child_roots = storage.children_default.iter().map(|(sk, child_content)| {
-				let state_root =
-					<<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
-						child_content.data.clone().into_iter().collect(),
-					);
-				(sk.clone(), state_root.encode())
-			});
-			let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
-				storage.top.clone().into_iter().chain(child_roots).collect(),
-			);
-			let block = {
-				let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
-					Vec::new(),
-				);
-			
-				Block::new(
-					<<Block as BlockT>::Header as HeaderT>::new(
-						Zero::zero(),
-						extrinsics_root,
-						state_root,
-						Default::default(),
-						Default::default(),
-					),
-					Default::default(),
-				)
-			};
-
+			let block = generate_genesis_state()?;
 			let header_hex = format!("0x{:?}", HexDisplay::from(&block.header().encode()));
 
 			if let Some(output) = &params.output {
@@ -163,6 +166,38 @@ pub fn run() -> Result<()> {
 
 			Ok(())
 		}
+		Some(Subcommand::Polkadot(polkadot_cli)) => {
+			let runner = polkadot_cli.create_runner(&polkadot_cli.run.base)?;
+			let authority_discovery_enabled = polkadot_cli.run.authority_discovery_enabled;
+			let grandpa_pause = if polkadot_cli.run.grandpa_pause.is_empty() {
+				None
+			} else {
+				Some((polkadot_cli.run.grandpa_pause[0], polkadot_cli.run.grandpa_pause[1]))
+			};
+
+			runner.run_node(
+				|config| {
+					polkadot_service::polkadot_new_light(config)
+				},
+				|config| {
+					polkadot_service::polkadot_new_full(
+						config,
+						None,
+						None,
+						authority_discovery_enabled,
+						6000,
+						grandpa_pause
+					).map(|(s, _, _)| s)
+				},
+				polkadot_service::PolkadotExecutor::native_version().runtime_version
+			)
+		},
+		Some(Subcommand::PolkadotValidationWorker(cmd)) => {
+			sc_cli::init_logger("");
+			polkadot_service::run_validation_worker(&cmd.mem_id)?;
+
+			Ok(())
+		},
 		None => {
 			let runner = cli.create_runner(&cli.run)?;
 
diff --git a/cumulus/test/parachain/tests/integration_test.rs b/cumulus/test/parachain/tests/integration_test.rs
new file mode 100644
index 00000000000..85595c82f22
--- /dev/null
+++ b/cumulus/test/parachain/tests/integration_test.rs
@@ -0,0 +1,372 @@
+// Copyright 2020 Parity Technologies (UK) Ltd.
+// This file is part of Substrate.
+
+// Substrate 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.
+
+// Substrate 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 Substrate.  If not, see <http://www.gnu.org/licenses/>.
+
+// TODO: this is necessary for the jsonrpsee macro used
+#![allow(unused_variables, dead_code)]
+
+use assert_cmd::cargo::cargo_bin;
+use async_std::{net, task::sleep};
+use codec::Encode;
+use futures::{future::FutureExt, join, pin_mut, select};
+use polkadot_primitives::parachain::{Info, Scheduling};
+use polkadot_primitives::Hash as PHash;
+use polkadot_runtime::{Header, OnlyStakingAndClaims, Runtime, SignedExtra, SignedPayload};
+use polkadot_runtime_common::{parachains, registrar, BlockHashCount};
+use serde_json::Value;
+use sp_arithmetic::traits::SaturatedConversion;
+use sp_runtime::generic;
+use sp_version::RuntimeVersion;
+use std::{
+	collections::HashSet,
+	env, fs,
+	io::Read,
+	path::PathBuf,
+	process::{Child, Command, Stdio},
+	time::Duration,
+};
+use substrate_test_runtime_client::AccountKeyring::Alice;
+use tempfile::tempdir;
+
+static POLKADOT_ARGS: &[&str] = &["polkadot", "--chain=res/polkadot_chainspec.json"];
+static INTEGRATION_TEST_ALLOWED_TIME: Option<&str> = option_env!("INTEGRATION_TEST_ALLOWED_TIME");
+
+jsonrpsee::rpc_api! {
+	Author {
+		#[rpc(method = "author_submitExtrinsic", positional_params)]
+		fn submit_extrinsic(extrinsic: String) -> PHash;
+	}
+
+	Chain {
+		#[rpc(method = "chain_getFinalizedHead")]
+		fn current_block_hash() -> PHash;
+
+		#[rpc(method = "chain_getHeader", positional_params)]
+		fn header(hash: PHash) -> Option<Header>;
+
+		#[rpc(method = "chain_getBlockHash", positional_params)]
+		fn block_hash(hash: Option<u64>) -> Option<PHash>;
+	}
+
+	State {
+		#[rpc(method = "state_getRuntimeVersion")]
+		fn runtime_version() -> RuntimeVersion;
+	}
+
+	System {
+		#[rpc(method = "system_networkState")]
+		fn network_state() -> Value;
+	}
+}
+
+// Adapted from
+// https://github.com/rust-lang/cargo/blob/485670b3983b52289a2f353d589c57fae2f60f82/tests/testsuite/support/mod.rs#L507
+fn target_dir() -> PathBuf {
+	env::current_exe()
+		.ok()
+		.map(|mut path| {
+			path.pop();
+			if path.ends_with("deps") {
+				path.pop();
+			}
+			path
+		})
+		.unwrap()
+}
+
+struct ChildHelper<'a> {
+	name: String,
+	child: &'a mut Child,
+}
+
+impl<'a> Drop for ChildHelper<'a> {
+	fn drop(&mut self) {
+		let name = self.name.clone();
+
+		self.terminate();
+
+		let mut stdout = String::new();
+		if let Some(reader) = self.child.stdout.as_mut() {
+			let _ = reader.read_to_string(&mut stdout);
+		}
+		eprintln!("process '{}' stdout:\n{}\n", name, stdout,);
+
+		let mut stderr = String::new();
+		if let Some(reader) = self.child.stderr.as_mut() {
+			let _ = reader.read_to_string(&mut stderr);
+		}
+		eprintln!("process '{}' stderr:\n{}\n", name, stderr,);
+	}
+}
+
+impl<'a> ChildHelper<'a> {
+	fn new(name: &str, child: &'a mut Child) -> ChildHelper<'a> {
+		ChildHelper {
+			name: name.to_string(),
+			child,
+		}
+	}
+
+	fn terminate(&mut self) {
+		match self.child.try_wait() {
+			Ok(Some(_)) => return,
+			Ok(None) => {}
+			Err(err) => {
+				eprintln!("could not wait for child process to finish: {}", err);
+				let _ = self.child.kill();
+				let _ = self.child.wait();
+				return;
+			}
+		}
+
+		let _ = self.child.kill();
+
+		let _ = self.child.wait();
+	}
+}
+
+async fn wait_for_tcp<A: net::ToSocketAddrs + std::fmt::Display>(address: A) {
+	while let Err(err) = net::TcpStream::connect(&address).await {
+		eprintln!("Waiting for {} to be up ({})...", address, err);
+		sleep(Duration::from_secs(2)).await;
+	}
+}
+
+#[async_std::test]
+#[ignore]
+async fn integration_test() {
+	assert!(
+		!net::TcpStream::connect("127.0.0.1:27015").await.is_ok(),
+		"tcp port is already open 127.0.0.1:27015, this test cannot be run",
+	);
+	assert!(
+		!net::TcpStream::connect("127.0.0.1:27016").await.is_ok(),
+		"tcp port is already open 127.0.0.1:27016, this test cannot be run",
+	);
+
+	let t1 = sleep(Duration::from_secs(
+		INTEGRATION_TEST_ALLOWED_TIME
+			.and_then(|x| x.parse().ok())
+			.unwrap_or(600),
+	))
+	.fuse();
+	let t2 = async {
+		// start alice
+		let polkadot_alice_dir = tempdir().unwrap();
+		let mut polkadot_alice = Command::new(cargo_bin("cumulus-test-parachain-collator"))
+			.stdout(Stdio::piped())
+			.stderr(Stdio::piped())
+			.args(POLKADOT_ARGS)
+			.arg("--base-path")
+			.arg(polkadot_alice_dir.path())
+			.arg("--alice")
+			.arg("--unsafe-rpc-expose")
+			.arg("--rpc-port=27015")
+			.arg("--port=27115")
+			.spawn()
+			.unwrap();
+		let polkadot_alice_helper = ChildHelper::new("alice", &mut polkadot_alice);
+
+		// start bob
+		let polkadot_bob_dir = tempdir().unwrap();
+		let mut polkadot_bob = Command::new(cargo_bin("cumulus-test-parachain-collator"))
+			.stdout(Stdio::piped())
+			.stderr(Stdio::piped())
+			.args(POLKADOT_ARGS)
+			.arg("--base-path")
+			.arg(polkadot_bob_dir.path())
+			.arg("--bob")
+			.arg("--unsafe-rpc-expose")
+			.arg("--rpc-port=27016")
+			.arg("--port=27116")
+			.spawn()
+			.unwrap();
+		let polkadot_bob_helper = ChildHelper::new("bob", &mut polkadot_bob);
+
+		// wait for both nodes to be up and running
+		join!(
+			wait_for_tcp("127.0.0.1:27015"),
+			wait_for_tcp("127.0.0.1:27016")
+		);
+
+		// export genesis state
+		let cmd = Command::new(cargo_bin("cumulus-test-parachain-collator"))
+			.arg("export-genesis-state")
+			.output()
+			.unwrap();
+		assert!(cmd.status.success());
+		let output = &cmd.stdout;
+		let genesis_state = hex::decode(&output[2..output.len() - 1]).unwrap();
+
+		// connect RPC clients
+		let transport_client_alice =
+			jsonrpsee::transport::http::HttpTransportClient::new("http://127.0.0.1:27015");
+		let mut client_alice = jsonrpsee::raw::RawClient::new(transport_client_alice);
+		let transport_client_bob =
+			jsonrpsee::transport::http::HttpTransportClient::new("http://127.0.0.1:27016");
+		let mut client_bob = jsonrpsee::raw::RawClient::new(transport_client_bob);
+
+		// retrieve nodes network id
+		let polkadot_alice_id = System::network_state(&mut client_alice).await.unwrap()["peerId"]
+			.as_str()
+			.unwrap()
+			.to_string();
+		let polkadot_bob_id = System::network_state(&mut client_bob).await.unwrap()["peerId"]
+			.as_str()
+			.unwrap()
+			.to_string();
+
+		// retrieve runtime version
+		let runtime_version = State::runtime_version(&mut client_alice).await.unwrap();
+
+		// get the current block
+		let current_block_hash = Chain::block_hash(&mut client_alice, None)
+			.await
+			.unwrap()
+			.unwrap();
+		let current_block = Chain::header(&mut client_alice, current_block_hash)
+			.await
+			.unwrap()
+			.unwrap()
+			.number
+			.saturated_into::<u64>();
+
+		let genesis_block = Chain::block_hash(&mut client_alice, 0)
+			.await
+			.unwrap()
+			.unwrap();
+
+		// create and sign transaction
+		let wasm = fs::read(target_dir().join(
+			"wbuild/cumulus-test-parachain-runtime/cumulus_test_parachain_runtime.compact.wasm",
+		))
+		.unwrap();
+		let call = pallet_sudo::Call::sudo(Box::new(
+			registrar::Call::<Runtime>::register_para(
+				100.into(),
+				Info {
+					scheduling: Scheduling::Always,
+				},
+				wasm.into(),
+				genesis_state.into(),
+			)
+			.into(),
+		));
+		let nonce = 0;
+		let period = BlockHashCount::get()
+			.checked_next_power_of_two()
+			.map(|c| c / 2)
+			.unwrap_or(2) as u64;
+		let tip = 0;
+		let extra: SignedExtra = (
+			OnlyStakingAndClaims,
+			frame_system::CheckVersion::<Runtime>::new(),
+			frame_system::CheckGenesis::<Runtime>::new(),
+			frame_system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
+			frame_system::CheckNonce::<Runtime>::from(nonce),
+			frame_system::CheckWeight::<Runtime>::new(),
+			pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
+			registrar::LimitParathreadCommits::<Runtime>::new(),
+			parachains::ValidateDoubleVoteReports::<Runtime>::new(),
+		);
+		let raw_payload = SignedPayload::from_raw(
+			call.clone().into(),
+			extra.clone(),
+			(
+				(),
+				runtime_version.spec_version,
+				genesis_block,
+				current_block_hash,
+				(),
+				(),
+				(),
+				(),
+				(),
+			),
+		);
+		let signature = raw_payload.using_encoded(|e| Alice.sign(e));
+
+		// register parachain
+		let ex = polkadot_runtime::UncheckedExtrinsic::new_signed(
+			call.into(),
+			Alice.into(),
+			sp_runtime::MultiSignature::Sr25519(signature),
+			extra,
+		);
+		let _register_block_hash =
+			Author::submit_extrinsic(&mut client_alice, format!("0x{}", hex::encode(ex.encode())))
+				.await
+				.unwrap();
+
+		// run cumulus
+		let cumulus_dir = tempdir().unwrap();
+		let mut cumulus = Command::new(cargo_bin("cumulus-test-parachain-collator"))
+			.stdout(Stdio::piped())
+			.stderr(Stdio::piped())
+			.arg("--base-path")
+			.arg(cumulus_dir.path())
+			.arg("--unsafe-rpc-expose")
+			.arg("--rpc-port=27017")
+			.arg("--port=27117")
+			.arg("--")
+			.arg(format!(
+				"--bootnodes=/ip4/127.0.0.1/tcp/27115/p2p/{}",
+				polkadot_alice_id
+			))
+			.arg(format!(
+				"--bootnodes=/ip4/127.0.0.1/tcp/27116/p2p/{}",
+				polkadot_bob_id
+			))
+			.spawn()
+			.unwrap();
+		let cumulus_helper = ChildHelper::new("cumulus", &mut cumulus);
+		wait_for_tcp("127.0.0.1:27017").await;
+
+		// connect rpc client to cumulus
+		let transport_client_cumulus =
+			jsonrpsee::transport::http::HttpTransportClient::new("http://127.0.0.1:27017");
+		let mut client_cumulus = jsonrpsee::raw::RawClient::new(transport_client_cumulus);
+
+		// wait for parachain blocks to be produced
+		let number_of_blocks = 4;
+		let mut previous_blocks = HashSet::with_capacity(number_of_blocks);
+		loop {
+			let current_block_hash = Chain::block_hash(&mut client_cumulus, None)
+				.await
+				.unwrap()
+				.unwrap();
+
+			if previous_blocks.insert(current_block_hash) {
+				eprintln!("new parachain block: {}", current_block_hash);
+
+				if previous_blocks.len() == number_of_blocks {
+					break;
+				}
+			}
+
+			sleep(Duration::from_secs(2)).await;
+		}
+	}
+	.fuse();
+
+	pin_mut!(t1, t2);
+
+	select! {
+		_ = t1 => {
+			panic!("the test took too long, maybe no parachain blocks have been produced");
+		},
+		_ = t2 => {},
+	}
+}
-- 
GitLab