diff --git a/kitchen/modules/weights/Cargo.toml b/kitchen/modules/weights/Cargo.toml new file mode 100644 index 000000000..e6aa0f70e --- /dev/null +++ b/kitchen/modules/weights/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "weights" +version = "0.1.0" +authors = ["Anonymous"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.6' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' diff --git a/kitchen/modules/weights/src/lib.rs b/kitchen/modules/weights/src/lib.rs new file mode 100644 index 000000000..6653fe93f --- /dev/null +++ b/kitchen/modules/weights/src/lib.rs @@ -0,0 +1,181 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// Transaction Weight Examples +// https://crates.parity.io/sr_primitives/weights/index.html +use support::{ensure, decl_module, decl_storage, dispatch::{Result, WeighData}}; +use runtime_primitives::weights::{ DispatchClass, Weight, ClassifyDispatch, SimpleDispatchInfo }; + +pub trait Trait: system::Trait {} + +decl_storage! { + trait Store for Module as SimpleMap { + StoredValue get(stored_value): u32; + } +} + +// A "scale" to weigh transactions. This scale can be used with any transactions that take a +// single argument of type u32. The ultimate weight of the transaction is the / product of the +// transaction parameter and the field of this struct. +pub struct Linear(u32); + +// The actual weight calculation happens in the +// impl WeighData block +impl WeighData<(&u32,)> for Linear { + fn weigh_data(&self, (x,): (&u32,)) -> Weight { + + // Use saturation so that an extremely large parameter value + // Does not cause overflow. + x.saturating_mul(self.0) + } +} + +// Any struct that is used to weigh data must also implement ClassifyDispatchInfo. Here we classify +// the transaction as Normal (as opposed to operational.) +impl ClassifyDispatch for Linear { + fn classify_dispatch(&self, _: T) -> DispatchClass { + // Classify all calls as Normal (which is the default) + Default::default() + } +} + +// Another scale to weight transactions. This one is more complex. / It computes weight according +// to the formula a*x^2 + b*y + c where / a, b, and c are fields in the struct, and x and y are +// transaction / parameters. +pub struct Quadratic(u32, u32, u32); + +impl WeighData<(&u32, &u32)> for Quadratic { + fn weigh_data(&self, (x, y): (&u32, &u32)) -> Weight { + + let ax2 = x.saturating_mul(*x).saturating_mul(self.0); + let by = y.saturating_mul(self.1); + let c = self.2; + + ax2.saturating_add(by).saturating_add(c) + } +} + +impl ClassifyDispatch for Quadratic { + fn classify_dispatch(&self, _: T) -> DispatchClass { + // Classify all calls as Normal (which is the default) + Default::default() + } +} + +// A final scale to weight transactions. This one weighs transactions where the first parameter +// is bool. If the bool is true, then the weight is linear in the second parameter. Otherwise +// the weight is constant. +pub struct Conditional(u32); + +impl WeighData<(&bool, &u32)> for Conditional { + fn weigh_data(&self, (switch, val): (&bool, &u32)) -> Weight { + + if *switch { + val.saturating_mul(self.0) + } + else { + self.0 + } + } +} + +impl ClassifyDispatch for Conditional { + fn classify_dispatch(&self, _: T) -> DispatchClass { + // Classify all calls as Normal (which is the default) + Default::default() + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + + // Store value does not loop at all so a fixed weight is appropriate. Fixed weights can + // be assigned using types available in the Substrate framework. No custom coding is + // necessary. + #[weight = SimpleDispatchInfo::FixedNormal(100)] + fn store_value(_origin, entry: u32) -> Result { + + StoredValue::put(entry); + + Ok(()) + } + + // WARNING: The functions that follow, allow the caller to control the + // amount of computation being performed. This is ONLY SAFE when using + // custom weighting structs as shown here. + + // add_n sets the storage value n times, so it should cost n times as much as + // store_value. Because it performs both a read and a write, the multiplier is set to 200 + // instead of 100 as before. + #[weight = Linear(200)] + fn add_n(_origin, n: u32) -> Result { + + let mut old : u32; + for _i in 1..=n { + old = StoredValue::get(); + StoredValue::put(old + 1); + } + Ok(()) + } + + // The actual expense of `double` is proportional to a storage value. Dispatch + // weightings can't use storage values directly, because the weight should be computable + // ahead of time. Instead we have the caller pass in the expected storage value and we + // ensure it is correct. + #[weight = Linear(200)] + fn double(_origin, initial_value: u32) -> Result { + + // Ensure the value passed by the caller actually matches storage If this condition + // were not true, the caller would be able to avoid paying appropriate fees. + let initial = StoredValue::get(); + ensure!(initial == initial_value, "Storage value did not match parameter"); + + for _i in 1..=initial { + let old = StoredValue::get(); + StoredValue::put(old + 1); + } + Ok(()) + } + + // This one is quadratic in the first argument plus linear in the second plus a constant. + // This calculation is not meant to do something really useful or common other than + // demonstrate that weights should grow by the same order as the compute required by the + // transaction. + #[weight = Quadratic(200, 30, 100)] + fn complex_calculations(_origin, x: u32, y: u32) -> Result { + // This first part performs a relatively cheap (hence 30) + // in-memory calculations. + let mut part1 = 0; + for _i in 1..=y { + part1 += 2 + } + + // The second part performs x^2 storage read-writes (hence 200) + for _j in 1..=x { + for _k in 1..=x { + StoredValue::put(StoredValue::get() + 1); + } + } + + // One final storage write (hence 100) + StoredValue::put(part1); + + Ok(()) + } + + // Here the first parameter, a boolean has a significant effect on the computational + // intensity of the call. + #[weight = Conditional(200)] + fn add_or_set(_origin, add_flag: bool, val: u32) -> Result { + if add_flag { + StoredValue::put(&val); + } + else { + for _i in 1..=val { + StoredValue::put(StoredValue::get()); + } + } + + Ok(()) + } + } +} diff --git a/kitchen/node/Cargo.toml b/kitchen/node/Cargo.toml index c3621da73..466f0cb4a 100644 --- a/kitchen/node/Cargo.toml +++ b/kitchen/node/Cargo.toml @@ -38,6 +38,8 @@ basic-authorship = { package = "substrate-basic-authorship", git = 'https://gith # Your runtime must have the necessary runtime modules to support consensus (Babe, Grandpa, etc) runtime = { package = "super-runtime", path = "../runtimes/super-runtime" } runtime-genesis = { package = "super-genesis", path = "../runtimes/super-genesis" } +# runtime = { package = "weight-fee-runtime", path = "../runtimes/weight-fee-runtime"} +# runtime-genesis = { package = "weight-fee-genesis", path = "../runtimes/weight-fee-genesis"} [build-dependencies] vergen = "3.0.4" diff --git a/kitchen/runtimes/super-genesis/Cargo.toml b/kitchen/runtimes/super-genesis/Cargo.toml index 00ef38998..7d7c16761 100644 --- a/kitchen/runtimes/super-genesis/Cargo.toml +++ b/kitchen/runtimes/super-genesis/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0" authors = ["Anonymous"] edition = "2018" -[dependencies.super-runtime] +[dependencies.runtime] package = 'super-runtime' path = '../super-runtime' diff --git a/kitchen/runtimes/super-genesis/src/lib.rs b/kitchen/runtimes/super-genesis/src/lib.rs index a490a8ad9..0089c1a9f 100644 --- a/kitchen/runtimes/super-genesis/src/lib.rs +++ b/kitchen/runtimes/super-genesis/src/lib.rs @@ -1,4 +1,4 @@ -use super_runtime::{ +use runtime::{ AccountId, BabeConfig, BalancesConfig, GenesisConfig, GrandpaConfig, SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, }; diff --git a/kitchen/runtimes/super-runtime/Cargo.toml b/kitchen/runtimes/super-runtime/Cargo.toml index b7b4e1f62..c91e36de2 100644 --- a/kitchen/runtimes/super-runtime/Cargo.toml +++ b/kitchen/runtimes/super-runtime/Cargo.toml @@ -26,7 +26,7 @@ schedule-on-finalize = { package = "schedule-on-finalize", path = "../../modules [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.6' # v1.0.0 indevhub/node-template +version = '1.0.6' [dependencies.rstd] default-features = false diff --git a/kitchen/runtimes/super-runtime/src/lib.rs b/kitchen/runtimes/super-runtime/src/lib.rs index 7caccd3a3..1b2fb8501 100644 --- a/kitchen/runtimes/super-runtime/src/lib.rs +++ b/kitchen/runtimes/super-runtime/src/lib.rs @@ -258,6 +258,20 @@ impl sudo::Trait for Runtime { type Proposal = Call; } +parameter_types! { + pub const TransactionBaseFee: u128 = 0; + pub const TransactionByteFee: u128 = 1; +} + +impl transaction_payment::Trait for Runtime { + type Currency = balances::Module; + type OnTransactionPayment = (); + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); +} + // ---------------------- Recipe Runtime Configurations ---------------------- impl simple_event::Trait for Runtime { type Event = Event; @@ -321,20 +335,6 @@ impl schedule_on_finalize::Trait for Runtime { type ExecutionFrequency = ClearFrequency; // for convenience (can use a different constant) } -parameter_types! { - pub const TransactionBaseFee: u128 = 0; - pub const TransactionByteFee: u128 = 1; -} - -impl transaction_payment::Trait for Runtime { - type Currency = balances::Module; - type OnTransactionPayment = (); - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = ConvertInto; - type FeeMultiplierUpdate = (); -} - construct_runtime!( pub enum Runtime where Block = Block, diff --git a/kitchen/runtimes/weight-fee-genesis/Cargo.toml b/kitchen/runtimes/weight-fee-genesis/Cargo.toml new file mode 100644 index 000000000..6e0d97140 --- /dev/null +++ b/kitchen/runtimes/weight-fee-genesis/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = 'weight-fee-genesis' +version = "2.0.0" +authors = ["Anonymous"] +edition = "2018" + +[dependencies.runtime] +package = 'weight-fee-runtime' +path = '../weight-fee-runtime' + +[dependencies] +babe-primitives = { package = "substrate-consensus-babe-primitives", git = "https://github.com/paritytech/substrate", branch = "master" } +grandpa-primitives = { package = "substrate-finality-grandpa-primitives", git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/kitchen/runtimes/weight-fee-genesis/src/lib.rs b/kitchen/runtimes/weight-fee-genesis/src/lib.rs new file mode 100644 index 000000000..0089c1a9f --- /dev/null +++ b/kitchen/runtimes/weight-fee-genesis/src/lib.rs @@ -0,0 +1,34 @@ +use runtime::{ + AccountId, BabeConfig, BalancesConfig, GenesisConfig, GrandpaConfig, + SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, +}; +use babe_primitives::{AuthorityId as BabeId}; +use grandpa_primitives::{AuthorityId as GrandpaId}; + +pub fn testnet_genesis(initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId)>, + root_key: AccountId, + endowed_accounts: Vec, + _enable_println: bool) -> GenesisConfig { + GenesisConfig { + system: Some(SystemConfig { + code: WASM_BINARY.to_vec(), + changes_trie_config: Default::default(), + }), + indices: Some(IndicesConfig { + ids: endowed_accounts.clone(), + }), + balances: Some(BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), + vesting: vec![], + }), + sudo: Some(SudoConfig { + key: root_key, + }), + babe: Some(BabeConfig { + authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), + }), + grandpa: Some(GrandpaConfig { + authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), + }), + } +} diff --git a/kitchen/runtimes/weight-fee-runtime/Cargo.toml b/kitchen/runtimes/weight-fee-runtime/Cargo.toml new file mode 100644 index 000000000..d9d3bb85d --- /dev/null +++ b/kitchen/runtimes/weight-fee-runtime/Cargo.toml @@ -0,0 +1,168 @@ +[package] +name = "weight-fee-runtime" +version = "2.0.0" +authors = ["Anonymous"] +edition = "2018" + +[dependencies] +serde = { version = "1.0", optional = true, features = ["derive"] } +safe-mix = { version = "1.0", default-features = false } +weights = { path = "../../modules/weights", default_features = false } + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.6' + +[dependencies.rstd] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +package = "sr-std" +branch = 'master' + +[dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' + +[dependencies.version] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-version' +branch = 'master' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dependencies.substrate-session] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-session' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.transaction-payment] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-transaction-payment' +branch = 'master' + +[dependencies.babe] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-babe' +branch = 'master' + +[dependencies.babe-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-consensus-babe-primitives' +branch = 'master' + +[dependencies.executive] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-executive' +branch = 'master' + +[dependencies.indices] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-indices' +branch = 'master' + +[dependencies.grandpa] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-grandpa' +branch = 'master' + +[dependencies.randomness-collective-flip] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-randomness-collective-flip' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.timestamp] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-timestamp' +branch = 'master' + +[dependencies.sudo] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-sudo' +branch = 'master' + +[dependencies.sr-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dependencies.client] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-client' +branch = 'master' + +[dependencies.offchain-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-offchain-primitives' +branch = 'master' + + +[build-dependencies] +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.3" } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "client/std", + "rstd/std", + "runtime-io/std", + "support/std", + "balances/std", + "babe/std", + "babe-primitives/std", + "executive/std", + "indices/std", + "grandpa/std", + "primitives/std", + "randomness-collective-flip/std", + "sr-primitives/std", + "system/std", + "timestamp/std", + "sudo/std", + "version/std", + "serde", + "safe-mix/std", + "offchain-primitives/std", + "substrate-session/std", + "transaction-payment/std", + "weights/std", +] diff --git a/kitchen/runtimes/weight-fee-runtime/build.rs b/kitchen/runtimes/weight-fee-runtime/build.rs new file mode 100644 index 000000000..ddbeefa11 --- /dev/null +++ b/kitchen/runtimes/weight-fee-runtime/build.rs @@ -0,0 +1,27 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; + +fn main() { + build_current_project_with_rustflags( + "wasm_binary.rs", + WasmBuilderSource::Crates("1.0.7"), + // This instructs LLD to export __heap_base as a global variable, which is used by the + // external memory allocator. + "-Clink-arg=--export=__heap_base", + ); +} diff --git a/kitchen/runtimes/weight-fee-runtime/src/lib.rs b/kitchen/runtimes/weight-fee-runtime/src/lib.rs new file mode 100644 index 000000000..4632007bf --- /dev/null +++ b/kitchen/runtimes/weight-fee-runtime/src/lib.rs @@ -0,0 +1,451 @@ +//! A Super Runtime. This runtime demonstrates all the recipes in the kitchen +//! in a single super runtime. + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit="256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use rstd::prelude::*; +use primitives::{OpaqueMetadata, crypto::key_types}; +use sr_primitives::{ + ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, + impl_opaque_keys, AnySignature +}; +use sr_primitives::traits::{NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto, Convert}; +use support::traits::Get; +use sr_primitives::weights::Weight; +use babe::{AuthorityId as BabeId, SameAuthoritiesForever}; +use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; +use grandpa::fg_primitives; +use client::{ + block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, + runtime_api as client_api, impl_runtime_apis +}; +use version::RuntimeVersion; +#[cfg(feature = "std")] +use version::NativeVersion; + +// A few exports that help ease life for downstream crates. +#[cfg(any(feature = "std", test))] +pub use sr_primitives::BuildStorage; +pub use timestamp::Call as TimestampCall; +pub use balances::Call as BalancesCall; +pub use sr_primitives::{Permill, Perbill}; +pub use support::{StorageValue, construct_runtime, parameter_types, traits::Randomness}; + +/// An index to a block. +pub type BlockNumber = u32; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = AnySignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = ::Signer; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + +/// Balance of an account. +pub type Balance = u128; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// A hash of some data used by the chain. +pub type Hash = primitives::H256; + +/// Digest item type. +pub type DigestItem = generic::DigestItem; + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core datastructures. +pub mod opaque { + use super::*; + + pub use sr_primitives::OpaqueExtrinsic as UncheckedExtrinsic; + + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; + + pub type SessionHandlers = (Grandpa, Babe); + + impl_opaque_keys! { + pub struct SessionKeys { + #[id(key_types::GRANDPA)] + pub grandpa: GrandpaId, + #[id(key_types::BABE)] + pub babe: BabeId, + } + } +} + +/// This runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("super-runtime"), + impl_name: create_runtime_str!("super-runtime"), + authoring_version: 3, + spec_version: 4, + impl_version: 4, + apis: RUNTIME_API_VERSIONS, +}; + +/// Constants for Babe. +pub const MILLISECS_PER_BLOCK: u64 = 6000; + +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; + +// These time units are defined in number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; + +// 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. +pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); + +/// The version infromation 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(), + } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const MaximumBlockWeight: Weight = 1_000_000; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); + pub const MaximumBlockLength: u32 = 5 * 1024 * 1024; + pub const Version: RuntimeVersion = VERSION; +} + +impl system::Trait for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type Call = Call; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = Indices; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type Event = Event; + /// The ubiquitous origin type. + type Origin = Origin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + type MaximumBlockWeight = MaximumBlockWeight; + /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. + type MaximumBlockLength = MaximumBlockLength; + /// Portion of the block weight that is available to all normal transactions. + type AvailableBlockRatio = AvailableBlockRatio; + type Version = Version; +} + +parameter_types! { + pub const EpochDuration: u64 = EPOCH_DURATION_IN_BLOCKS as u64; + pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; +} + +impl babe::Trait for Runtime { + type EpochDuration = EpochDuration; + type ExpectedBlockTime = ExpectedBlockTime; + type EpochChangeTrigger = SameAuthoritiesForever; +} + +impl grandpa::Trait for Runtime { + type Event = Event; +} + +impl indices::Trait for Runtime { + /// The type for recording indexing into the account enumeration. If this ever overflows, there + /// will be problems! + type AccountIndex = u32; + /// Use the standard means of resolving an index hint from an id. + type ResolveHint = indices::SimpleResolveHint; + /// Determine whether an account is dead. + type IsDeadAccount = Balances; + /// The ubiquitous event type. + type Event = Event; +} + +parameter_types! { + pub const MinimumPeriod: u64 = SLOT_DURATION / 2; +} + +impl timestamp::Trait for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = Babe; + type MinimumPeriod = MinimumPeriod; +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 500; + pub const TransferFee: u128 = 0; + pub const CreationFee: u128 = 0; +} + +impl balances::Trait for Runtime { + /// The type for recording an account's balance. + type Balance = Balance; + /// What to do if an account's free balance gets zeroed. + type OnFreeBalanceZero = (); + /// What to do if a new account is created. + type OnNewAccount = Indices; + /// The ubiquitous event type. + type Event = Event; + type DustRemoval = (); + type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; +} + +impl sudo::Trait for Runtime { + type Event = Event; + type Proposal = Call; +} + +impl weights::Trait for Runtime {} + + +// --------------------- Multiple Options for WeightToFee ----------------------- + +/// Convert from weight to balance via a simple coefficient multiplication. The associated type C +/// encapsulates a constant in units of balance per weight. +pub struct LinearWeightToFee(rstd::marker::PhantomData); + +impl Convert for LinearWeightToFee + where C: Get { + + fn convert(w: Weight) -> Balance { + // substrate-node a weight of 10_000 (smallest non-zero weight) to be mapped to 10^7 units of + // fees, hence: + let coefficient = C::get(); + Balance::from(w).saturating_mul(coefficient) + } +} + +/// Convert from weight to balance via a quadratic curve. The type parameters encapsulate the +/// coefficients. +pub struct QuadraticWeightToFee(C0, C1, C2); + +impl Convert for QuadraticWeightToFee + where C0: Get, C1: Get, C2: Get { + + fn convert(w: Weight) -> Balance { + let c0 = C0::get(); + let c1 = C1::get(); + let c2 = C2::get(); + let w = Balance::from(w); + + // All the safe math reduces to + // c0 + c1 * w + c2 * w * w + + let c1w = c1.saturating_mul(w); + let c2w2 = c2.saturating_mul(w).saturating_mul(w); + + c0.saturating_add(c1w).saturating_add(c2w2) + } +} + + + +// Enable only when WeightToFee = LinearWeightToFee +// parameter_types!{ +// pub const FeeWeightRatio: u128 = 1_000; +// } + +// Enable only when WeightToFee = QuadraticWeightToFee +parameter_types! { + pub const WeightFeeConstant: u128 = 1_000; + pub const WeightFeeLinear: u128 = 100; + pub const WeightFeeQuadratic : u128 = 10; +} + +// Always necessary +parameter_types! { + pub const TransactionBaseFee: u128 = 0; + pub const TransactionByteFee: u128 = 1; +} + +impl transaction_payment::Trait for Runtime { + type Currency = balances::Module; + type OnTransactionPayment = (); + + // Base fee is applied to every single transaction + type TransactionBaseFee = TransactionBaseFee; + + // Byte fee is multiplied by the length of the + // serialized transaction in bytes + type TransactionByteFee = TransactionByteFee; + + // Function to convert module's weight to a chargeable fee. + // Enable exactly one of the following examples + //type WeightToFee = ConvertInto; + //type WeightToFee = LinearWeightToFee; + type WeightToFee = QuadraticWeightToFee; + + //TODO Explore how to change FeeMultiplierUpdate + type FeeMultiplierUpdate = (); +} + +// -------------------------------------------- + + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system::{Module, Call, Storage, Config, Event}, + Timestamp: timestamp::{Module, Call, Storage, Inherent}, + Babe: babe::{Module, Call, Storage, Config, Inherent(Timestamp)}, + Grandpa: grandpa::{Module, Call, Storage, Config, Event}, + Indices: indices::{default, Config}, + Balances: balances::{default, Error}, + RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage}, + Sudo: sudo, + TransactionPayment: transaction_payment::{Module, Storage}, + // The Recipe Modules + Weights: weights::{Module, Call, Storage}, + } +); + +/// The address format for describing accounts. +pub type Address = ::Source; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + system::CheckVersion, + system::CheckGenesis, + system::CheckEra, + system::CheckNonce, + system::CheckWeight, + transaction_payment::ChargeTransactionPayment +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = executive::Executive, Runtime, AllModules>; + +impl_runtime_apis! { + impl client_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl client_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + Runtime::metadata().into() + } + } + + impl block_builder_api::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + + fn random_seed() -> ::Hash { + RandomnessCollectiveFlip::random_seed() + } + } + + impl client_api::TaggedTransactionQueue for Runtime { + fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity { + Executive::validate_transaction(tx) + } + } + + impl offchain_primitives::OffchainWorkerApi for Runtime { + fn offchain_worker(number: NumberFor) { + Executive::offchain_worker(number) + } + } + + impl fg_primitives::GrandpaApi for Runtime { + fn grandpa_authorities() -> Vec<(GrandpaId, GrandpaWeight)> { + Grandpa::grandpa_authorities() + } + } + + impl babe_primitives::BabeApi for Runtime { + fn configuration() -> babe_primitives::BabeConfiguration { + // The choice of `c` parameter (where `1 - c` represents the + // probability of a slot being empty), is done in accordance to the + // slot duration and expected target block time, for safely + // resisting network delays of maximum two seconds. + // + babe_primitives::BabeConfiguration { + slot_duration: Babe::slot_duration(), + epoch_length: EpochDuration::get(), + c: PRIMARY_PROBABILITY, + genesis_authorities: Babe::authorities(), + randomness: Babe::randomness(), + secondary_slots: true, + } + } + } + + impl substrate_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string")); + opaque::SessionKeys::generate(seed) + } + } +} diff --git a/src/design/econsecurity.md b/src/design/econsecurity.md index b34a82f5c..95d6e9083 100644 --- a/src/design/econsecurity.md +++ b/src/design/econsecurity.md @@ -1,13 +1,88 @@ -# Efficiency => Security in Substrate +# Economic Security in Substrate An algorithm is considered to be *efficient* if its running time is polynomial in the size of the input, and *highly efficient* if its running time is linear in the size of the input. **It is important for all on-chain algorithms to be highly efficient, because they must scale linearly as the size of the Polkadot network grows**. In contrast, off-chain algorithms are only required to be efficient. - [Web3 Research](http://research.web3.foundation/en/latest/polkadot/NPoS/1.intro/) -Moreover, any resources used by a transaction must explicitly be paid for within the module. If the resources used might be dependent on transaction parameters or pre-existing chain state, the in-module fee structure must adapt accordingly. Specifically, measuring the balance between **resources used** and **price paid** is an important design activity for runtime security. +Any resources used by a transaction must explicitly be paid for, and it is a module author's job to ensure that appropriate fees are required. Maintaining the balance between **resources used** and **price paid** is an important design activity for runtime security. *Indeed, mispriced EVM operations have shown how operations that underestimate cost can open economic DOS attack vectors: [Onwards; Underpriced EVM Operations](https://www.parity.io/onwards/), [Under-Priced DOS Attacks on Ethereum](https://www4.comp.polyu.edu.hk/~csxluo/DoSEVM.pdf)* - \ No newline at end of file +Substrate provides several ways to affect the fees charges for executing a transaction. Substrate developer hub contains full details about [fees](https://substrate.dev/docs/en/next/development/module/fees) and [weights](https://substrate.dev/docs/en/next/conceptual/runtime/weight). + +* Base fee - Applies a fixed fee to each and every transaction. A parameter in the `transaction_payment` module. + +* Length fee - Applies a fee proportional to the transaction's length in bytes. The constant is a parameter in the `transaction_payment` module. + +* Transaction weight - Each transaction can declare a weight, either fixed, or calculated from its parameters. This is exemplified briefly below and more thoroughly in the kitchen. + +* Weight to Fee - A function to convert weight to fee. It doesn't need to be linear, although it often is. The same conversion function is applied across all transactions from all modules in the runtime. This is exemplified briefly below and more thoroughly in the kitchen. + +## Assigning Transaction Weights + +For simple transactions a fixed weight will do. +```rust +decl_module! { + pub struct Module for enum Call { + + #[weight = SimpleDispatchInfo::FixedNormal(100)] + fn store_value(_origin, entry: u32) -> Result { + // --snip-- + } +``` + +For more complex transactions, custom weight calculations can be performed. +```rust +pub struct Conditional(u32); + +impl WeighData<(&bool, &u32)> for Conditional { + fn weigh_data(&self, (switch, val): (&bool, &u32)) -> Weight { + + if *switch { + val.saturating_mul(self.0) + } + else { + self.0 + } + } +} +``` + +These examples, and several others can be compiled in the kitchen. + +While you can make reasonable estimates of resource consumption at +design time, it is always best to actually measure the resources +required of your functions through an empirical process. Failure to +perform such rigorous measurement may result in an economically +insecure chain. + +## Converting Weight To Fees + +In many cases converting weight to fees 1:1 will suffice and be accomplished with [`ConvertInto`](https://crates.parity.io/sr_primitives/traits/struct.ConvertInto.html). This approach is taken in the [node template](https://github.com/substrate-developer-hub/substrate-node-template/blob/43ee95347b6626580b1d9d554c3c8b77dc85bc01/runtime/src/lib.rs#L230) as well as the kitchen's own super runtime. +```rust +impl transaction_payment::Trait for Runtime { + // --snip-- + type WeightToFee = ConvertInto; +} +``` + +This example uses a quadratic conversion and supports custom coefficients +```rust +pub struct QuadraticWeightToFee(C0, C1, C2); + +impl Convert for QuadraticWeightToFee + where C0: Get, C1: Get, C2: Get { + + fn convert(w: Weight) -> Balance { + let c0 = C0::get(); + let c1 = C1::get(); + let c2 = C2::get(); + let w = Balance::from(w); + + // TODO use safe math + c0 + c1 * w + c2 * w * w + } +} +``` + +These examples, and several others can be compiled in the kitchen.