diff --git a/Cargo.lock b/Cargo.lock index 7376d035104b4..80e877ae20576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2642,6 +2642,34 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "frame" +version = "0.0.1-dev" +dependencies = [ + "docify 0.2.0", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "pallet-examples", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-arithmetic", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", +] + [[package]] name = "frame-benchmarking" version = "4.0.0-dev" @@ -5086,6 +5114,53 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "minimal-node" +version = "4.0.0-dev" +dependencies = [ + "clap 4.3.2", + "frame", + "futures", + "futures-timer", + "jsonrpsee", + "minimal-runtime", + "sc-basic-authorship", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-manual-seal", + "sc-executor", + "sc-network", + "sc-offchain", + "sc-rpc-api", + "sc-service", + "sc-telemetry", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-io", + "sp-keyring", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", +] + +[[package]] +name = "minimal-runtime" +version = "0.1.0" +dependencies = [ + "frame", + "pallet-balances", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "substrate-wasm-builder", +] + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -6615,6 +6690,15 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-example-frame-crate" +version = "4.0.0-dev" +dependencies = [ + "frame", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "pallet-example-kitchensink" version = "4.0.0-dev" @@ -6671,6 +6755,7 @@ dependencies = [ "pallet-default-config-example", "pallet-dev-mode", "pallet-example-basic", + "pallet-example-frame-crate", "pallet-example-kitchensink", "pallet-example-offchain-worker", "pallet-example-split", diff --git a/Cargo.toml b/Cargo.toml index bbbb8563f5b28..faa6f1c7a7f14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,8 @@ path = "bin/node/cli/bin/main.rs" resolver = "2" members = [ + "bin/minimal/node", + "bin/minimal/runtime", "bin/node-template/node", "bin/node-template/pallets/template", "bin/node-template/runtime", @@ -111,6 +113,7 @@ members = [ "client/transaction-pool", "client/transaction-pool/api", "client/utils", + "frame", "frame/alliance", "frame/asset-conversion", "frame/assets", diff --git a/bin/minimal/node/Cargo.toml b/bin/minimal/node/Cargo.toml new file mode 100644 index 0000000000000..ac9813c3dd684 --- /dev/null +++ b/bin/minimal/node/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "minimal-node" +version = "4.0.0-dev" +description = "A fresh FRAME-based Substrate node, ready for hacking." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io/" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" +build = "build.rs" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[[bin]] +name = "minimal-node" + +[dependencies] +clap = { version = "4.0.9", features = ["derive"] } +futures = { version = "0.3.21", features = ["thread-pool"] } +futures-timer = "3.0.1" +jsonrpsee = { version = "0.16.2", features = ["server"] } + +sc-cli = { path = "../../../client/cli" } +sc-executor = { path = "../../../client/executor" } +sc-network = { path = "../../../client/network" } +sc-service = { path = "../../../client/service" } +sc-telemetry = { path = "../../../client/telemetry" } +sc-transaction-pool = { path = "../../../client/transaction-pool" } +sc-transaction-pool-api = { path = "../../../client/transaction-pool/api" } +sc-consensus = { path = "../../../client/consensus/common" } +sc-consensus-manual-seal = { path = "../../../client/consensus/manual-seal" } +sc-rpc-api = { path = "../../../client/rpc-api" } +sc-basic-authorship = { path = "../../../client/basic-authorship" } +sc-offchain = { path = "../../../client/offchain" } +sc-client-api = { path = "../../../client/api" } + +sp-timestamp = { path = "../../../primitives/timestamp" } +sp-keyring = { path = "../../../primitives/keyring" } +sp-api = { path = "../../../primitives/api" } +sp-blockchain = { path = "../../../primitives/blockchain" } +sp-block-builder = { path = "../../../primitives/block-builder" } +sp-io = { path = "../../../primitives/io" } + + +substrate-frame-rpc-system = { path = "../../../utils/frame/rpc/system" } + +frame = { path = "../../../frame" } +runtime = { package = "minimal-runtime", path = "../runtime" } + +[build-dependencies] +substrate-build-script-utils = { version = "3.0.0", path = "../../../utils/build-script-utils" } + +[features] +default = [] diff --git a/bin/minimal/node/build.rs b/bin/minimal/node/build.rs new file mode 100644 index 0000000000000..a5b173537e8a2 --- /dev/null +++ b/bin/minimal/node/build.rs @@ -0,0 +1,6 @@ +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + rerun_if_git_head_changed(); +} diff --git a/bin/minimal/node/src/chain_spec.rs b/bin/minimal/node/src/chain_spec.rs new file mode 100644 index 0000000000000..c25b7889bbe0a --- /dev/null +++ b/bin/minimal/node/src/chain_spec.rs @@ -0,0 +1,66 @@ +use runtime::{BalancesConfig, RuntimeGenesisConfig, SudoConfig, SystemConfig, WASM_BINARY}; +use sc_service::{ChainType, Properties}; +use sp_keyring::AccountKeyring; + +/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. +pub type ChainSpec = sc_service::GenericChainSpec; + +fn props() -> Properties { + let mut properties = Properties::new(); + properties.insert("tokenDecimals".to_string(), 0.into()); + properties.insert("tokenSymbol".to_string(), "TEST".into()); + properties +} + +pub fn development_config() -> Result { + let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; + Ok(ChainSpec::from_genesis( + "Development", + "dev", + ChainType::Development, + move || testnet_genesis(wasm_binary), + vec![], + None, + None, + None, + Some(props()), + None, + )) +} + +pub fn local_testnet_config() -> Result { + let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; + + Ok(ChainSpec::from_genesis( + "Local Testnet", + "local_testnet", + ChainType::Local, + move || testnet_genesis(wasm_binary), + vec![], + None, + None, + None, + Some(props()), + None, + )) +} + +/// Configure initial storage state for FRAME modules. +fn testnet_genesis(wasm_binary: &[u8]) -> RuntimeGenesisConfig { + use frame::traits::Get; + use runtime::interface::{Balance, MinimumBalance}; + let endowment = >::get().max(1) * 1000; + let balances = AccountKeyring::iter() + .map(|a| (a.to_account_id(), endowment)) + .collect::>(); + RuntimeGenesisConfig { + system: SystemConfig { + // Add Wasm runtime to storage. + code: wasm_binary.to_vec(), + _config: Default::default(), + }, + balances: BalancesConfig { balances }, + sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, + ..Default::default() + } +} diff --git a/bin/minimal/node/src/cli.rs b/bin/minimal/node/src/cli.rs new file mode 100644 index 0000000000000..36985a48a8aef --- /dev/null +++ b/bin/minimal/node/src/cli.rs @@ -0,0 +1,64 @@ +use sc_cli::RunCmd; + +#[derive(Debug, Clone)] +pub enum Consensus { + ManualSeal(u64), + InstantSeal, +} + +impl std::str::FromStr for Consensus { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(if s == "instant-seal" { + Consensus::InstantSeal + } else if let Some(block_time) = s.strip_prefix("manual-seal-") { + Consensus::ManualSeal(block_time.parse().map_err(|_| "invalid block time")?) + } else { + return Err("incorrect consensus identifier".into()) + }) + } +} + +#[derive(Debug, clap::Parser)] +pub struct Cli { + #[command(subcommand)] + pub subcommand: Option, + + #[clap(long, default_value = "manual-seal-3000")] + pub consensus: Consensus, + + #[clap(flatten)] + pub run: RunCmd, +} + +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + /// Key management cli utilities + #[command(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 whole chain. + PurgeChain(sc_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Db meta columns information. + ChainInfo(sc_cli::ChainInfoCmd), +} diff --git a/bin/minimal/node/src/command.rs b/bin/minimal/node/src/command.rs new file mode 100644 index 0000000000000..573ff31720c34 --- /dev/null +++ b/bin/minimal/node/src/command.rs @@ -0,0 +1,110 @@ +use crate::{ + chain_spec, + cli::{Cli, Subcommand}, + service, +}; +use sc_cli::SubstrateCli; +use sc_service::PartialComponents; + +#[cfg(feature = "try-runtime")] +use try_runtime_cli::block_building_info::timestamp_with_aura_info; + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Substrate Node".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + env!("CARGO_PKG_DESCRIPTION").into() + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "support.anonymous.an".into() + } + + fn copyright_start_year() -> i32 { + 2017 + } + + fn load_spec(&self, id: &str) -> Result, String> { + Ok(match id { + "dev" => Box::new(chain_spec::development_config()?), + "" | "local" => Box::new(chain_spec::local_testnet_config()?), + path => + Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), + }) + } +} + +/// Parse and run command line arguments +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)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.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)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.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)) => { + let runner = cli.create_runner(cmd)?; + runner.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)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.database)) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, backend, .. } = + service::new_partial(&config)?; + Ok((cmd.run(client, backend, None), task_manager)) + }) + }, + Some(Subcommand::ChainInfo(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run::(&config)) + }, + None => { + let runner = cli.create_runner(&cli.run)?; + runner.run_node_until_exit(|config| async move { + service::new_full(config, cli.consensus).map_err(sc_cli::Error::Service) + }) + }, + } +} diff --git a/bin/minimal/node/src/lib.rs b/bin/minimal/node/src/lib.rs new file mode 100644 index 0000000000000..f13719f81d1f1 --- /dev/null +++ b/bin/minimal/node/src/lib.rs @@ -0,0 +1,4 @@ +pub mod chain_spec; +pub(crate) mod cli; +pub mod rpc; +pub mod service; diff --git a/bin/minimal/node/src/main.rs b/bin/minimal/node/src/main.rs new file mode 100644 index 0000000000000..4449d28b9fa41 --- /dev/null +++ b/bin/minimal/node/src/main.rs @@ -0,0 +1,13 @@ +//! Substrate Node Template CLI library. +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; +mod rpc; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/bin/minimal/node/src/rpc.rs b/bin/minimal/node/src/rpc.rs new file mode 100644 index 0000000000000..d10401caa4511 --- /dev/null +++ b/bin/minimal/node/src/rpc.rs @@ -0,0 +1,55 @@ +//! A collection of node-specific RPC methods. +//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer +//! used by Substrate nodes. This file extends those RPC definitions with +//! capabilities that are specific to this project's runtime configuration. + +#![warn(missing_docs)] + +use jsonrpsee::RpcModule; +use runtime::interface::{AccountId, Index, OpaqueBlock}; +use sc_transaction_pool_api::TransactionPool; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use std::sync::Arc; + +pub use sc_rpc_api::DenyUnsafe; + +/// Full client dependencies. +pub struct FullDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Whether to deny unsafe calls + pub deny_unsafe: DenyUnsafe, +} + +/// Instantiate all full RPC extensions. +pub fn create_full( + deps: FullDeps, +) -> Result, Box> +where + C: sp_api::ProvideRuntimeApi< + // TODO: bloody hell.. + frame::deps::sp_runtime::generic::Block< + frame::deps::sp_runtime::generic::Header, + frame::deps::sp_runtime::OpaqueExtrinsic, + >, + // OpaqueBlock, + >, + C: HeaderBackend + HeaderMetadata + 'static, + C: Send + Sync + 'static, + P: TransactionPool + 'static, + C::Api: BlockBuilder, + C::Api: substrate_frame_rpc_system::AccountNonceApi, +{ + 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())?; + // NOTE: we have intentionally ignored adding tx-pool's custom RPC here. + + Ok(module) +} diff --git a/bin/minimal/node/src/service.rs b/bin/minimal/node/src/service.rs new file mode 100644 index 0000000000000..700dc45b52043 --- /dev/null +++ b/bin/minimal/node/src/service.rs @@ -0,0 +1,236 @@ +use futures::FutureExt; +use runtime::{self, interface::OpaqueBlock as Block, RuntimeApi}; +use sc_client_api::backend::Backend; +use sc_executor::WasmExecutor; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; +use sc_telemetry::{Telemetry, TelemetryWorker}; +use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use std::sync::Arc; + +use crate::cli::Consensus; + +#[cfg(feature = "runtime-benchmarks")] +type HostFunctions = + (sp_io::SubstrateHostFunctions, frame_benchmarking::benchmarking::HostFunctions); + +#[cfg(not(feature = "runtime-benchmarks"))] +type HostFunctions = sp_io::SubstrateHostFunctions; + +pub(crate) type FullClient = + sc_service::TFullClient>; +type FullBackend = sc_service::TFullBackend; +type FullSelectChain = sc_consensus::LongestChain; + +pub fn new_partial( + config: &Configuration, +) -> Result< + sc_service::PartialComponents< + FullClient, + FullBackend, + FullSelectChain, + sc_consensus::DefaultImportQueue, + sc_transaction_pool::FullPool, + Option, + >, + ServiceError, +> { + 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 = sc_service::new_wasm_executor(&config); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::( + 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 = sc_consensus_manual_seal::import_queue( + Box::new(client.clone()), + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + ); + + Ok(sc_service::PartialComponents { + client, + backend, + task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: (telemetry), + }) +} + +/// Builds a new service for a full client. +pub fn new_full(config: Configuration, consensus: Consensus) -> Result { + let sc_service::PartialComponents { + client, + backend, + mut task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: mut telemetry, + } = new_partial(&config)?; + + let net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); + + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = + 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, + net_config, + block_announce_validator_builder: None, + warp_sync_params: None, + })?; + + if config.offchain_worker.enabled { + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-worker", + sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { + runtime_api_provider: client.clone(), + is_validator: config.role.is_authority(), + keystore: Some(keystore_container.keystore()), + offchain_db: backend.offchain_storage(), + transaction_pool: Some(OffchainTransactionPoolFactory::new( + transaction_pool.clone(), + )), + network_provider: network.clone(), + enable_http_requests: true, + custom_extensions: |_| vec![], + }) + .run(client.clone(), task_manager.spawn_handle()) + .boxed(), + ); + } + + let rpc_extensions_builder = { + let client = client.clone(); + let pool = transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = + crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; + crate::rpc::create_full(deps).map_err(Into::into) + }) + }; + + let prometheus_registry = config.prometheus_registry().cloned(); + + let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + network, + client: client.clone(), + keystore: keystore_container.keystore(), + task_manager: &mut task_manager, + transaction_pool: transaction_pool.clone(), + rpc_builder: rpc_extensions_builder, + backend, + system_rpc_tx, + tx_handler_controller, + sync_service, + config, + telemetry: telemetry.as_mut(), + })?; + + let proposer = sc_basic_authorship::ProposerFactory::new( + task_manager.spawn_handle(), + client.clone(), + transaction_pool.clone(), + prometheus_registry.as_ref(), + telemetry.as_ref().map(|x| x.handle()), + ); + + match consensus { + Consensus::InstantSeal => { + let params = sc_consensus_manual_seal::InstantSealParams { + block_import: client.clone(), + env: proposer, + client, + pool: transaction_pool, + select_chain, + consensus_data_provider: None, + create_inherent_data_providers: move |_, ()| async move { + Ok(sp_timestamp::InherentDataProvider::from_system_time()) + }, + }; + + let authorship_future = sc_consensus_manual_seal::run_instant_seal(params); + + task_manager.spawn_essential_handle().spawn_blocking( + "instant-seal", + None, + authorship_future, + ); + }, + Consensus::ManualSeal(block_time) => { + let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); + task_manager.spawn_handle().spawn("block_authoring", None, async move { + loop { + futures_timer::Delay::new(std::time::Duration::from_millis(block_time)).await; + sink.try_send(sc_consensus_manual_seal::EngineCommand::SealNewBlock { + create_empty: true, + finalize: true, + parent_hash: None, + sender: None, + }) + .unwrap(); + } + }); + + let params = sc_consensus_manual_seal::ManualSealParams { + block_import: client.clone(), + env: proposer, + client, + pool: transaction_pool, + select_chain, + commands_stream: Box::pin(commands_stream), + consensus_data_provider: None, + create_inherent_data_providers: move |_, ()| async move { + Ok(sp_timestamp::InherentDataProvider::from_system_time()) + }, + }; + let authorship_future = sc_consensus_manual_seal::run_manual_seal(params); + + task_manager.spawn_essential_handle().spawn_blocking( + "manual-seal", + None, + authorship_future, + ); + }, + } + + network_starter.start_network(); + Ok(task_manager) +} diff --git a/bin/minimal/runtime/Cargo.toml b/bin/minimal/runtime/Cargo.toml new file mode 100644 index 0000000000000..e0646b32d0e1b --- /dev/null +++ b/bin/minimal/runtime/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "minimal-runtime" +version = "0.1.0" +edition = "2021" + +[dependencies] +parity-scale-codec = { version = "3.0.0", default-features = false } +scale-info = { version = "2.6.0", default-features = false } + +# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. +frame = { path = "../../../frame", default-features = false, features = ["runtime"] } + +# pallets that we want to use +pallet-balances = { path = "../../../frame/balances", default-features = false } +pallet-sudo = { path = "../../../frame/sudo", default-features = false } +pallet-timestamp = { path = "../../../frame/timestamp", default-features = false } +pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } + +[build-dependencies] +substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true } + +[features] +default = ["std"] +std = [ + "frame/std", + + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment/std", + + "parity-scale-codec/std", + "scale-info/std", + + "substrate-wasm-builder", +] + + diff --git a/bin/minimal/runtime/build.rs b/bin/minimal/runtime/build.rs new file mode 100644 index 0000000000000..c03d618535be0 --- /dev/null +++ b/bin/minimal/runtime/build.rs @@ -0,0 +1,10 @@ +fn main() { + #[cfg(feature = "std")] + { + substrate_wasm_builder::WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build(); + } +} diff --git a/bin/minimal/runtime/src/lib.rs b/bin/minimal/runtime/src/lib.rs new file mode 100644 index 0000000000000..dca5625488a11 --- /dev/null +++ b/bin/minimal/runtime/src/lib.rs @@ -0,0 +1,209 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use frame::{ + deps::frame_support::weights::FixedFee, + prelude::*, + runtime::{ + apis::{self, *}, + prelude::*, + types_common, + }, +}; +#[runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("minimal-runtime"), + impl_name: create_runtime_str!("minimal-runtime"), + authoring_version: 1, + spec_version: 100, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); + +type Block = types_common::BlockOf; +type RuntimeExecutive = + Executive, Runtime, AllPalletsWithSystem>; + +construct_runtime!( + pub struct Runtime { + System: frame_system = 0, + Balances: pallet_balances = 1, + Sudo: pallet_sudo = 2, + // TODO: this is causing some issues in the JS side. + // Timestamp: pallet_timestamp = 3, + TransactionPayment: pallet_transaction_payment = 4, + } +); + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; +} + +#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type Version = Version; + type OnSetCode = (); + + type Block = Block; + type BlockHashCount = (); + + /// TODO: The default account-data provided by frame-system unfortunately cannot include a sane + /// value for this, as it relies on pallet-balances. Perhaps we should make an exception here, + /// and provide a `frame_system::config_preludes::SolochainDefaultConfigWithBalances` and + /// `frame_system::config_preludes::SolochainDefaultConfigWithAssets`? + type AccountData = pallet_balances::AccountData<::Balance>; +} + +#[derive_impl(pallet_balances::config_preludes::SolochainDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeHoldReason = RuntimeHoldReason; + type DustRemoval = (); + type AccountStore = System; + type ExistentialDeposit = ConstU128<1>; +} + +#[derive_impl(pallet_sudo::config_preludes::SolochainDefaultConfig as pallet_sudo::DefaultConfig)] +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; +} + +#[derive_impl(pallet_timestamp::config_preludes::SolochainDefaultConfig as pallet_timestamp::DefaultConfig)] +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = (); +} + +#[derive_impl(pallet_transaction_payment::config_preludes::SolochainDefaultConfig as pallet_transaction_payment::DefaultConfig)] +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type WeightToFee = FixedFee<0, interface::Balance>; + type LengthToFee = FixedFee<1, interface::Balance>; +} + +/// Some re-exports that the node side code needs to know. Some are useful in this context as well. +/// +/// Other types should preferably be private. +pub mod interface { + use super::*; + pub type OpaqueBlock = types_common::OpaqueBlockOf; + pub type AccountId = ::AccountId; + pub type Index = ::Nonce; + pub type Hash = ::Hash; + pub type Balance = ::Balance; + pub type MinimumBalance = ::ExistentialDeposit; +} + +impl_runtime_apis! { + impl apis::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + RuntimeExecutive::execute_block(block) + } + + fn initialize_block(header: &HeaderFor) { + RuntimeExecutive::initialize_block(header) + } + } + impl apis::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> Vec { + Runtime::metadata_versions() + } + } + + impl apis::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ExtrinsicFor) -> ApplyExtrinsicResult { + RuntimeExecutive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> HeaderFor { + RuntimeExecutive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: InherentData, + ) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl apis::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ExtrinsicFor, + block_hash: ::Hash, + ) -> TransactionValidity { + RuntimeExecutive::validate_transaction(source, tx, block_hash) + } + } + + impl apis::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &HeaderFor) { + RuntimeExecutive::offchain_worker(header) + } + } + + impl apis::SessionKeys for Runtime { + fn generate_session_keys(_seed: Option>) -> Vec { + Default::default() + } + + fn decode_session_keys( + _encoded: Vec, + ) -> Option, apis::KeyTypeId)>> { + Default::default() + } + } + + impl apis::AccountNonceApi for Runtime { + fn account_nonce(account: interface::AccountId) -> interface::Index { + System::account_nonce(account) + } + } +} diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index ac933eb6e551e..09c842eea4851 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -17,10 +17,12 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", ] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../../../frame/benchmarking" } + frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/system" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../../../frame/benchmarking" } + [dev-dependencies] sp-core = { version = "21.0.0", path = "../../../../primitives/core" } sp-io = { version = "23.0.0", path = "../../../../primitives/io" } @@ -30,10 +32,12 @@ sp-runtime = { version = "24.0.0", path = "../../../../primitives/runtime" } default = ["std"] std = [ "codec/std", - "frame-benchmarking?/std", + "scale-info/std", + "frame-support/std", "frame-system/std", - "scale-info/std", + + "frame-benchmarking?/std", ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] try-runtime = ["frame-support/try-runtime"] diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 03c9418b5c560..aad8029d58b08 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -247,7 +247,7 @@ pub async fn run_instant_seal( // instant-seal creates blocks as soon as transactions are imported // into the transaction pool. let commands_stream = pool.import_notification_stream().map(|_| EngineCommand::SealNewBlock { - create_empty: false, + create_empty: true, finalize: false, parent_hash: None, sender: None, diff --git a/frame/Cargo.toml b/frame/Cargo.toml new file mode 100644 index 0000000000000..b95892109fcd5 --- /dev/null +++ b/frame/Cargo.toml @@ -0,0 +1,95 @@ +[package] +name = "frame" +version = "0.0.1-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "The single package to get you started with building frame pallets and runtimes" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +# external deps +parity-scale-codec = { version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } + +# primitive deps, used for developing FRAME pallets. +sp-runtime = { default-features = false, path = "../primitives/runtime" } +sp-std = { default-features = false, path = "../primitives/std" } +sp-io = { default-features = false, path = "../primitives/io" } +sp-core = { default-features = false, path = "../primitives/core" } +sp-arithmetic = { default-features = false, path = "../primitives/arithmetic" } + +# frame deps, for developing FRAME pallets. +frame-support = { default-features = false, path = "./support" } +frame-system = { default-features = false, path = "./system" } + +# primitive types used for developing FRAME runtimes. +sp-version = { default-features = false, path = "../primitives/version", optional = true } +sp-api = { default-features = false, path = "../primitives/api", optional = true } +sp-block-builder = { default-features = false, path = "../primitives/block-builder", optional = true } +sp-transaction-pool = { default-features = false, path = "../primitives/transaction-pool", optional = true } +sp-offchain = { default-features = false, path = "../primitives/offchain", optional = true } +sp-session = { default-features = false, path = "../primitives/session", optional = true } +sp-consensus-aura = { default-features = false, path = "../primitives/consensus/aura", optional = true } +sp-consensus-grandpa = { default-features = false, path = "../primitives/consensus/grandpa", optional = true } +sp-inherents = { default-features = false, path = "../primitives/inherents", optional = true } + +frame-executive = { default-features = false, path = "../frame/executive", optional = true } +frame-system-rpc-runtime-api = { default-features = false, path = "../frame/system/rpc/runtime-api", optional = true } + +docify = "0.2.0" + +[dev-dependencies] +pallet-examples = { path = "./examples" } + + +[features] +default = [ "std", "runtime" ] +std = [ + # external deps + "parity-scale-codec/std", + "scale-info/std", + + # primitive deps + "sp-runtime/std", + "sp-std/std", + "sp-io/std", + "sp-core/std", + "sp-arithmetic/std", + + # frame dpes + "frame-support/std", + "frame-system/std", + + # Optional deps for runtime feature + "sp-version?/std", + "sp-api?/std", + "sp-block-builder?/std", + "sp-transaction-pool?/std", + "sp-offchain?/std", + "sp-session?/std", + "sp-consensus-aura?/std", + "sp-consensus-grandpa?/std", + "sp-inherents?/std", + + "frame-executive?/std", + "frame-system-rpc-runtime-api?/std", +] +runtime = [ + "sp-version", + "sp-api", + "sp-block-builder", + "sp-transaction-pool", + "sp-offchain", + "sp-session", + "sp-consensus-aura", + "sp-consensus-grandpa", + "sp-inherents", + + "frame-executive", + "frame-system-rpc-runtime-api" +] diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index e77b0f5e5d956..ea7e20efffc04 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -213,7 +213,41 @@ pub mod pallet { pub type CreditOf = Credit<::AccountId, Pallet>; - #[pallet::config] + pub mod config_preludes { + use super::*; + + pub struct TestDefaultConfig; + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + type Balance = u64; + type MaxLocks = ConstU32<{ u32::MAX }>; + type MaxReserves = ConstU32<{ u32::MAX }>; + type MaxFreezes = ConstU32<{ u32::MAX }>; + type MaxHolds = ConstU32<{ u32::MAX }>; + + type ReserveIdentifier = (); + type FreezeIdentifier = (); + + type WeightInfo = (); + } + + pub struct SolochainDefaultConfig; + #[frame_support::register_default_impl(SolochainDefaultConfig)] + impl DefaultConfig for SolochainDefaultConfig { + type Balance = u128; + type MaxLocks = ConstU32<{ 128 }>; + type MaxReserves = ConstU32<{ 128 }>; + type MaxFreezes = ConstU32<{ 128 }>; + type MaxHolds = ConstU32<{ 128 }>; + + type ReserveIdentifier = (); + type FreezeIdentifier = (); + + type WeightInfo = (); + } + } + + #[pallet::config(with_default)] pub trait Config: frame_system::Config { /// The overarching event type. type RuntimeEvent: From> @@ -236,6 +270,7 @@ pub mod pallet { + FixedPointOperand; /// Handler for the unbalanced reduction when removing a dust account. + #[pallet::no_default] type DustRemoval: OnUnbalanced>; /// The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO! @@ -247,9 +282,11 @@ pub mod pallet { /// /// Bottom line: Do yourself a favour and make it at least one! #[pallet::constant] + #[pallet::no_default] type ExistentialDeposit: Get; /// The means of storing the balances of an account. + #[pallet::no_default] type AccountStore: StoredMap>; /// The ID type for reserves. @@ -258,6 +295,7 @@ pub mod pallet { type ReserveIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy; /// The overarching hold reason. + #[pallet::no_default] type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Ord + Copy; /// The ID type for freezes. diff --git a/frame/examples/Cargo.toml b/frame/examples/Cargo.toml index af67bef792b6f..6dcf127c6ef73 100644 --- a/frame/examples/Cargo.toml +++ b/frame/examples/Cargo.toml @@ -18,6 +18,7 @@ pallet-example-offchain-worker = { default-features = false, path = "./offchain- pallet-example-kitchensink = { default-features = false, path = "./kitchensink" } pallet-dev-mode = { default-features = false, path = "./dev-mode" } pallet-example-split = { default-features = false, path = "./split" } +pallet-example-frame-crate = { default-features = false, path = "./frame-crate" } [features] default = [ "std" ] diff --git a/frame/examples/frame-crate/Cargo.toml b/frame/examples/frame-crate/Cargo.toml new file mode 100644 index 0000000000000..3c40c2239a0a6 --- /dev/null +++ b/frame/examples/frame-crate/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "pallet-example-frame-crate" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "MIT-0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME example pallet with umbrella crate" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +frame = { path = "../..", default-features = false } + + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + + "frame/std", +] diff --git a/frame/examples/frame-crate/src/lib.rs b/frame/examples/frame-crate/src/lib.rs new file mode 100644 index 0000000000000..010b6debdb8f9 --- /dev/null +++ b/frame/examples/frame-crate/src/lib.rs @@ -0,0 +1,56 @@ +use frame::prelude::*; + +#[frame::pallet(dev_mode)] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: IsType<::RuntimeEvent> + From>; + } + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::event] + pub enum Event {} + + #[pallet::storage] + pub type Value = StorageValue; + + #[pallet::call] + impl Pallet { + pub fn some_dispatchable(_origin: OriginFor) -> DispatchResult { + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + use crate::pallet as my_pallet; + use frame::{prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + MyPallet: my_pallet, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type BaseCallFilter = frame::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type OnSetCode = (); + type Block = MockBlock; + type BlockHashCount = (); + } + + impl my_pallet::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + } +} diff --git a/frame/examples/src/lib.rs b/frame/examples/src/lib.rs index d1cd32bb50f26..f35868540873d 100644 --- a/frame/examples/src/lib.rs +++ b/frame/examples/src/lib.rs @@ -38,3 +38,6 @@ //! //! - [**`pallet-example-split`**](./split): A simple example of a FRAME pallet demonstrating the //! ability to split sections across multiple files. +//! +//! - [**`pallet-example-frame-crate`**](./frame-crate): Example pallet showcasing how one can be +//! built using only the `frame` umbrella crate. diff --git a/frame/message-queue/src/lib.rs b/frame/message-queue/src/lib.rs index 08795914ebe78..f04f5aa178472 100644 --- a/frame/message-queue/src/lib.rs +++ b/frame/message-queue/src/lib.rs @@ -28,7 +28,7 @@ //! This is paramount for the success of the pallet since message execution is done in //! `on_initialize` which must _never_ under-estimate its PoV weight. It also needs a frugal PoV //! footprint since PoV is scarce and this is (possibly) done in every block. This must also hold -//! in the presence of unpredictable message size distributions. +//! in the presence of unpredictable message size distributions. //! 3. Usable as XCMP, DMP and UMP message/dispatch queue - possibly through adapter types. //! //! # Design @@ -39,10 +39,10 @@ //! which queue it will be stored. Messages are stored by being appended to the last [`Page`] of a //! book. Each book keeps track of its pages by indexing `Pages`. The `ReadyRing` contains all //! queues which hold at least one unprocessed message and are thereby *ready* to be serviced. The -//! `ServiceHead` indicates which *ready* queue is the next to be serviced. -//! The pallet implements [`frame_support::traits::EnqueueMessage`], -//! [`frame_support::traits::ServiceQueues`] and has [`frame_support::traits::ProcessMessage`] and -//! [`OnQueueChanged`] hooks to communicate with the outside world. +//! `ServiceHead` indicates which *ready* queue is the next to be serviced. The pallet implements +//! [`frame_support::traits::EnqueueMessage`], [`frame_support::traits::ServiceQueues`] and has +//! [`frame_support::traits::ProcessMessage`] and [`OnQueueChanged`] hooks to communicate with the +//! outside world. //! //! NOTE: The storage items are not linked since they are not public. //! @@ -118,12 +118,11 @@ //! executed automatically through `on_initialize` nor by calling //! [`frame_support::traits::ServiceQueues::service_queues`]. //! -//! Manual intervention in the form of -//! [`frame_support::traits::ServiceQueues::execute_overweight`] is necessary. Overweight messages -//! emit an [`Event::OverweightEnqueued`] event which can be used to extract the arguments for -//! manual execution. This only works on permanently overweight messages. There is no guarantee that -//! this will work since the message could be part of a stale page and be reaped before execution -//! commences. +//! Manual intervention in the form of [`frame_support::traits::ServiceQueues::execute_overweight`] +//! is necessary. Overweight messages emit an [`Event::OverweightEnqueued`] event which can be used +//! to extract the arguments for manual execution. This only works on permanently overweight +//! messages. There is no guarantee that this will work since the message could be part of a stale +//! page and be reaped before execution commences. //! //! # Terminology //! diff --git a/frame/src/lib.rs b/frame/src/lib.rs new file mode 100644 index 0000000000000..fd2cc28ddd170 --- /dev/null +++ b/frame/src/lib.rs @@ -0,0 +1,428 @@ +//! # FRAME +//! +//! > Substrate's State Transition Function (Runtime) Framework. +//! +//! ```co_compile +//! ______ ______ ________ ___ __ __ ______ +//! /_____/\ /_____/\ /_______/\ /__//_//_/\ /_____/\ +//! \::::_\/_\:::_ \ \ \::: _ \ \\::\| \| \ \\::::_\/_ +//! \:\/___/\\:(_) ) )_\::(_) \ \\:. \ \\:\/___/\ +//! \:::._\/ \: __ `\ \\:: __ \ \\:.\-/\ \ \\::___\/_ +//! \:\ \ \ \ `\ \ \\:.\ \ \ \\. \ \ \ \\:\____/\ +//! \_\/ \_\/ \_\/ \__\/\__\/ \__\/ \__\/ \_____\/ +//! ``` +//! +//! ## Warning: Experimental +//! +//! This crate and all of its content is experimental, and should not yet be used in production. +//! +//! ## Introduction +//! +//! Substrate is at a very high level composed of two parts: +//! +//! 1. A *runtime* which represents the state transition function (i.e. "Business Logic") of a +//! blockchain, and is encoded as a Wasm blob. +//! 2. A client whose primary purpose is to execute the given runtime. +//! +//! FRAME is the Substrate's framework of choice to build a runtime. +//! +//! FRAME is composed of two major components, **pallets** and a **runtime**. Pallets are isolated +//! pieces of logic that can be composed together to form a runtime. +//! +//! ## Pallets +//! +//! A pallet is analogous to a module in the runtime, which can itself be composed of multiple +//! components, most notable of which are: +//! +//! - Storage +//! - Dispatchables +//! - Events +//! - Errors +//! +//! Most of these components are defined using macros, the full list of which can be found in +//! [`frame_support::pallet_macros`] +//! +//! ## Runtime +//! +//! This crate also provides the ability to amalgamate multiple pallets into a single runtime. See +//! [`runtime`]. +//! +//! > To do so, the `runtime` feature of this crate needs to be enabled. +//! +//! ## Example +//! +//! This example showcases a very simple pallet that exposes a single dispatchable, and a minimal +//! runtime that contains it. +#![doc = docify::embed!("src/lib.rs", tests)] +//! +//! ## Underlying dependencies +//! +//! This crate is an amalgamation of multiple other crates that are often used together to compose a +//! pallet. It is not necessary to use it, and it may fall short for certain purposes. +//! +//! In short, this crate only re-exports types and traits from multiple sources. All of these +//! sources are listed (and re-exported again) in [`deps`]. + +#![cfg_attr(not(feature = "std"), no_std)] + +/// export the main pallet macro. This can wrap a `mod pallet` and will transform it into being +/// a pallet, eg `#[frame::pallet] mod pallet { .. }`. +/// +/// Note that this is not part of the prelude, in order to make it such that the common way to +/// define a macro is `#[frame::pallet] mod pallet { .. }`, followed by `#[pallet::foot]`, +/// `#[pallet::bar]` inside the mod. +pub use frame_support::pallet; + +/// The logging library of the runtime. Can be normally like the classic `log` crate. +pub use frame_support::log; + +/// The main prelude of this `FRAME`. +/// +/// This prelude should almost always be the first non-import line of code in any pallet or runtime. +/// +/// ``` +/// use frame::prelude::*; +/// +/// // rest of your pallet.. +/// mod pallet {} +/// ``` +pub mod prelude { + /// `frame_system`'s parent crate, which is mandatory in all pallets build with this crate. + /// + /// Conveniently, the keyword `frame_system` is in scope as one uses `use + /// frame::prelude::*` + #[doc(inline)] + pub use frame_system; + + /// Pallet prelude of `frame-support`. + /// + /// Note: this needs to revised once `frame-support` evolves. + #[doc(no_inline)] + pub use frame_support::pallet_prelude::*; + + /// Pallet prelude of `frame-system`. + #[doc(no_inline)] + pub use frame_system::pallet_prelude::*; + + /// All of the std alternative types. + #[doc(no_inline)] + pub use sp_std::prelude::*; + + /// All FRAME-relevant derive macros. + #[doc(no_inline)] + pub use super::derive::*; +} + +/// The main testing prelude of `FRAME`. +/// +/// A test setup typically starts with: +/// +/// ``` +/// use frame::{prelude::*, testing_prelude::*}; +/// // rest of your test setup. +/// ``` +#[cfg(feature = "std")] +pub mod testing_prelude { + /// Testing includes building a runtime, so we bring in all preludes related to runtimes as + /// well. + pub use super::runtime::{prelude::*, testing_prelude::*}; + + /// Other helper macros from `frame_support` that help with asserting in tests. + pub use frame_support::{ + assert_err, assert_err_ignore_postinfo, assert_error_encoded_size, assert_noop, assert_ok, + assert_storage_noop, storage_alias, + }; + + pub use frame_system::{self, mocking::*}; + pub use sp_io::TestExternalities as TestState; + pub use sp_std::if_std; +} + +/// All of the types and tools needed to build FRAME-based runtimes. +#[cfg(any(feature = "runtime", feature = "std"))] +pub mod runtime { + /// The main prelude of `FRAME` for building runtimes. + /// + /// A runtime typically starts with: + /// + /// ``` + /// use frame::{prelude::*, runtime::prelude::*}; + /// ``` + pub mod prelude { + /// All of the types related to the FRAME runtime executive. + pub use frame_executive::*; + + /// Macro to amalgamate the runtime into `struct Runtime`. + pub use frame_support::construct_runtime; + + /// Macro to easily derive the `Config` trait of various pallet for `Runtime`. + pub use frame_support::derive_impl; + + /// Macros to easily impl traits such as `Get` for types. + // TODO: using linking in the Get int he line above triggers an ICE :/ + pub use frame_support::{ord_parameter_types, parameter_types}; + + /// Const types that can easily be used in conjuncture with `Get`. + pub use frame_support::traits::{ + ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, + ConstU32, ConstU64, ConstU8, + }; + + /// Types to define your runtime version. + pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion}; + + // TODO: this is a temporary hack + pub use frame_support::{self}; + + #[cfg(feature = "std")] + pub use sp_version::NativeVersion; + } + + /// Types and traits for runtimes that implement runtime APIs. + /// + /// A testing runtime should not need this. + /// + /// A non-testing runtime should have this enabled, as such: + /// + /// ``` + /// use frame::runtime::{prelude::*, apis::{*,}}; + /// ``` + // TODO: This is because of wildcard imports, and it should be not needed once we can avoid + // that. Imports like that are needed because we seem to need some unknown types in the macro + // expansion. + #[allow(ambiguous_glob_reexports)] + pub mod apis { + // Types often used in the runtime APIs. + pub use frame_support::OpaqueMetadata; + pub use sp_inherents::{CheckInherentsResult, InherentData}; + pub use sp_runtime::ApplyExtrinsicResult; + + /// Macro to implement runtime APIs. + pub use sp_api::impl_runtime_apis; + + pub use frame_system_rpc_runtime_api::*; + pub use sp_api::{self, *}; + pub use sp_block_builder::*; + pub use sp_consensus_aura::*; + pub use sp_consensus_grandpa::*; + pub use sp_offchain::*; + pub use sp_session::api::*; + pub use sp_transaction_pool::runtime_api::*; + } + + /// A set of opinionated types aliases commonly used in runtimes. + /// + /// This is one set of opinionated types. They are compatible with one another, but are not + /// guaranteed to work if you start tweaking a portion. + /// + /// Some note-worthy opinions in this prelude: + /// + /// - `u32` block number. + /// - [`sp_runtime::MultiAddress`] and [`sp_runtime::MultiSignature`] are used as the account id + /// and signature types. This implies that this prelude can possibly used with an + /// "account-index" system (eg `pallet-indices`). And, in any case, it should be paired with + /// `AccountIdLookup` in [`frame_system::Config::Lookup`]. + pub mod types_common { + use frame_system::Config as SysConfig; + + /// A signature type compatible capably of handling multiple crypto-schemes. + pub type Signature = sp_runtime::MultiSignature; + + /// The corresponding account-id type of [`Signature`]. + pub type AccountId = < + ::Signer + as + sp_runtime::traits::IdentifyAccount + >::AccountId; + + /// The block-number type, which should be fed into [`frame_system::Config`]. + pub type BlockNumber = u32; + + type HeaderInner = sp_runtime::generic::Header::Hashing>; + + // NOTE: `AccountIndex` is provided for future compatibility, if you want to introduce + // something like `pallet-indices`. + type ExtrinsicInner = sp_runtime::generic::UncheckedExtrinsic< + sp_runtime::MultiAddress, + ::RuntimeCall, + Signature, + Extra, + >; + + /// The block type, which should be fed into [`frame_system::Config`]. + /// + /// Should be parameterized with `T: frame_system::Config` and a tuple of `SignedExtension`. + /// When in doubt, use [`SystemSignedExtensionsOf`]. + // Note that this cannot be dependent on `T` for block-number because it would lead to a + // circular dependency (self-referential generics). + pub type BlockOf = + sp_runtime::generic::Block, ExtrinsicInner>; + + /// The opaque block type. This is the same [`BlockOf`], but it has + /// [`sp_runtime::OpaqueExtrinsic`] as its final extrinsic type. This should be provided to + /// the client side as the extrinsic type. + pub type OpaqueBlockOf = + sp_runtime::generic::Block, sp_runtime::OpaqueExtrinsic>; + + /// Default set of signed extensions exposed from the `frame_system`. + /// + /// crucially, this does not contain any tx-payment extension. + pub type SystemSignedExtensionsOf = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + ); + } + + /// The main prelude of FRAME for building runtimes, and in the context of testing. + /// + /// counter part of `runtime::prelude`. + #[cfg(feature = "std")] + pub mod testing_prelude { + pub use sp_core::storage::Storage; + pub use sp_runtime::BuildStorage; + } +} + +/// All traits often used in FRAME pallets. +/// +/// Note that types implementing these traits can also be found in this module. +// TODO: `Hash` and `Bounded` are defined multiple times; should be fixed once these two crates are +// cleaned up. +#[allow(ambiguous_glob_reexports)] +pub mod traits { + pub use frame_support::traits::*; + pub use sp_runtime::traits::*; +} + +/// The arithmetic types used for safe math. +pub mod arithmetic { + pub use sp_arithmetic::{traits::*, *}; +} + +/// Low level primitive types used in FRAME pallets. +pub mod primitives { + pub use sp_core::{H160, H256, H512, U256, U512}; + pub use sp_runtime::traits::{BlakeTwo256, Hash, Keccak256}; +} + +/// All derive macros used in frame. +/// +/// This is already part of the [`prelude`]. +pub mod derive { + pub use frame_support::{ + CloneNoBound, DebugNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, + RuntimeDebugNoBound, + }; + pub use parity_scale_codec::{Decode, Encode}; + pub use scale_info::TypeInfo; + pub use sp_runtime::RuntimeDebug; + pub use sp_std::fmt::Debug; +} + +/// Access to all of the dependencies of this crate. In case the re-exports are not enough, this +/// module can be used. +/// +/// Any time one uses this module to access a dependency, you can have a moment to think about +/// whether this item could have been placed in any of the other modules and preludes in this crate. +/// In most cases, hopefully the answer is yes. +#[doc(hidden)] +pub mod deps { + // TODO: It would be great to somehow instruct RA to prefer *not* suggesting auto-imports from + // these. For example, we prefer `frame::derive::CloneNoBound` rather than + // `frame::deps::frame_support::CloneNoBound`. + pub use frame_support; + pub use frame_system; + + pub use sp_arithmetic; + pub use sp_core; + pub use sp_io; + pub use sp_runtime; + pub use sp_std; + + pub use parity_scale_codec as codec; + pub use scale_info; + + #[cfg(feature = "runtime")] + pub use frame_executive; + #[cfg(feature = "runtime")] + pub use sp_api; + #[cfg(feature = "runtime")] + pub use sp_block_builder; + #[cfg(feature = "runtime")] + pub use sp_consensus_aura; + #[cfg(feature = "runtime")] + pub use sp_consensus_grandpa; + #[cfg(feature = "runtime")] + pub use sp_inherents; + #[cfg(feature = "runtime")] + pub use sp_offchain; + #[cfg(feature = "runtime")] + pub use sp_version; +} + +#[docify::export] +#[cfg(test)] +mod tests { + use crate as frame; + use frame::{prelude::*, testing_prelude::*}; + + #[frame::pallet(dev_mode)] + pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: IsType<::RuntimeEvent> + + From>; + } + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::event] + pub enum Event {} + + #[pallet::storage] + pub type Value = StorageValue; + + #[pallet::call] + impl Pallet { + pub fn some_dispatchable(_origin: OriginFor) -> DispatchResult { + Ok(()) + } + } + } + + mod runtime { + use super::{frame, pallet as pallet_example}; + use frame::{prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + Example: pallet_example, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type BaseCallFilter = frame::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type OnSetCode = (); + type Block = MockBlock; + type BlockHashCount = (); + } + + impl pallet_example::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + } + } +} diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index f735469558c70..e4e2157adf3ea 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -126,12 +126,28 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - #[pallet::config] + pub mod config_preludes { + use super::*; + + pub struct TestDefaultConfig; + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + type WeightInfo = (); + } + + pub struct SolochainDefaultConfig; + #[frame_support::register_default_impl(SolochainDefaultConfig)] + impl DefaultConfig for SolochainDefaultConfig { + type WeightInfo = (); + } + } + #[pallet::config(with_default)] pub trait Config: frame_system::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// A sudo-able call. + #[pallet::no_default] type RuntimeCall: Parameter + UnfilteredDispatchable + GetDispatchInfo; diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index efc2244154479..61879722073ee 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -282,7 +282,7 @@ fn construct_runtime_implicit_to_explicit( expansion = quote::quote!( #frame_support::tt_call! { macro = [{ #pallet_path::tt_default_parts }] - frame_support = [{ #frame_support }] + your_tt_return = [{ #frame_support::tt_return }] ~~> #frame_support::match_and_insert! { target = [{ #expansion }] pattern = [{ #pallet_name: #pallet_path #pallet_instance }] @@ -318,7 +318,7 @@ fn construct_runtime_explicit_to_explicit_expanded( expansion = quote::quote!( #frame_support::tt_call! { macro = [{ #pallet_path::tt_extra_parts }] - frame_support = [{ #frame_support }] + your_tt_return = [{ #frame_support::tt_return }] ~~> #frame_support::match_and_insert! { target = [{ #expansion }] pattern = [{ #pallet_name: #pallet_path #pallet_instance }] @@ -403,17 +403,19 @@ fn construct_runtime_final_expansion( let integrity_test = decl_integrity_test(&scrate); let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - let warning = - where_section.map_or(None, |where_section| { - Some(proc_macro_warning::Warning::new_deprecated("WhereSection") - .old("use a `where` clause in `construct_runtime`") - .new("use `frame_system::Config` to set the `Block` type and delete this clause. - It is planned to be removed in December 2023") - .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) - .span(where_section.span) - .build(), + let warning = where_section.map_or(None, |where_section| { + Some( + proc_macro_warning::Warning::new_deprecated("WhereSection") + .old("use a `where` clause in `construct_runtime`") + .new( + "use `frame_system::Config` to set the `Block` type and delete this clause. + It is planned to be removed in December 2023", + ) + .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) + .span(where_section.span) + .build(), ) - }); + }); let res = quote!( #warning @@ -783,7 +785,7 @@ fn decl_static_assertions( quote! { #scrate::tt_call! { macro = [{ #path::tt_error_token }] - frame_support = [{ #scrate }] + your_tt_return = [{ #scrate::tt_return }] ~~> #scrate::assert_error_encoded_size! { path = [{ #path }] runtime = [{ #runtime }] diff --git a/frame/support/procedural/src/pallet/expand/error.rs b/frame/support/procedural/src/pallet/expand/error.rs index 376a6a9f51c6d..d071112bd1371 100644 --- a/frame/support/procedural/src/pallet/expand/error.rs +++ b/frame/support/procedural/src/pallet/expand/error.rs @@ -42,9 +42,9 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { macro_rules! #error_token_unique_id { { $caller:tt - frame_support = [{ $($frame_support:ident)::* }] + your_tt_return = [{ $my_tt_return:path }] } => { - $($frame_support::)*tt_return! { + $my_tt_return! { $caller } }; @@ -170,9 +170,9 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { macro_rules! #error_token_unique_id { { $caller:tt - frame_support = [{ $($frame_support:ident)::* }] + your_tt_return = [{ $my_tt_return:path }] } => { - $($frame_support::)*tt_return! { + $my_tt_return! { $caller error = [{ #error_ident }] } diff --git a/frame/support/procedural/src/pallet/expand/genesis_config.rs b/frame/support/procedural/src/pallet/expand/genesis_config.rs index cbe47bd8505f6..ca1c89b1a16f9 100644 --- a/frame/support/procedural/src/pallet/expand/genesis_config.rs +++ b/frame/support/procedural/src/pallet/expand/genesis_config.rs @@ -17,6 +17,7 @@ use crate::{pallet::Def, COUNTER}; use frame_support_procedural_tools::get_doc_literals; +use quote::ToTokens; use syn::{spanned::Spanned, Ident}; /// @@ -79,7 +80,7 @@ pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { let genesis_config_item = &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; - let serde_crate = format!("{}::serde", frame_support); + let serde_crate = format!("{}::serde", frame_support.to_token_stream()); match genesis_config_item { syn::Item::Enum(syn::ItemEnum { attrs, .. }) | diff --git a/frame/support/procedural/src/pallet/expand/tt_default_parts.rs b/frame/support/procedural/src/pallet/expand/tt_default_parts.rs index 356bdbf67e923..7621a56b04133 100644 --- a/frame/support/procedural/src/pallet/expand/tt_default_parts.rs +++ b/frame/support/procedural/src/pallet/expand/tt_default_parts.rs @@ -94,9 +94,9 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { macro_rules! #default_parts_unique_id { { $caller:tt - frame_support = [{ $($frame_support:ident)::* }] + your_tt_return = [{ $my_tt_return:path }] } => { - $($frame_support)*::tt_return! { + $my_tt_return! { $caller tokens = [{ expanded::{ @@ -124,9 +124,9 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { macro_rules! #extra_parts_unique_id { { $caller:tt - frame_support = [{ $($frame_support:ident)::* }] + your_tt_return = [{ $my_tt_return:path }] } => { - $($frame_support)*::tt_return! { + $my_tt_return! { $caller tokens = [{ expanded::{ diff --git a/frame/support/procedural/src/pallet/parse/composite.rs b/frame/support/procedural/src/pallet/parse/composite.rs index 2bbfcd2e998ab..882b174154c88 100644 --- a/frame/support/procedural/src/pallet/parse/composite.rs +++ b/frame/support/procedural/src/pallet/parse/composite.rs @@ -91,7 +91,7 @@ impl CompositeDef { pub fn try_from( attr_span: proc_macro2::Span, index: usize, - scrate: &proc_macro2::Ident, + scrate: &syn::Path, item: &mut syn::Item, ) -> syn::Result { let item = if let syn::Item::Enum(item) = item { diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 6a3693a3ab0f2..8e28aef549932 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -16,7 +16,7 @@ // limitations under the License. use super::helper; -use frame_support_procedural_tools::get_doc_literals; +use frame_support_procedural_tools::{get_doc_literals, is_using_frame_crate}; use quote::ToTokens; use syn::{spanned::Spanned, token, Token}; @@ -153,24 +153,8 @@ pub struct PalletAttr { typ: PalletAttrType, } -pub struct ConfigBoundParse(syn::Ident); - -impl syn::parse::Parse for ConfigBoundParse { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let ident = input.parse::()?; - input.parse::()?; - input.parse::()?; - - if input.peek(syn::token::Lt) { - input.parse::()?; - } - - Ok(Self(ident)) - } -} - -/// Parse for `IsType<::RuntimeEvent>` and retrieve `$ident` -pub struct IsTypeBoundEventParse(syn::Ident); +/// Parse for `IsType<::RuntimeEvent>` and retrieve `$path` +pub struct IsTypeBoundEventParse(syn::Path); impl syn::parse::Parse for IsTypeBoundEventParse { fn parse(input: syn::parse::ParseStream) -> syn::Result { @@ -179,15 +163,13 @@ impl syn::parse::Parse for IsTypeBoundEventParse { input.parse::()?; input.parse::()?; input.parse::()?; - let ident = input.parse::()?; - input.parse::()?; - input.parse::()?; + let config_path = input.parse::()?; input.parse::]>()?; input.parse::()?; input.parse::()?; input.parse::]>()?; - Ok(Self(ident)) + Ok(Self(config_path)) } } @@ -225,7 +207,7 @@ impl syn::parse::Parse for FromEventParse { /// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected. /// (Event type is reserved type) fn check_event_type( - frame_system: &syn::Ident, + frame_system: &syn::Path, trait_item: &syn::TraitItem, trait_has_instance: bool, ) -> syn::Result { @@ -240,15 +222,28 @@ fn check_event_type( // Check bound contains IsType and From let has_is_type_bound = type_.bounds.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| b.0 == *frame_system) + syn::parse2::(s.to_token_stream()).map_or(false, |b| { + let mut expected_system_config = if is_using_frame_crate(&frame_system) { + // in this case, we know that the only valid frame_system path is one that + // is `frame_system`, as `frame` re-exports it as such. + let fixed_frame_system = + syn::parse2::(quote::quote!(frame_system)) + .expect("is a valid path; qed"); + fixed_frame_system + } else { + frame_system.clone() + }; + expected_system_config + .segments + .push(syn::PathSegment::from(syn::Ident::new("Config", b.0.span()))); + b.0 == expected_system_config + }) }); if !has_is_type_bound { let msg = format!( "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ - bound: `IsType<::RuntimeEvent>`", - frame_system, + bound: `IsType<::RuntimeEvent>`", ); return Err(syn::Error::new(type_.span(), msg)) } @@ -299,7 +294,7 @@ pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenS impl ConfigDef { pub fn try_from( - frame_system: &syn::Ident, + frame_system: &syn::Path, attr_span: proc_macro2::Span, index: usize, item: &mut syn::Item, @@ -397,8 +392,32 @@ impl ConfigDef { let disable_system_supertrait_check = attr.is_some(); let has_frame_system_supertrait = item.supertraits.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| b.0 == *frame_system) + syn::parse2::(s.to_token_stream()).map_or(false, |b| { + let mut expected_system_config = if is_using_frame_crate(&frame_system) { + // in this case, we know that the only valid supertrait is one that + // `frame_system::Config`, as `frame` re-exports it as such. + let fixed_frame_system = syn::parse2::(quote::quote!(frame_system)) + .expect("is a valid path; qed"); + fixed_frame_system + } else { + // a possibly renamed frame-system, which is a direct dependency. + frame_system.clone() + }; + expected_system_config + .segments + .push(syn::PathSegment::from(syn::Ident::new("Config", b.span()))); + // the parse path (b.0) might be something like `frame_system::Config<...>`, so we + // only compare the idents along the path. + expected_system_config + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() == b + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() + }) }); if !has_frame_system_supertrait && !disable_system_supertrait_check { @@ -420,7 +439,8 @@ impl ConfigDef { (try `pub trait Config: frame_system::Config {{ ...` or \ `pub trait Config: frame_system::Config {{ ...`). \ To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`", - frame_system, found, + frame_system.to_token_stream(), + found, ); return Err(syn::Error::new(item.span(), msg)) } diff --git a/frame/support/procedural/src/pallet/parse/mod.rs b/frame/support/procedural/src/pallet/parse/mod.rs index 0f5e5f1136610..41da051f49b90 100644 --- a/frame/support/procedural/src/pallet/parse/mod.rs +++ b/frame/support/procedural/src/pallet/parse/mod.rs @@ -60,8 +60,8 @@ pub struct Def { pub extra_constants: Option, pub composites: Vec, pub type_values: Vec, - pub frame_system: syn::Ident, - pub frame_support: syn::Ident, + pub frame_system: syn::Path, + pub frame_support: syn::Path, pub dev_mode: bool, } @@ -98,7 +98,6 @@ impl Def { for (index, item) in items.iter_mut().enumerate() { let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; - match pallet_attr { Some(PalletAttr::Config(span, with_default)) if config.is_none() => config = Some(config::ConfigDef::try_from( diff --git a/frame/support/procedural/src/pallet_error.rs b/frame/support/procedural/src/pallet_error.rs index 246a5bd4a219a..051ad5bcf1cca 100644 --- a/frame/support/procedural/src/pallet_error.rs +++ b/frame/support/procedural/src/pallet_error.rs @@ -111,7 +111,7 @@ pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenS fn generate_field_types( field: &syn::Field, - scrate: &syn::Ident, + scrate: &syn::Path, ) -> syn::Result> { let attrs = &field.attrs; @@ -143,7 +143,7 @@ fn generate_field_types( fn generate_variant_field_types( variant: &syn::Variant, - scrate: &syn::Ident, + scrate: &syn::Path, ) -> syn::Result>> { let attrs = &variant.attrs; diff --git a/frame/support/procedural/src/storage_alias.rs b/frame/support/procedural/src/storage_alias.rs index b44a7ee997fe2..66c40e46a1a3a 100644 --- a/frame/support/procedural/src/storage_alias.rs +++ b/frame/support/procedural/src/storage_alias.rs @@ -224,7 +224,7 @@ impl StorageType { /// Generate the actual type declaration. fn generate_type_declaration( &self, - crate_: &Ident, + crate_: &syn::Path, storage_instance: &StorageInstance, storage_name: &Ident, storage_generics: Option<&SimpleGenerics>, @@ -550,7 +550,7 @@ struct StorageInstance { /// Generate the [`StorageInstance`] for the storage alias. fn generate_storage_instance( - crate_: &Ident, + crate_: &syn::Path, storage_name: &Ident, storage_generics: Option<&SimpleGenerics>, storage_where_clause: Option<&WhereClause>, diff --git a/frame/support/procedural/src/tt_macro.rs b/frame/support/procedural/src/tt_macro.rs index 69b5eb3d5521a..399d6dee649dc 100644 --- a/frame/support/procedural/src/tt_macro.rs +++ b/frame/support/procedural/src/tt_macro.rs @@ -18,7 +18,6 @@ //! Implementation of the `create_tt_return_macro` macro use crate::COUNTER; -use frame_support_procedural_tools::generate_crate_access_2018; use proc_macro2::{Ident, TokenStream}; use quote::format_ident; @@ -65,9 +64,9 @@ impl syn::parse::Parse for CreateTtReturnMacroDef { /// macro_rules! my_tt_macro { /// { /// $caller:tt -/// $(frame_support = [{ $($frame_support:ident)::* }])? +/// $(your_tt_return = [{ $my_tt_return:path }])? /// } => { -/// frame_support::tt_return! { +/// $my_tt_return! { /// $caller /// foo = [{ bar }] /// } @@ -77,11 +76,6 @@ impl syn::parse::Parse for CreateTtReturnMacroDef { pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let CreateTtReturnMacroDef { name, args } = syn::parse_macro_input!(input as CreateTtReturnMacroDef); - - let frame_support = match generate_crate_access_2018("frame-support") { - Ok(i) => i, - Err(e) => return e.into_compile_error().into(), - }; let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); let count = COUNTER.with(|counter| counter.borrow_mut().inc()); let unique_name = format_ident!("{}_{}", name, count); @@ -92,9 +86,9 @@ pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::Tok macro_rules! #unique_name { { $caller:tt - $(frame_support = [{ $($frame_support:ident)::* }])? + $(your_tt_return = [{ $my_tt_macro:path }])? } => { - #frame_support::tt_return! { + $my_tt_return! { $caller #( #keys = [{ #values }] diff --git a/frame/support/procedural/tools/src/lib.rs b/frame/support/procedural/tools/src/lib.rs index 541accc8ab96a..dfd3aa01eee70 100644 --- a/frame/support/procedural/tools/src/lib.rs +++ b/frame/support/procedural/tools/src/lib.rs @@ -46,24 +46,50 @@ pub fn generate_crate_access(unique_id: &str, def_crate: &str) -> TokenStream { } } +/// Check if the output of [`generate_crate_access_2018`] (or generally another path) is using the +/// `frame` crate or not. +pub fn is_using_frame_crate(path: &syn::Path) -> bool { + path.segments.first().map(|s| s.ident == "frame").unwrap_or(false) +} + /// Generate the crate access for the crate using 2018 syntax. /// -/// for `frame-support` output will for example be `frame_support`. -pub fn generate_crate_access_2018(def_crate: &str) -> Result { - match crate_name(def_crate) { +/// If `frame` is in scope, it will use `frame::deps::`. Else, it will try and find +/// `` directly. +pub fn generate_crate_access_2018(def_crate: &str) -> Result { + if let Ok(FoundCrate::Name(name)) = crate_name(&"frame") { + let path = format!("{}::deps::{}", name, def_crate.to_string().replace("-", "_")); + let path = syn::parse_str::(&path)?; + return Ok(path) + } + + let ident = match crate_name(def_crate) { Ok(FoundCrate::Itself) => { let name = def_crate.to_string().replace("-", "_"); Ok(syn::Ident::new(&name, Span::call_site())) }, Ok(FoundCrate::Name(name)) => Ok(Ident::new(&name, Span::call_site())), Err(e) => Err(Error::new(Span::call_site(), e)), - } + }?; + + Ok(syn::Path::from(ident)) } /// Generates the hidden includes that are required to make the macro independent from its scope. pub fn generate_hidden_includes(unique_id: &str, def_crate: &str) -> TokenStream { let mod_name = generate_hidden_includes_mod_name(unique_id); + if let Ok(FoundCrate::Name(name)) = crate_name(&"frame") { + let path = format!("{}::deps::{}", name, def_crate.to_string().replace("-", "_")); + let path = syn::parse_str::(&path).expect("is a valid path; qed"); + return quote::quote!( + #[doc(hidden)] + mod #mod_name { + pub use #path as hidden_include; + } + ) + } + match crate_name(def_crate) { Ok(FoundCrate::Itself) => quote!(), Ok(FoundCrate::Name(name)) => { diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 438d4151e3252..cec3e421f9dad 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1540,6 +1540,7 @@ pub mod testing_prelude { /// Prelude to be used alongside pallet macro, for ease of use. pub mod pallet_prelude { pub use crate::{ + defensive, defensive_assert, dispatch::{ DispatchClass, DispatchError, DispatchResult, DispatchResultWithPostInfo, Parameter, Pays, @@ -1548,11 +1549,14 @@ pub mod pallet_prelude { inherent::{InherentData, InherentIdentifier, ProvideInherent}, storage, storage::{ + bounded_btree_map::BoundedBTreeMap, + bounded_btree_set::BoundedBTreeSet, bounded_vec::BoundedVec, types::{ CountedStorageMap, Key as NMapKey, OptionQuery, ResultQuery, StorageDoubleMap, StorageMap, StorageNMap, StorageValue, ValueQuery, }, + weak_bounded_vec::WeakBoundedVec, StorageList, }, traits::{ diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 56006269ab1df..acd21430072c5 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -233,6 +233,40 @@ pub mod pallet { type BlockLength = (); type DbWeight = (); } + + /// Default configurations of this pallet in a solo-chain environment. + /// + /// ## Considerations: + /// + /// By default, this type makes the following choices: + /// + /// * Use a normal 32 byte account id, with a [`DefaultConfig::Lookup`] that implies no + /// 'account-indexing' pallet is being used. + /// * Given that we don't know anything about the existence of a currency system in scope, + /// an [`DefaultConfig::AccountData`] is chosen that has no addition data. overwrite this + /// if you use `pallet-balances` or similar. + /// * Make sure to overwrite [`DefaultConfig::Version`]. + /// * 2s block time, and a default 5mb block size is used. + pub struct SolochainDefaultConfig; + + #[frame_support::register_default_impl(SolochainDefaultConfig)] + impl DefaultConfig for SolochainDefaultConfig { + type Nonce = u32; + type Hash = sp_core::hash::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = sp_runtime::AccountId32; + type Lookup = sp_runtime::traits::AccountIdLookup; + type MaxConsumers = frame_support::traits::ConstU32<128>; + type AccountData = crate::AccountInfo; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type Version = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + } } /// System configuration trait. Implemented by runtime. @@ -1796,4 +1830,11 @@ pub mod pallet_prelude { /// Type alias for the `BlockNumber` associated type of system config. pub type BlockNumberFor = as sp_runtime::traits::Header>::Number; + + /// Type alias for the `Extrinsic` associated type of system config. + pub type ExtrinsicFor = + <::Block as sp_runtime::traits::Block>::Extrinsic; + + /// Type alias for the `RuntimeCall` associated type of system config. + pub type RuntimeCallFor = ::RuntimeCall; } diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index 4eb95941d7828..143b8fb9872cd 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -123,10 +123,27 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + pub mod config_preludes { + use super::*; + + pub struct TestDefaultConfig; + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + type WeightInfo = (); + } + + pub struct SolochainDefaultConfig; + #[frame_support::register_default_impl(SolochainDefaultConfig)] + impl DefaultConfig for SolochainDefaultConfig { + type WeightInfo = (); + } + } + /// The pallet configuration trait - #[pallet::config] + #[pallet::config(with_default)] pub trait Config: frame_system::Config { /// Type used for expressing timestamp. + #[pallet::no_default] type Moment: Parameter + Default + AtLeast32Bit @@ -137,6 +154,7 @@ pub mod pallet { /// Something which can be notified when the timestamp is set. Set this to `()` if not /// needed. + #[pallet::no_default] type OnTimestampSet: OnTimestampSet; /// The minimum period between blocks. Beware that this is different to the *expected* @@ -144,6 +162,7 @@ pub mod pallet { /// generally work with this to determine a sensible block time. e.g. For Aura, it will be /// double this period on default settings. #[pallet::constant] + #[pallet::no_default] type MinimumPeriod: Get; /// Weight information for extrinsics in this pallet. diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 0cc7bdad46e0e..d91e239f888a4 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -319,7 +319,25 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); - #[pallet::config] + pub mod config_preludes { + use super::*; + + pub struct SolochainDefaultConfig; + #[frame_support::register_default_impl(SolochainDefaultConfig)] + impl DefaultConfig for SolochainDefaultConfig { + type FeeMultiplierUpdate = (); + type OperationalFeeMultiplier = (); + } + + pub struct TestDefaultConfig; + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + type FeeMultiplierUpdate = (); + type OperationalFeeMultiplier = (); + } + } + + #[pallet::config(with_default)] pub trait Config: frame_system::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -330,12 +348,24 @@ pub mod pallet { /// adjusted, depending on the used resources by the transaction. If the /// transaction weight is lower than expected, parts of the transaction fee /// might be refunded. In the end the fees can be deposited. + #[pallet::no_default] type OnChargeTransaction: OnChargeTransaction; - /// A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their + /// Convert a weight value into a deductible fee based on the currency type. + #[pallet::no_default] + type WeightToFee: WeightToFee>; + + /// Convert a length value into a deductible fee based on the currency type. + #[pallet::no_default] + type LengthToFee: WeightToFee>; + + /// Update the multiplier of the next block, based on the previous block's weight. + type FeeMultiplierUpdate: MultiplierUpdate; + + /// A fee multiplier for `Operational` extrinsics to compute "virtual tip" to boost their /// `priority` /// - /// This value is multipled by the `final_fee` to obtain a "virtual tip" that is later + /// This value is multiplied by the `final_fee` to obtain a "virtual tip" that is later /// added to a tip component in regular `priority` calculations. /// It means that a `Normal` transaction can front-run a similarly-sized `Operational` /// extrinsic (with no tip), by including a tip value greater than the virtual tip. @@ -355,15 +385,6 @@ pub mod pallet { /// transactions. #[pallet::constant] type OperationalFeeMultiplier: Get; - - /// Convert a weight value into a deductible fee based on the currency type. - type WeightToFee: WeightToFee>; - - /// Convert a length value into a deductible fee based on the currency type. - type LengthToFee: WeightToFee>; - - /// Update the multiplier of the next block, based on the previous block's weight. - type FeeMultiplierUpdate: MultiplierUpdate; } #[pallet::type_value] diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index c9389154bbf40..2e870cd466459 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -15,23 +15,26 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::common::API_VERSION_ATTRIBUTE; +use inflector::Inflector; use proc_macro2::{Span, TokenStream}; - +use proc_macro_crate::{crate_name, FoundCrate}; +use quote::{format_ident, quote, ToTokens}; use syn::{ parse_quote, spanned::Spanned, token::And, Attribute, Error, FnArg, GenericArgument, Ident, ImplItem, ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath, }; -use quote::{format_ident, quote}; - -use proc_macro_crate::{crate_name, FoundCrate}; - -use crate::common::API_VERSION_ATTRIBUTE; - -use inflector::Inflector; - /// Generates the access to the `sc_client` crate. pub fn generate_crate_access() -> TokenStream { + if let Ok(FoundCrate::Name(name)) = crate_name(&"frame") { + // TODO: add tes to make sure `frame` always contains `frame::deps::sp_api`. + // TODO: handle error. + let path = format!("{}::deps::{}", name, "sp_api"); + let path = syn::parse_str::(&path).unwrap(); + return path.into_token_stream() + } + match crate_name("sp-api") { Ok(FoundCrate::Itself) => quote!(sp_api), Ok(FoundCrate::Name(renamed_name)) => { @@ -261,8 +264,6 @@ pub fn versioned_trait_name(trait_ident: &Ident, version: u64) -> Ident { /// Extract the documentation from the provided attributes. #[cfg(feature = "frame-metadata")] pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec { - use quote::ToTokens; - attrs .iter() .filter_map(|attr| { diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 72f996b336832..3864072f1bfa0 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -937,8 +937,7 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// - the path may be followed by `///`, in which case everything after the `///` is treated /// as a password. /// - If `s` begins with a `/` character it is prefixed with the Substrate public `DEV_PHRASE` - /// and - /// interpreted as above. + /// and interpreted as above. /// /// In this case they are interpreted as HDKD junctions; purely numeric items are interpreted as /// integers, non-numeric items as strings. Junctions prefixed with `/` are interpreted as soft diff --git a/primitives/keyring/src/ed25519.rs b/primitives/keyring/src/ed25519.rs index c3ad86409e905..3060bfb1ad987 100644 --- a/primitives/keyring/src/ed25519.rs +++ b/primitives/keyring/src/ed25519.rs @@ -35,6 +35,12 @@ pub enum Keyring { Dave, Eve, Ferdie, + AliceStash, + BobStash, + CharlieStash, + DaveStash, + EveStash, + FerdieStash, One, Two, } @@ -104,6 +110,12 @@ impl From for &'static str { Keyring::Dave => "Dave", Keyring::Eve => "Eve", Keyring::Ferdie => "Ferdie", + Keyring::AliceStash => "Alice//stash", + Keyring::BobStash => "Bob//stash", + Keyring::CharlieStash => "Charlie//stash", + Keyring::DaveStash => "Dave//stash", + Keyring::EveStash => "Eve//stash", + Keyring::FerdieStash => "Ferdie//stash", Keyring::One => "One", Keyring::Two => "Two", } diff --git a/primitives/keyring/src/lib.rs b/primitives/keyring/src/lib.rs index 7432aff12544a..2546aaa2c0341 100644 --- a/primitives/keyring/src/lib.rs +++ b/primitives/keyring/src/lib.rs @@ -23,9 +23,8 @@ pub mod sr25519; /// Test account crypto for ed25519. pub mod ed25519; -/// Convenience export: Sr25519's Keyring is exposed as `AccountKeyring`, -/// since it tends to be used for accounts (although it may also be used -/// by authorities). +/// Convenience export: Sr25519's Keyring is exposed as `AccountKeyring`, since it tends to be +/// used for accounts (although it may also be used by authorities). pub use sr25519::Keyring as AccountKeyring; pub use ed25519::Keyring as Ed25519Keyring; diff --git a/primitives/keyring/src/sr25519.rs b/primitives/keyring/src/sr25519.rs index c738cfdc59d9e..914a66b4d837c 100644 --- a/primitives/keyring/src/sr25519.rs +++ b/primitives/keyring/src/sr25519.rs @@ -35,6 +35,12 @@ pub enum Keyring { Dave, Eve, Ferdie, + AliceStash, + BobStash, + CharlieStash, + DaveStash, + EveStash, + FerdieStash, One, Two, } @@ -114,6 +120,12 @@ impl From for &'static str { Keyring::Dave => "Dave", Keyring::Eve => "Eve", Keyring::Ferdie => "Ferdie", + Keyring::AliceStash => "Alice//stash", + Keyring::BobStash => "Bob//stash", + Keyring::CharlieStash => "Charlie//stash", + Keyring::DaveStash => "Dave//stash", + Keyring::EveStash => "Eve//stash", + Keyring::FerdieStash => "Ferdie//stash", Keyring::One => "One", Keyring::Two => "Two", } diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index 0b1cd2b54290e..1cdc0b8e4051b 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -134,16 +134,16 @@ impl Checkable - for UncheckedExtrinsic +impl Checkable + for UncheckedExtrinsic where - Address: Member + MaybeDisplay, + LookupSource: Member + MaybeDisplay, Call: Encode + Member, Signature: Member + traits::Verify, ::Signer: IdentifyAccount, Extra: SignedExtension, AccountId: Member + MaybeDisplay, - Lookup: traits::Lookup, + Lookup: traits::Lookup, { type Checked = CheckedExtrinsic; diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index dd861ad05de9b..0e1d4c31fd712 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -466,7 +466,7 @@ impl Verify for MultiSignature { } /// Signature verify that can work with any known signature types.. -#[derive(Eq, PartialEq, Clone, Default, Encode, Decode, RuntimeDebug)] +#[derive(Eq, PartialEq, Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct AnySignature(H512); diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 51d91958ab533..e18089c3724a6 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -226,7 +226,7 @@ pub trait StaticLookup { } /// A lookup implementation returning the input value. -#[derive(Default)] +#[derive(Default, Clone, Copy, PartialEq, Eq)] pub struct IdentityLookup(PhantomData); impl StaticLookup for IdentityLookup { type Source = T; @@ -1666,8 +1666,6 @@ impl SignedExtension for Tuple { } } -/// Only for bare bone testing when you don't care about signed extensions at all. -#[cfg(feature = "std")] impl SignedExtension for () { type AccountId = u64; type AdditionalSigned = (); diff --git a/primitives/session/src/api.rs b/primitives/session/src/api.rs new file mode 100644 index 0000000000000..f57b04c93c9ff --- /dev/null +++ b/primitives/session/src/api.rs @@ -0,0 +1,21 @@ +pub use sp_core::crypto::KeyTypeId; +use sp_std::prelude::*; + +sp_api::decl_runtime_apis! { + /// Session keys runtime api. + pub trait SessionKeys { + /// Generate a set of session keys with optionally using the given seed. + /// The keys should be stored within the keystore exposed via runtime + /// externalities. + /// + /// The seed needs to be a valid `utf8` string. + /// + /// Returns the concatenated SCALE encoded public keys. + fn generate_session_keys(seed: Option>) -> Vec; + + /// Decode the given public session keys. + /// + /// Returns the list of public raw public keys + key type. + fn decode_session_keys(encoded: Vec) -> Option, sp_core::crypto::KeyTypeId)>>; + } +} diff --git a/primitives/session/src/lib.rs b/primitives/session/src/lib.rs index 45395e9766f55..46b0d81912414 100644 --- a/primitives/session/src/lib.rs +++ b/primitives/session/src/lib.rs @@ -26,28 +26,12 @@ use sp_api::ProvideRuntimeApi; #[cfg(feature = "std")] use sp_runtime::traits::Block as BlockT; -use sp_core::{crypto::KeyTypeId, RuntimeDebug}; +use sp_core::RuntimeDebug; use sp_staking::SessionIndex; use sp_std::vec::Vec; -sp_api::decl_runtime_apis! { - /// Session keys runtime api. - pub trait SessionKeys { - /// Generate a set of session keys with optionally using the given seed. - /// The keys should be stored within the keystore exposed via runtime - /// externalities. - /// - /// The seed needs to be a valid `utf8` string. - /// - /// Returns the concatenated SCALE encoded public keys. - fn generate_session_keys(seed: Option>) -> Vec; - - /// Decode the given public session keys. - /// - /// Returns the list of public raw public keys + key type. - fn decode_session_keys(encoded: Vec) -> Option, KeyTypeId)>>; - } -} +pub mod api; +pub use api::*; /// Number of validators in a given session. pub type ValidatorCount = u32; diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 36cf864dd5389..1e740944c4e19 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -248,7 +248,22 @@ where } } +/// Implementor of `WeightToFee` such that it maps any unit of weight to a fixed fee. +pub struct FixedFee(sp_std::marker::PhantomData); + +impl WeightToFee for FixedFee +where + T: BaseArithmetic + From + Copy + Unsigned, +{ + type Balance = T; + + fn weight_to_fee(_: &Weight) -> Self::Balance { + F.into() + } +} + /// Implementor of [`WeightToFee`] that uses a constant multiplier. +/// /// # Example /// /// ```