Skip to content
Snippets Groups Projects
Unverified Commit 1b37b9e1 authored by Nikos Kontakis's avatar Nikos Kontakis Committed by GitHub
Browse files

Implement parachain and validator registration process in Subxt (#112)


Fixes #106

---------

Co-authored-by: default avatarJavier Viola <javier@parity.io>
parent baf29310
Branches
No related merge requests found
......@@ -34,3 +34,5 @@ sha2 = { version = "0.10.2", default-features = false }
hex = "0.4"
sp-core = "22.0.0"
libp2p = { version = "0.52" }
subxt = "0.32.0"
subxt-signer = { version = "0.32.0", features = ["subxt"]}
use std::time::Duration;
use configuration::{NetworkConfigBuilder, RegistrationStrategy};
use orchestrator::{AddNodeOpts, Orchestrator};
use provider::NativeProvider;
use support::fs::local::LocalFileSystem;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = NetworkConfigBuilder::new()
.with_relaychain(|r| {
r.with_chain("rococo-local")
.with_default_command("polkadot")
.with_node(|node| node.with_name("alice"))
.with_node(|node| node.with_name("bob"))
})
.with_parachain(|p| {
p.with_id(100)
.cumulus_based(true)
.with_registration_strategy(RegistrationStrategy::UsingExtrinsic)
.with_collator(|n| n.with_name("collator").with_command("polkadot-parachain"))
})
.build()
.unwrap();
let fs = LocalFileSystem;
let provider = NativeProvider::new(fs.clone());
let orchestrator = Orchestrator::new(fs, provider);
let mut network = orchestrator.spawn(config).await?;
println!("🚀🚀🚀🚀 network deployed");
// add a new node
let opts = AddNodeOpts {
rpc_port: Some(9444),
is_validator: true,
..Default::default()
};
// TODO: add check to ensure if unique
network.add_node("new1", opts, None).await?;
tokio::time::sleep(Duration::from_secs(2)).await;
// Example of some opertions that you can do
// with `nodes` (e.g pause, resume, restart)
tokio::time::sleep(Duration::from_secs(10)).await;
// Get a ref to the node
let node = network.get_node("alice")?;
let is_10 = node.assert("block_height{status=\"best\"}", 10).await?;
println!("is_10: {is_10}");
let role = node.reports("node_roles").await?;
println!("Role is {role}");
// pause the node
// node.pause().await?;
// println!("node new1 paused!");
tokio::time::sleep(Duration::from_secs(2)).await;
// node.resume().await?;
// println!("node new1 resumed!");
let col_opts = AddNodeOpts {
command: Some("polkadot-parachain".try_into()?),
..Default::default()
};
network.add_node("new-col-1", col_opts, Some(100)).await?;
println!("new collator deployed!");
// For now let just loop....
#[allow(clippy::empty_loop)]
loop {}
// Ok(())
}
......@@ -23,4 +23,6 @@ sha2 = { workspace = true, default-features = false }
hex = { workspace = true }
sp-core = { workspace = true }
libp2p = { workspace = true }
subxt = { workspace = true }
subxt-signer = { workspace = true }
reqwest = { workspace = true }
pub mod chain_spec;
pub mod errors;
pub mod key;
pub mod para_artifact;
mod bootnode_addr;
mod command;
mod identity;
mod key;
mod keystore;
mod port;
......
......@@ -21,7 +21,10 @@ use provider::{constants::LOCALHOST, types::TransferedFile, Provider};
use support::fs::{FileSystem, FileSystemError};
use tokio::time::timeout;
use crate::{generators::chain_spec::ParaGenesisConfig, spawner::SpawnNodeCtx};
use crate::{
generators::chain_spec::ParaGenesisConfig, shared::types::RegisterParachainOptions,
spawner::SpawnNodeCtx,
};
pub struct Orchestrator<T, P>
where
......@@ -141,14 +144,13 @@ where
.await?;
}
let para_to_register_in_genesis: Vec<&ParachainSpec> = network_spec
.parachains
.iter()
.filter(|para| match &para.registration_strategy {
RegistrationStrategy::InGenesis => true,
RegistrationStrategy::UsingExtrinsic => false,
})
.collect();
// Gather the parachains to register in genesis and the ones to register with extrinsic
let (para_to_register_in_genesis, para_to_register_with_extrinsic): (
Vec<&ParachainSpec>,
Vec<&ParachainSpec>,
) = network_spec.parachains.iter().partition(|para| {
matches!(para.registration_strategy, RegistrationStrategy::InGenesis)
});
let mut para_artifacts = vec![];
for para in para_to_register_in_genesis {
......@@ -233,6 +235,9 @@ where
.iter_mut()
.map(|node| spawner::spawn_node(node, global_files_to_inject.clone(), &ctx));
// Initiate the node_ws_uel which will be later used in the Parachain_with_extrinsic config
let mut node_ws_url: String = "".to_string();
// Calculate the bootnodes addr from the running nodes
let mut bootnodes_addr: Vec<String> = vec![];
for node in futures::future::try_join_all(spawning_tasks).await? {
......@@ -246,6 +251,12 @@ where
&node.spec.p2p_cert_hash,
)?,
);
// Is used in the register_para_options (We need to get this from the relay and not the collators)
if node_ws_url.is_empty() {
node_ws_url = node.ws_uri.clone()
}
// Add the node to the `Network` instance
network.add_running_node(node, None);
}
......@@ -256,6 +267,7 @@ where
let spawning_tasks = relaynodes
.iter()
.map(|node| spawner::spawn_node(node, global_files_to_inject.clone(), &ctx));
for node in futures::future::try_join_all(spawning_tasks).await? {
// Add the node to the `Network` instance
network.add_running_node(node, None);
......@@ -324,6 +336,38 @@ where
}
}
// TODO: we should wait until node is ready!
if !para_to_register_with_extrinsic.is_empty() {
tokio::time::sleep(Duration::from_secs(10)).await;
}
// Now we need to register the paras with extrinsic from the Vec collected before;
for para in para_to_register_with_extrinsic {
let register_para_options: RegisterParachainOptions = RegisterParachainOptions {
id: para.id,
// This needs to resolve correctly
wasm_path: para
.genesis_wasm
.artifact_path()
.ok_or(OrchestratorError::InvariantError(
"artifact path for wasm must be set at this point",
))?
.to_path_buf(),
state_path: para
.genesis_state
.artifact_path()
.ok_or(OrchestratorError::InvariantError(
"artifact path for state must be set at this point",
))?
.to_path_buf(),
node_ws_url: node_ws_url.clone(),
onboard_as_para: para.onboard_as_parachain,
seed: None, // TODO: Seed is passed by?
finalization: false, // TODO: Seed is passed by?
};
Parachain::register(register_para_options, &scoped_fs).await?;
}
// TODO (future):
// - add-ons (introspector/tracing/etc)
......
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
str::FromStr,
};
use subxt::{dynamic::Value, OnlineClient, SubstrateConfig};
use subxt_signer::{sr25519::Keypair, SecretUri};
use support::fs::FileSystem;
// use crate::generators::key::generate_pair;
// use sp_core::{sr25519, Pair};
use super::node::NetworkNode;
use crate::{shared::types::RegisterParachainOptions, ScopedFilesystem};
#[derive(Debug)]
pub struct Parachain {
......@@ -35,4 +45,62 @@ impl Parachain {
collators: Default::default(),
}
}
pub async fn register(
options: RegisterParachainOptions,
scoped_fs: &ScopedFilesystem<'_, impl FileSystem>,
) -> Result<(), anyhow::Error> {
println!("Registering parachain: {:?}", options);
// get the seed
let sudo: Keypair;
if let Some(possible_seed) = options.seed {
sudo = Keypair::from_seed(possible_seed).expect("seed should return a Keypair.");
} else {
let uri = SecretUri::from_str("//Alice")?;
sudo = Keypair::from_uri(&uri)?;
}
let genesis_state = scoped_fs
.read_to_string(options.state_path)
.await
.expect("State Path should be ok by this point.");
let wasm_data = scoped_fs
.read_to_string(options.wasm_path)
.await
.expect("Wasm Path should be ok by this point.");
let api = OnlineClient::<SubstrateConfig>::from_url(options.node_ws_url).await?;
let schedule_para = subxt::dynamic::tx(
"ParasSudoWrapper",
"sudo_schedule_para_initialize",
vec![
Value::primitive(options.id.into()),
Value::named_composite([
(
"genesis_head",
Value::from_bytes(hex::decode(&genesis_state[2..])?),
),
(
"validation_code",
Value::from_bytes(hex::decode(&wasm_data[2..])?),
),
("para_kind", Value::bool(options.onboard_as_para)),
]),
],
);
let sudo_call = subxt::dynamic::tx("Sudo", "sudo", vec![schedule_para.into_value()]);
// TODO: uncomment below and fix the sign and submit (and follow afterwards until
// finalized block) to register the parachain
let result = api
.tx()
.sign_and_submit_then_watch_default(&sudo_call, &sudo)
.await?;
let result = result.wait_for_in_block().await?;
println!("In block: {:#?}", result.block_hash());
Ok(())
}
}
use std::{
collections::HashMap,
net::TcpListener,
path::PathBuf,
sync::{Arc, RwLock},
};
pub type Accounts = HashMap<String, NodeAccount>;
use configuration::shared::{
resources::Resources,
types::{Arg, AssetLocation, Command, Image, Port},
};
pub type Accounts = HashMap<String, NodeAccount>;
#[derive(Debug, Clone, PartialEq)]
pub struct NodeAccount {
pub address: String,
......@@ -55,3 +57,21 @@ pub struct ChainDefaultContext<'a> {
pub default_db_snapshot: Option<&'a AssetLocation>,
pub default_args: Vec<&'a Arg>,
}
#[derive(Debug, Clone)]
pub struct RegisterParachainOptions {
pub id: u32,
pub wasm_path: PathBuf,
pub state_path: PathBuf,
pub node_ws_url: String,
pub onboard_as_para: bool,
pub seed: Option<[u8; 32]>,
pub finalization: bool,
}
#[derive(Debug, Clone)]
pub struct ParachainGenesisArgs {
pub genesis_head: String,
pub validation_code: String,
pub parachain: bool,
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment