Add an initial Substrate instantiation

Consensus has been nuked for an AcceptAny currently routed throough PoW 
(when it doesn't have to be, doing so just took care of a few pieces of 
leg work).

Updates AGPL handling.
This commit is contained in:
Luke Parker
2022-07-15 00:05:00 -04:00
parent 5ede5b9e8f
commit 0b879a53fa
22 changed files with 2094 additions and 658 deletions

View File

@@ -0,0 +1,74 @@
use sc_service::ChainType;
use sp_runtime::traits::Verify;
use sp_core::{sr25519, Pair, Public};
use sp_runtime::traits::IdentifyAccount;
use serai_runtime::{
WASM_BINARY, AccountId, Signature, GenesisConfig, SystemConfig, BalancesConfig
};
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
type AccountPublic = <Signature as Verify>::Signer;
fn get_from_seed<TPublic: Public>(seed: &'static str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None).unwrap().public()
}
fn get_account_id_from_seed<TPublic: Public>(seed: &'static str) -> AccountId
where AccountPublic: From<<TPublic::Pair as Pair>::Public> {
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
fn testnet_genesis(
wasm_binary: &[u8],
endowed_accounts: Vec<AccountId>
) -> GenesisConfig {
GenesisConfig {
system: SystemConfig {
code: wasm_binary.to_vec(),
},
balances: BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
},
transaction_payment: Default::default()
}
}
pub fn development_config() -> Result<ChainSpec, &'static str> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available")?;
Ok(
ChainSpec::from_genesis(
// Name
"Development Network",
// ID
"dev",
ChainType::Development,
|| {
testnet_genesis(
wasm_binary,
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
]
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
Some("serai"),
// Fork ID
None,
// Properties
None,
// Extensions
None
)
)
}

53
substrate/node/src/cli.rs Normal file
View File

@@ -0,0 +1,53 @@
use sc_cli::RunCmd;
#[derive(Debug, clap::Parser)]
pub struct Cli {
#[clap(subcommand)]
pub subcommand: Option<Subcommand>,
#[clap(flatten)]
pub run: RunCmd,
}
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
/// Key management CLI utilities
#[clap(subcommand)]
Key(sc_cli::KeySubcommand),
/// Build a chain specification
BuildSpec(sc_cli::BuildSpecCmd),
/// Validate blocks
CheckBlock(sc_cli::CheckBlockCmd),
/// Export blocks
ExportBlocks(sc_cli::ExportBlocksCmd),
/// Export the state of a given block into a chain spec
ExportState(sc_cli::ExportStateCmd),
/// Import blocks
ImportBlocks(sc_cli::ImportBlocksCmd),
/// Remove the entire chain
PurgeChain(sc_cli::PurgeChainCmd),
/// Revert the chain to a previous state
Revert(sc_cli::RevertCmd),
/// Sub-commands concerned with benchmarking
#[clap(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try some command against the runtime state
#[cfg(feature = "try-runtime")]
TryRuntime(try_runtime_cli::TryRuntimeCmd),
/// Try some command against the runtime state. Note: `try-runtime` feature must be enabled
#[cfg(not(feature = "try-runtime"))]
TryRuntime,
/// DB meta columns information
ChainInfo(sc_cli::ChainInfoCmd),
}

View File

@@ -0,0 +1,176 @@
use std::sync::Arc;
use sc_service::PartialComponents;
use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};
use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli};
use serai_runtime::Block;
use crate::{
chain_spec,
cli::{Cli, Subcommand},
command_helper::{BenchmarkExtrinsicBuilder, inherent_benchmark_data},
service
};
impl SubstrateCli for Cli {
fn impl_name() -> String {
"Serai Node".into()
}
fn impl_version() -> String {
env!("SUBSTRATE_CLI_IMPL_VERSION").to_string()
}
fn description() -> String {
env!("CARGO_PKG_DESCRIPTION").to_string()
}
fn author() -> String {
env!("CARGO_PKG_AUTHORS").to_string()
}
fn support_url() -> String {
"serai.exchange".to_string()
}
fn copyright_start_year() -> i32 {
2022
}
fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
match id {
"dev" => Ok(Box::new(chain_spec::development_config()?)),
_ => panic!("Unknown network ID")
}
}
fn native_runtime_version(_: &Box<dyn ChainSpec>) -> &'static RuntimeVersion {
&serai_runtime::VERSION
}
}
pub fn run() -> sc_cli::Result<()> {
let cli = Cli::from_args();
match &cli.subcommand {
Some(Subcommand::Key(cmd)) => cmd.run(&cli),
Some(Subcommand::BuildSpec(cmd)) => {
cli.create_runner(cmd)?.sync_run(|config| cmd.run(config.chain_spec, config.network))
},
Some(Subcommand::CheckBlock(cmd)) => {
cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents {
client,
task_manager,
import_queue,
..
} = service::new_partial(&config)?;
Ok((cmd.run(client, import_queue), task_manager))
})
},
Some(Subcommand::ExportBlocks(cmd)) => {
cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
Ok((cmd.run(client, config.database), task_manager))
})
},
Some(Subcommand::ExportState(cmd)) => {
cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
Ok((cmd.run(client, config.chain_spec), task_manager))
})
},
Some(Subcommand::ImportBlocks(cmd)) => {
cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents {
client,
task_manager,
import_queue,
..
} = service::new_partial(&config)?;
Ok((cmd.run(client, import_queue), task_manager))
})
},
Some(Subcommand::PurgeChain(cmd)) => {
cli.create_runner(cmd)?.sync_run(|config| cmd.run(config.database))
},
Some(Subcommand::Revert(cmd)) => {
cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents {
client,
task_manager,
backend,
..
} = service::new_partial(&config)?;
Ok((cmd.run(client, backend, None), task_manager))
})
},
Some(Subcommand::Benchmark(cmd)) => {
cli.create_runner(cmd)?.sync_run(|config| {
match cmd {
BenchmarkCmd::Pallet(cmd) => {
cmd.run::<Block, service::ExecutorDispatch>(config)
},
BenchmarkCmd::Block(cmd) => {
cmd.run(service::new_partial(&config)?.client)
},
BenchmarkCmd::Storage(cmd) => {
let PartialComponents { client, backend, .. } = service::new_partial(&config)?;
cmd.run(config, client, backend.expose_db(), backend.expose_storage())
},
BenchmarkCmd::Overhead(cmd) => {
let client = service::new_partial(&config)?.client;
cmd.run(
config,
client.clone(),
inherent_benchmark_data()?,
Arc::new(BenchmarkExtrinsicBuilder::new(client))
)
},
BenchmarkCmd::Machine(cmd) => cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())
}
})
},
#[cfg(feature = "try-runtime")]
Some(Subcommand::TryRuntime(cmd)) => {
cli.create_runner(cmd)?.async_run(|config| {
Ok(
(
cmd.run::<Block, service::ExecutorDispatch>(config),
sc_service::TaskManager::new(
config.tokio_handle.clone(),
config.prometheus_config.as_ref().map(|cfg| &cfg.registry)
).map_err(|e| sc_cli::Error::Service(sc_service::Error::Prometheus(e)))?
)
)
})
},
#[cfg(not(feature = "try-runtime"))]
Some(Subcommand::TryRuntime) => Err("TryRuntime wasn't enabled when building the node".into()),
Some(Subcommand::ChainInfo(cmd)) => {
cli.create_runner(cmd)?.sync_run(|config| cmd.run::<Block>(&config))
},
None => {
cli.create_runner(&cli.run)?.run_node_until_exit(|config| async {
service::new_full(config).map_err(sc_cli::Error::Service)
})
}
}
}

View File

@@ -0,0 +1,95 @@
use std::{sync::Arc, time::Duration};
use sp_core::{Encode, Pair};
use sp_keyring::Sr25519Keyring;
use sp_inherents::{InherentData, InherentDataProvider};
use sp_runtime::OpaqueExtrinsic;
use sc_cli::Result;
use sc_client_api::BlockBackend;
use serai_runtime as runtime;
use runtime::SystemCall;
use crate::service::FullClient;
pub struct BenchmarkExtrinsicBuilder {
client: Arc<FullClient>
}
impl BenchmarkExtrinsicBuilder {
pub fn new(client: Arc<FullClient>) -> Self {
Self { client }
}
}
impl frame_benchmarking_cli::ExtrinsicBuilder for BenchmarkExtrinsicBuilder {
fn remark(&self, nonce: u32) -> std::result::Result<OpaqueExtrinsic, &'static str> {
Ok(
OpaqueExtrinsic::from(
create_benchmark_extrinsic(
self.client.as_ref(),
Sr25519Keyring::Bob.pair(),
SystemCall::remark { remark: vec![] }.into(),
nonce
)
)
)
}
}
pub fn create_benchmark_extrinsic(
client: &FullClient,
sender: sp_core::sr25519::Pair,
call: runtime::Call,
nonce: u32,
) -> runtime::UncheckedExtrinsic {
let extra = (
frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
frame_system::CheckTxVersion::<runtime::Runtime>::new(),
frame_system::CheckGenesis::<runtime::Runtime>::new(),
frame_system::CheckEra::<runtime::Runtime>::from(
sp_runtime::generic::Era::mortal(
u64::from(
runtime::BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2)
),
client.chain_info().best_number.into(),
)
),
frame_system::CheckNonce::<runtime::Runtime>::from(nonce),
frame_system::CheckWeight::<runtime::Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0)
);
runtime::UncheckedExtrinsic::new_signed(
call.clone(),
sp_runtime::AccountId32::from(sender.public()).into(),
runtime::Signature::Sr25519(
runtime::SignedPayload::from_raw(
call.clone(),
extra.clone(),
(
(),
runtime::VERSION.spec_version,
runtime::VERSION.transaction_version,
client.block_hash(0).ok().flatten().unwrap(),
client.chain_info().best_hash,
(),
(),
(),
)
).using_encoded(|e| sender.sign(e))
),
extra,
)
}
pub fn inherent_benchmark_data() -> Result<InherentData> {
let mut inherent_data = InherentData::new();
sp_timestamp::InherentDataProvider::new(Duration::from_millis(0).into())
.provide_inherent_data(&mut inherent_data)
.map_err(|e| format!("creating inherent data: {:?}", e))?;
Ok(inherent_data)
}

View File

@@ -0,0 +1,3 @@
pub mod chain_spec;
pub mod rpc;
pub mod service;

View File

@@ -0,0 +1,13 @@
mod chain_spec;
#[macro_use]
mod service;
mod command_helper;
mod command;
mod rpc;
mod cli;
fn main() -> sc_cli::Result<()> {
command::run()
}

42
substrate/node/src/rpc.rs Normal file
View File

@@ -0,0 +1,42 @@
use std::sync::Arc;
use jsonrpsee::RpcModule;
use sp_blockchain::{Error as BlockchainError, HeaderBackend, HeaderMetadata};
use sc_transaction_pool_api::TransactionPool;
use sp_block_builder::BlockBuilder;
use sp_api::ProvideRuntimeApi;
pub use sc_rpc_api::DenyUnsafe;
use serai_runtime::{opaque::Block, AccountId, Balance, Index};
pub struct FullDeps<C, P> {
pub client: Arc<C>,
pub pool: Arc<P>,
pub deny_unsafe: DenyUnsafe
}
pub fn create_full<
C: ProvideRuntimeApi<Block> +
HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockchainError> +
Send + Sync + 'static,
P: TransactionPool + 'static
>(
deps: FullDeps<C, P>
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
where C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index> +
pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance> +
BlockBuilder<Block> {
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
use substrate_frame_rpc_system::{System, SystemApiServer};
let mut module = RpcModule::new(());
let FullDeps { client, pool, deny_unsafe } = deps;
module.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?;
module.merge(TransactionPayment::new(client).into_rpc())?;
Ok(module)
}

View File

@@ -0,0 +1,170 @@
use std::sync::Arc;
use sc_service::{error::Error as ServiceError, Configuration, TaskManager};
use sc_executor::NativeElseWasmExecutor;
use sc_telemetry::{Telemetry, TelemetryWorker};
use serai_runtime::{self, opaque::Block, RuntimeApi};
pub(crate) use serai_consensus::{ExecutorDispatch, FullClient};
type FullBackend = sc_service::TFullBackend<Block>;
type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
type PartialComponents = sc_service::PartialComponents<
FullClient,
FullBackend,
FullSelectChain,
sc_consensus::DefaultImportQueue<Block, FullClient>,
sc_transaction_pool::FullPool<Block, FullClient>,
Option<Telemetry>,
>;
pub fn new_partial(config: &Configuration) -> Result<PartialComponents, ServiceError> {
if config.keystore_remote.is_some() {
return Err(ServiceError::Other("Remote Keystores are not supported".to_string()))
}
let telemetry = config
.telemetry_endpoints
.clone()
.filter(|x| !x.is_empty())
.map(|endpoints| -> Result<_, sc_telemetry::Error> {
let worker = TelemetryWorker::new(16)?;
let telemetry = worker.handle().new_telemetry(endpoints);
Ok((worker, telemetry))
})
.transpose()?;
let executor = NativeElseWasmExecutor::<ExecutorDispatch>::new(
config.wasm_method,
config.default_heap_pages,
config.max_runtime_instances,
config.runtime_cache_size
);
let (
client,
backend,
keystore_container,
task_manager
) = sc_service::new_full_parts::<Block, RuntimeApi, _>(
config,
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
executor
)?;
let client = Arc::new(client);
let telemetry = telemetry.map(|(worker, telemetry)| {
task_manager.spawn_handle().spawn("telemetry", None, worker.run());
telemetry
});
let select_chain = sc_consensus::LongestChain::new(backend.clone());
let transaction_pool = sc_transaction_pool::BasicPool::new_full(
config.transaction_pool.clone(),
config.role.is_authority().into(),
config.prometheus_registry(),
task_manager.spawn_essential_handle(),
client.clone()
);
let import_queue = serai_consensus::import_queue(
&task_manager,
client.clone(),
select_chain.clone(),
config.prometheus_registry()
)?;
Ok(
sc_service::PartialComponents {
client,
backend,
task_manager,
import_queue,
keystore_container,
select_chain,
transaction_pool,
other: telemetry,
}
)
}
pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
let sc_service::PartialComponents {
client,
backend,
mut task_manager,
import_queue,
keystore_container,
select_chain,
other: mut telemetry,
transaction_pool
} = new_partial(&config)?;
let (network, system_rpc_tx, network_starter) = sc_service::build_network(
sc_service::BuildNetworkParams {
config: &config,
client: client.clone(),
transaction_pool: transaction_pool.clone(),
spawn_handle: task_manager.spawn_handle(),
import_queue,
block_announce_validator_builder: None,
warp_sync: None,
}
)?;
if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config,
task_manager.spawn_handle(),
client.clone(),
network.clone(),
);
}
let role = config.role.clone();
let prometheus_registry = config.prometheus_registry().cloned();
let rpc_extensions_builder = {
let client = client.clone();
let pool = transaction_pool.clone();
Box::new(
move |deny_unsafe, _| {
crate::rpc::create_full(
crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }
).map_err(Into::into)
}
)
};
sc_service::spawn_tasks(
sc_service::SpawnTasksParams {
network: network.clone(),
client: client.clone(),
keystore: keystore_container.sync_keystore(),
task_manager: &mut task_manager,
transaction_pool: transaction_pool.clone(),
rpc_builder: rpc_extensions_builder,
backend,
system_rpc_tx,
config,
telemetry: telemetry.as_mut(),
}
)?;
if role.is_authority() {
serai_consensus::authority(
&task_manager,
client,
network,
transaction_pool,
select_chain,
prometheus_registry.as_ref()
);
}
network_starter.start_network();
Ok(task_manager)
}