diff --git a/.github/workflows/runtime-tests.yml b/.github/workflows/runtime-tests.yml deleted file mode 100644 index cc508299..00000000 --- a/.github/workflows/runtime-tests.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Tests - -on: - pull_request: - branches: - - master - push: - branches: - - master - -jobs: - build_release_node: - name: Build Release Node - runs-on: ubuntu-18.04 - steps: - - name: Checkout Code - uses: actions/checkout@v1 - - # Steps taken from https://github.com/actions/cache/blob/master/examples.md#rust---cargo - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - name: Install toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2020-03-09 - components: rustfmt - target: wasm32-unknown-unknown - default: true - - - name: Build the node - run: cargo build --release - - execute_runtime_tests: - name: Execute Runtime Tests - needs: build_release_node - runs-on: ubuntu-18.04 - steps: - - name: Checkout Code - uses: actions/checkout@v1 - - # Steps taken from https://github.com/actions/cache/blob/master/examples.md#rust---cargo - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - name: Install toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2020-03-09 - components: rustfmt - target: wasm32-unknown-unknown - default: true - - - name: Execute Runtime Tests - run: cargo test -p academy-pow-runtime diff --git a/.github/workflows/unit-tests-suite.yml b/.github/workflows/unit-tests-suite.yml new file mode 100644 index 00000000..f1440797 --- /dev/null +++ b/.github/workflows/unit-tests-suite.yml @@ -0,0 +1,59 @@ +--- +name: Unit tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: false + +env: + CARGO_TERM_COLOR: always + +jobs: + unit-test-suite: + + runs-on: ubuntu-latest + + steps: + + - name: Install protoc compiler + shell: bash + run: | + sudo apt-get install -y protobuf-compiler + + - name: Checkout Source code + uses: actions/checkout@v3 + + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Install Rust toolchain + uses: Cardinal-Cryptography/github-actions/install-rust-toolchain@v1 + + - name: Run fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all + + - name: Run clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-targets -- --no-deps -D warnings + + - name: Run tests + run: cargo test --verbose diff --git a/README.md b/README.md index dcf3d8d9..ddf9e975 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Academy PoW +![Unit Tests](https://github.com/Polkadot-Blockchain-Academy/Academy-PoW/workflows/unit-tests-suite.yml/badge.svg) + This repo houses a Proof of Work based blockchain node for use in the Polkadot Blockchain Academy. It happens to be based on Substrate, but no Substrate knowledge is required. ## Connecting the UI @@ -21,4 +23,4 @@ In the left panel of the UI, switch to the `Local Node` endpoint. ## More Help -This code will be used primarily as an in-class activity that is instructor led, so just wait for details in class. \ No newline at end of file +This code will be used primarily as an in-class activity that is instructor led, so just wait for details in class. diff --git a/node/build.rs b/node/build.rs index aa9206c1..f97fd98a 100644 --- a/node/build.rs +++ b/node/build.rs @@ -1,4 +1,4 @@ fn main() { - substrate_build_script_utils::generate_cargo_keys(); - substrate_build_script_utils::rerun_if_git_head_changed(); + substrate_build_script_utils::generate_cargo_keys(); + substrate_build_script_utils::rerun_if_git_head_changed(); } diff --git a/node/sha3pow/src/lib.rs b/node/sha3pow/src/lib.rs index af2830ac..2e7fb6ef 100644 --- a/node/sha3pow/src/lib.rs +++ b/node/sha3pow/src/lib.rs @@ -1,147 +1,149 @@ +use std::sync::Arc; + use parity_scale_codec::{Decode, Encode}; use sc_client_api::HeaderBackend; use sc_consensus_pow::{Error, PowAlgorithm}; use sha3::{Digest, Sha3_256}; -use sp_api::{ProvideRuntimeApi, HeaderT}; +use sp_api::{HeaderT, ProvideRuntimeApi}; use sp_consensus_pow::{DifficultyApi, Seal as RawSeal}; use sp_core::{H256, U256}; -use sp_runtime::generic::BlockId; -use sp_runtime::traits::Block as BlockT; -use std::sync::Arc; +use sp_runtime::{generic::BlockId, traits::Block as BlockT}; /// Determine whether the given hash satisfies the given difficulty. /// The test is done by multiplying the two together. If the product /// overflows the bounds of U256, then the product (and thus the hash) /// was too high. pub fn hash_meets_difficulty(hash: &H256, difficulty: U256) -> bool { - let num_hash = U256::from(&hash[..]); - let (_, overflowed) = num_hash.overflowing_mul(difficulty); + let num_hash = U256::from(&hash[..]); + let (_, overflowed) = num_hash.overflowing_mul(difficulty); - !overflowed + !overflowed } /// A Seal struct that will be encoded to a Vec as used as the /// `RawSeal` type. #[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)] pub struct Seal { - pub difficulty: U256, - pub work: H256, - pub nonce: U256, + pub difficulty: U256, + pub work: H256, + pub nonce: U256, } /// A not-yet-computed attempt to solve the proof of work. Calling the /// compute method will compute the hash and return the seal. #[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)] pub struct Compute { - pub difficulty: U256, - pub pre_hash: H256, - pub nonce: U256, + pub difficulty: U256, + pub pre_hash: H256, + pub nonce: U256, } impl Compute { - pub fn compute(self) -> Seal { - let work = H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice()); - - Seal { - nonce: self.nonce, - difficulty: self.difficulty, - work, - } - } + pub fn compute(self) -> Seal { + let work = H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice()); + + Seal { + nonce: self.nonce, + difficulty: self.difficulty, + work, + } + } } /// A complete PoW Algorithm that uses Sha3 hashing. /// Needs a reference to the client so it can grab the difficulty from the runtime. pub struct Sha3Algorithm { - client: Arc, + client: Arc, } impl Sha3Algorithm { - pub fn new(client: Arc) -> Self { - Self { client } - } + pub fn new(client: Arc) -> Self { + Self { client } + } } // Manually implement clone. Deriving doesn't work because // it'll derive impl Clone for Sha3Algorithm. But C in practice isn't Clone. impl Clone for Sha3Algorithm { - fn clone(&self) -> Self { - Self::new(self.client.clone()) - } + fn clone(&self) -> Self { + Self::new(self.client.clone()) + } } // Here we implement the general PowAlgorithm trait for our concrete Sha3Algorithm impl, C> PowAlgorithm for Sha3Algorithm where - C: ProvideRuntimeApi, - C::Api: DifficultyApi, - C: HeaderBackend, + C: ProvideRuntimeApi, + C::Api: DifficultyApi, + C: HeaderBackend, { - type Difficulty = U256; - - fn difficulty(&self, parent: B::Hash) -> Result> { - let parent_header = self.client - .header(parent) - .expect("Database should perform lookup successfully") - .expect("parent header should be present in the db"); - - let parent_number = *parent_header.number(); - - // Start with a simple hard-coded difficulty - // let difficulty = U256::from(100_000_000); - - // Sketch out the possibility for difficulty-based hard forks - let difficulty = if parent_number < 2400u32.into() { - U256::from(100_000_000) - } - else /*parent_number < TODO*/ { - U256::from(1_000_000_000) - // } - // else { - // self.client - // .runtime_api() - // .difficulty(&parent_id) - // .map_err(|err| { - // sc_consensus_pow::Error::Environment(format!( - // "Fetching difficulty from runtime failed: {:?}", - // err - // )) - // }) - }; - - Ok(difficulty) - } - - fn verify( - &self, - _parent: &BlockId, - pre_hash: &H256, - _pre_digest: Option<&[u8]>, - seal: &RawSeal, - difficulty: Self::Difficulty, - ) -> Result> { - // Try to construct a seal object by decoding the raw seal given - let seal = match Seal::decode(&mut &seal[..]) { - Ok(seal) => seal, - Err(_) => return Ok(false), - }; - - // See whether the hash meets the difficulty requirement. If not, fail fast. - if !hash_meets_difficulty(&seal.work, difficulty) { - return Ok(false); - } - - // Make sure the provided work actually comes from the correct pre_hash - let compute = Compute { - difficulty, - pre_hash: *pre_hash, - nonce: seal.nonce, - }; - - if compute.compute() != seal { - return Ok(false); - } - - Ok(true) - } + type Difficulty = U256; + + fn difficulty(&self, parent: B::Hash) -> Result> { + let parent_header = self + .client + .header(parent) + .expect("Database should perform lookup successfully") + .expect("parent header should be present in the db"); + + let parent_number = *parent_header.number(); + + // Start with a simple hard-coded difficulty + // let difficulty = U256::from(100_000_000); + + // Sketch out the possibility for difficulty-based hard forks + let difficulty = if parent_number < 2400u32.into() { + U256::from(100_000_000) + } else { + /*parent_number < TODO*/ + + U256::from(1_000_000_000) + // } + // else { + // self.client + // .runtime_api() + // .difficulty(&parent_id) + // .map_err(|err| { + // sc_consensus_pow::Error::Environment(format!( + // "Fetching difficulty from runtime failed: {:?}", + // err + // )) + // }) + }; + + Ok(difficulty) + } + + fn verify( + &self, + _parent: &BlockId, + pre_hash: &H256, + _pre_digest: Option<&[u8]>, + seal: &RawSeal, + difficulty: Self::Difficulty, + ) -> Result> { + // Try to construct a seal object by decoding the raw seal given + let seal = match Seal::decode(&mut &seal[..]) { + Ok(seal) => seal, + Err(_) => return Ok(false), + }; + + // See whether the hash meets the difficulty requirement. If not, fail fast. + if !hash_meets_difficulty(&seal.work, difficulty) { + return Ok(false); + } + + // Make sure the provided work actually comes from the correct pre_hash + let compute = Compute { + difficulty, + pre_hash: *pre_hash, + nonce: seal.nonce, + }; + + if compute.compute() != seal { + return Ok(false); + } + + Ok(true) + } } diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index 3d76a8b4..2e4fad03 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -1,10 +1,10 @@ -use sp_core::{Pair, Public, sr25519}; use academy_pow_runtime::{ - AccountId, BalancesConfig, GenesisConfig, DifficultyAdjustmentConfig, - /*SudoConfig,*/ SystemConfig, WASM_BINARY, Signature, + AccountId, BalancesConfig, DifficultyAdjustmentConfig, GenesisConfig, Signature, + /*SudoConfig,*/ SystemConfig, WASM_BINARY, }; -use sc_service; -use sp_runtime::traits::{Verify, IdentifyAccount}; + +use sp_core::{sr25519, Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; // Note this is the URL for the telemetry server //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; @@ -14,100 +14,109 @@ pub type ChainSpec = sc_service::GenericChainSpec; /// Helper function to generate a crypto pair from seed pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() } type AccountPublic = ::Signer; /// Helper function to generate an account ID from seed -pub fn get_account_id_from_seed(seed: &str) -> AccountId where - AccountPublic: From<::Public> +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, { - AccountPublic::from(get_from_seed::(seed)).into_account() + AccountPublic::from(get_from_seed::(seed)).into_account() } pub fn development_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; + let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; - Ok(ChainSpec::from_genesis( - "Development", - "dev", - sc_service::ChainType::Development, - || testnet_genesis( - wasm_binary, - get_account_id_from_seed::("Alice"), - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - true, - ), - vec![], - None, - None, - None, - None, - None, - )) + Ok(ChainSpec::from_genesis( + "Development", + "dev", + sc_service::ChainType::Development, + || { + testnet_genesis( + wasm_binary, + get_account_id_from_seed::("Alice"), + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + true, + ) + }, + vec![], + None, + None, + None, + None, + None, + )) } pub fn local_testnet_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; + let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; - Ok(ChainSpec::from_genesis( - "Local Testnet", - "local_testnet", - sc_service::ChainType::Local, - || testnet_genesis( - wasm_binary, - get_account_id_from_seed::("Alice"), - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - true, - ), - vec![], - None, - None, - None, - None, - None, - )) + Ok(ChainSpec::from_genesis( + "Local Testnet", + "local_testnet", + sc_service::ChainType::Local, + || { + testnet_genesis( + wasm_binary, + get_account_id_from_seed::("Alice"), + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + true, + ) + }, + vec![], + None, + None, + None, + None, + None, + )) } fn testnet_genesis( - wasm_binary: &[u8], - root_key: AccountId, - endowed_accounts: Vec, - _enable_println: bool + wasm_binary: &[u8], + _root_key: AccountId, + endowed_accounts: Vec, + _enable_println: bool, ) -> GenesisConfig { - GenesisConfig { - system: SystemConfig { - code: wasm_binary.to_vec(), - }, - balances: BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), - }, - // sudo: SudoConfig { - // key: Some(root_key), - // }, - difficulty_adjustment: DifficultyAdjustmentConfig { - initial_difficulty: 4_000_000.into(), - }, - } + GenesisConfig { + system: SystemConfig { + code: wasm_binary.to_vec(), + }, + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1 << 60)) + .collect(), + }, + // sudo: SudoConfig { + // key: Some(root_key), + // }, + difficulty_adjustment: DifficultyAdjustmentConfig { + initial_difficulty: 4_000_000.into(), + }, + } } diff --git a/node/src/cli.rs b/node/src/cli.rs index e298c83b..0c813142 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -2,54 +2,54 @@ use std::convert::TryInto; #[derive(Debug, clap::Parser)] pub struct Cli { - #[clap(subcommand)] - pub subcommand: Option, + #[clap(subcommand)] + pub subcommand: Option, - #[clap(flatten)] - pub run: RunCmd, + #[clap(flatten)] + pub run: RunCmd, } #[derive(Debug, clap::Parser)] pub struct RunCmd { - #[clap(flatten)] - pub base: sc_cli::RunCmd, + #[clap(flatten)] + pub base: sc_cli::RunCmd, - /// Miner's SR25519 public key for block rewards - #[clap(long, value_parser = parse_sr25519_public_key)] - pub sr25519_public_key: Option, + /// Miner's SR25519 public key for block rewards + #[clap(long, value_parser = parse_sr25519_public_key)] + pub sr25519_public_key: Option, } fn parse_sr25519_public_key(i: &str) -> Result { - hex::decode(i) - .map_err(|e| e.to_string())? - .as_slice() - .try_into() - .or(Err("invalid length for SR25519 public key".to_string())) + hex::decode(i) + .map_err(|e| e.to_string())? + .as_slice() + .try_into() + .or(Err("invalid length for SR25519 public key".to_string())) } #[derive(Debug, clap::Subcommand)] pub enum Subcommand { - /// Build a chain specification. - BuildSpec(sc_cli::BuildSpecCmd), + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), - /// Validate blocks. - CheckBlock(sc_cli::CheckBlockCmd), + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), - /// Export blocks. - ExportBlocks(sc_cli::ExportBlocksCmd), + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), - /// Export the state of a given block into a chain spec. - ExportState(sc_cli::ExportStateCmd), + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), - /// Import blocks. - ImportBlocks(sc_cli::ImportBlocksCmd), + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), - /// Remove the whole chain. - PurgeChain(sc_cli::PurgeChainCmd), + /// Remove the whole chain. + PurgeChain(sc_cli::PurgeChainCmd), - /// Revert the chain to a previous state. - Revert(sc_cli::RevertCmd), + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), - /// Db meta columns information. - ChainInfo(sc_cli::ChainInfoCmd), -} \ No newline at end of file + /// Db meta columns information. + ChainInfo(sc_cli::ChainInfoCmd), +} diff --git a/node/src/command.rs b/node/src/command.rs index 9408988a..1c1407c5 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -14,123 +14,149 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::service; -use crate::chain_spec; -use crate::cli::{Cli, Subcommand}; -use sc_cli::CliConfiguration; -use sc_cli::{SubstrateCli, RuntimeVersion, ChainSpec}; -use sc_service::PartialComponents; use academy_pow_runtime::Block; +use sc_cli::{ChainSpec, CliConfiguration, RuntimeVersion, SubstrateCli}; +use sc_service::PartialComponents; + +use crate::{ + chain_spec, + cli::{Cli, Subcommand}, + service, +}; impl SubstrateCli for Cli { - fn impl_name() -> String { - "Academy PoW Chain".into() - } - - fn impl_version() -> String { - env!("SUBSTRATE_CLI_IMPL_VERSION").into() - } - - fn executable_name() -> String { - env!("CARGO_PKG_NAME").into() - } - - fn author() -> String { - env!("CARGO_PKG_AUTHORS").into() - } - - fn description() -> String { - env!("CARGO_PKG_DESCRIPTION").into() - } - - fn support_url() -> String { - "support.anonymous.an".into() - } - - fn copyright_start_year() -> i32 { - 2019 - } - - fn load_spec(&self, id: &str) -> Result, String> { - Ok(match id { - "" => Box::new(chain_spec::ChainSpec::from_json_bytes( - &include_bytes!("../../spec.json")[..], - )?), - "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), - )?), - }) - } - - fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { - &academy_pow_runtime::VERSION - } + fn impl_name() -> String { + "Academy PoW Chain".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn executable_name() -> String { + env!("CARGO_PKG_NAME").into() + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn description() -> String { + env!("CARGO_PKG_DESCRIPTION").into() + } + + fn support_url() -> String { + "support.anonymous.an".into() + } + + fn copyright_start_year() -> i32 { + 2019 + } + + fn load_spec(&self, id: &str) -> Result, String> { + Ok(match id { + "" => Box::new(chain_spec::ChainSpec::from_json_bytes( + &include_bytes!("../../spec.json")[..], + )?), + "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), + )?), + }) + } + + fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { + &academy_pow_runtime::VERSION + } } /// Parse and run command line arguments pub fn run() -> sc_cli::Result<()> { - let cli = Cli::from_args(); - - match &cli.subcommand { - 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, service::build_pow_import_queue)?; - 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, service::build_pow_import_queue)?; - 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, service::build_pow_import_queue)?; - 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, service::build_pow_import_queue)?; - 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, service::build_pow_import_queue)?; - 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 sr25519_public_key = cli.run.sr25519_public_key.unwrap_or(sp_core::sr25519::Public::from_raw([0; 32])); - let instant_seal = cli.run.base.is_dev()?; - let runner = cli.create_runner(&cli.run.base)?; - runner.run_node_until_exit(|config| async move { - service::new_full(config, sr25519_public_key, instant_seal).map_err(sc_cli::Error::Service) - }) - }, - } + let cli = Cli::from_args(); + + match &cli.subcommand { + 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, service::build_pow_import_queue)?; + 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, service::build_pow_import_queue)?; + 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, service::build_pow_import_queue)?; + 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, service::build_pow_import_queue)?; + 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, service::build_pow_import_queue)?; + 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 sr25519_public_key = cli + .run + .sr25519_public_key + .unwrap_or(sp_core::sr25519::Public::from_raw([0; 32])); + let instant_seal = cli.run.base.is_dev()?; + let runner = cli.create_runner(&cli.run.base)?; + runner.run_node_until_exit(|config| async move { + service::new_full(config, sr25519_public_key, instant_seal) + .map_err(sc_cli::Error::Service) + }) + } + } } diff --git a/node/src/main.rs b/node/src/main.rs index 369e6932..0e4e82f5 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -8,5 +8,5 @@ mod cli; mod command; fn main() -> sc_cli::Result<()> { - command::run() + command::run() } diff --git a/node/src/service.rs b/node/src/service.rs index 73d6b3f9..d229155a 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -1,36 +1,37 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. +use core::clone::Clone; use std::sync::Arc; -use sc_consensus::LongestChain; -use sp_api::TransactionFor; + use academy_pow_runtime::{self, opaque::Block, RuntimeApi}; -use sc_service::{error::Error as ServiceError, Configuration, PartialComponents, TaskManager}; +use parity_scale_codec::Encode; +use sc_consensus::LongestChain; use sc_executor::NativeElseWasmExecutor; +use sc_service::{error::Error as ServiceError, Configuration, PartialComponents, TaskManager}; +use sc_telemetry::{Telemetry, TelemetryWorker}; use sha3pow::Sha3Algorithm; -use core::clone::Clone; +use sp_api::TransactionFor; use sp_core::sr25519; -use parity_scale_codec::Encode; -use sc_telemetry::{Telemetry, TelemetryWorker}; // Our native executor instance. pub struct ExecutorDispatch; impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + type ExtendHostFunctions = (); - type ExtendHostFunctions = (); - - fn dispatch(method: &str, data: &[u8]) -> Option> { - academy_pow_runtime::api::dispatch(method, data) - } + fn dispatch(method: &str, data: &[u8]) -> Option> { + academy_pow_runtime::api::dispatch(method, data) + } - fn native_version() -> sc_executor::NativeVersion { - academy_pow_runtime::native_version() - } + fn native_version() -> sc_executor::NativeVersion { + academy_pow_runtime::native_version() + } } //TODO We'll need the mining worker. Can probably copy from recipes -type FullClient = sc_service::TFullClient>; +type FullClient = + sc_service::TFullClient>; type FullBackend = sc_service::TFullBackend; type FullSelectChain = sc_consensus::LongestChain; @@ -39,281 +40,277 @@ type BoxBlockImport = sc_consensus::BoxBlockImport(config: &Configuration, build_import_queue: BIQ) -> Result< - PartialComponents< - FullClient, - FullBackend, - FullSelectChain, - BasicImportQueue, - sc_transaction_pool::FullPool, - ( - BoxBlockImport, - Option, - ), - >, - ServiceError, +#[allow(clippy::type_complexity)] +pub fn new_partial( + config: &Configuration, + build_import_queue: BIQ, +) -> Result< + PartialComponents< + FullClient, + FullBackend, + FullSelectChain, + BasicImportQueue, + sc_transaction_pool::FullPool, + (BoxBlockImport, Option), + >, + ServiceError, > where - BIQ: FnOnce( - Arc, - &Configuration, - &FullSelectChain, - &TaskManager, - ) -> Result< - ( - BasicImportQueue, - BoxBlockImport, - ), - ServiceError, - > + BIQ: FnOnce( + Arc, + &Configuration, + &FullSelectChain, + &TaskManager, + ) -> Result<(BasicImportQueue, BoxBlockImport), 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_native_or_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 = 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, block_import) = build_import_queue( - client.clone(), - config, - &select_chain, - &task_manager, - )?; - - Ok(PartialComponents { - client, - backend, - task_manager, - import_queue, - keystore_container, - select_chain, - transaction_pool, - other: (block_import, telemetry), - }) + 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_native_or_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 = 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, block_import) = + build_import_queue(client.clone(), config, &select_chain, &task_manager)?; + + Ok(PartialComponents { + client, + backend, + task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: (block_import, telemetry), + }) } /// Build the import queue for the manual seal service. pub fn build_manual_seal_import_queue( - client: Arc, - config: &Configuration, - _select_chain: &FullSelectChain, - task_manager: &TaskManager, + client: Arc, + config: &Configuration, + _select_chain: &FullSelectChain, + task_manager: &TaskManager, ) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError> { - Ok(( - sc_consensus_manual_seal::import_queue( - Box::new(client.clone()), - &task_manager.spawn_essential_handle(), - config.prometheus_registry(), - ), - Box::new(client), - )) + Ok(( + sc_consensus_manual_seal::import_queue( + Box::new(client.clone()), + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + ), + Box::new(client), + )) } /// Build the import queue for the pow service pub fn build_pow_import_queue( - client: Arc, - config: &Configuration, - select_chain: &FullSelectChain, - task_manager: &TaskManager, + client: Arc, + config: &Configuration, + select_chain: &FullSelectChain, + task_manager: &TaskManager, ) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError> { - let pow_block_import = sc_consensus_pow::PowBlockImport::new( - client.clone(), - client.clone(), - Sha3Algorithm::new(client.clone()), - 0, // check inherents starting at block 0 - select_chain.clone(), - move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - // We don't need the current mining key to check inherents, so we just use a default. - let author = academy_pow_runtime::block_author::InherentDataProvider(Default::default()); - - Ok((timestamp, author)) - }, - ); - - let import_queue = sc_consensus_pow::import_queue( - Box::new(pow_block_import.clone()), - None, - Sha3Algorithm::new(client.clone()), - &task_manager.spawn_essential_handle(), - config.prometheus_registry(), - )?; - - Ok((import_queue, Box::new(pow_block_import))) + let pow_block_import = sc_consensus_pow::PowBlockImport::new( + client.clone(), + client.clone(), + Sha3Algorithm::new(client.clone()), + 0, // check inherents starting at block 0 + select_chain.clone(), + move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + // We don't need the current mining key to check inherents, so we just use a default. + let author = + academy_pow_runtime::block_author::InherentDataProvider(Default::default()); + + Ok((timestamp, author)) + }, + ); + + let import_queue = sc_consensus_pow::import_queue( + Box::new(pow_block_import.clone()), + None, + Sha3Algorithm::new(client), + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + )?; + + Ok((import_queue, Box::new(pow_block_import))) } /// Builds a new service for a full client. -pub fn new_full(config: Configuration, sr25519_public_key: sr25519::Public, instant_seal: bool) -> Result { - - let build_import_queue = if instant_seal { - build_manual_seal_import_queue - } else { - build_pow_import_queue - }; - - let sc_service::PartialComponents { - client, - backend, - mut task_manager, - import_queue, - keystore_container, - select_chain, - transaction_pool, - other: (pow_block_import, mut telemetry), - } = new_partial(&config, build_import_queue)?; - - 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, - block_announce_validator_builder: None, - warp_sync_params: None, - })?; - - let role = config.role.clone(); - let prometheus_registry = config.prometheus_registry().cloned(); - - sc_service::spawn_tasks(sc_service::SpawnTasksParams { - network: network.clone(), - client: client.clone(), - keystore: keystore_container.keystore(), - task_manager: &mut task_manager, - transaction_pool: transaction_pool.clone(), - rpc_builder: Box::new(|_, _| Ok(jsonrpsee::RpcModule::new(()))), - backend, - system_rpc_tx, - tx_handler_controller, - sync_service: sync_service.clone(), - config, - telemetry: telemetry.as_mut(), - })?; - - - - if role.is_authority() { - 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()), - ); - - // If instant seal is requested, we just start it. Otherwise, we do the full PoW setup. - if instant_seal { - - 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); - } else { - - let (mining_worker, mining_worker_task) = sc_consensus_pow::start_mining_worker( - Box::new(pow_block_import), - client.clone(), - select_chain, - Sha3Algorithm::new(client.clone()), - proposer, - sync_service.clone(), - sync_service.clone(), - None, - // This code is copied from above. Would be better to not repeat it. - move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let author = - academy_pow_runtime::block_author::InherentDataProvider( - sr25519_public_key.encode(), - ); - - Ok((timestamp, author)) - }, - std::time::Duration::from_secs(10), - std::time::Duration::from_secs(5), - ); - - task_manager - .spawn_essential_handle() - .spawn_blocking("pow-miner", Some("pow-mining"), mining_worker_task); - - // Start Mining - //TODO Some of this should move into the sha3pow crate. - use sp_core::U256; - use sha3pow::{Compute, hash_meets_difficulty}; - let mut nonce: U256 = U256::from(0); - std::thread::spawn(move || loop { - let worker = mining_worker.clone(); - let metadata = worker.metadata(); - if let Some(metadata) = metadata { - let compute = Compute { - difficulty: metadata.difficulty, - pre_hash: metadata.pre_hash, - nonce, - }; - let seal = compute.compute(); - if hash_meets_difficulty(&seal.work, seal.difficulty) { - nonce = U256::from(0); - let _ = futures::executor::block_on(worker.submit(seal.encode())); - } else { - nonce = nonce.saturating_add(U256::from(1)); - if nonce == U256::MAX { - nonce = U256::from(0); - } - } - } else { - std::thread::sleep(std::time::Duration::from_secs(1)); - } - }); - } - } - - network_starter.start_network(); - Ok(task_manager) +pub fn new_full( + config: Configuration, + sr25519_public_key: sr25519::Public, + instant_seal: bool, +) -> Result { + let build_import_queue = if instant_seal { + build_manual_seal_import_queue + } else { + build_pow_import_queue + }; + + let sc_service::PartialComponents { + client, + backend, + mut task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: (pow_block_import, mut telemetry), + } = new_partial(&config, build_import_queue)?; + + 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, + block_announce_validator_builder: None, + warp_sync_params: None, + })?; + + let role = config.role.clone(); + let prometheus_registry = config.prometheus_registry().cloned(); + + 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: Box::new(|_, _| Ok(jsonrpsee::RpcModule::new(()))), + backend, + system_rpc_tx, + tx_handler_controller, + sync_service: sync_service.clone(), + config, + telemetry: telemetry.as_mut(), + })?; + + if role.is_authority() { + 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()), + ); + + // If instant seal is requested, we just start it. Otherwise, we do the full PoW setup. + if instant_seal { + 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, + ); + } else { + let (mining_worker, mining_worker_task) = sc_consensus_pow::start_mining_worker( + Box::new(pow_block_import), + client.clone(), + select_chain, + Sha3Algorithm::new(client), + proposer, + sync_service.clone(), + sync_service, + None, + // This code is copied from above. Would be better to not repeat it. + move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let author = academy_pow_runtime::block_author::InherentDataProvider( + sr25519_public_key.encode(), + ); + + Ok((timestamp, author)) + }, + std::time::Duration::from_secs(10), + std::time::Duration::from_secs(5), + ); + + task_manager.spawn_essential_handle().spawn_blocking( + "pow-miner", + Some("pow-mining"), + mining_worker_task, + ); + + // Start Mining + //TODO Some of this should move into the sha3pow crate. + use sha3pow::{hash_meets_difficulty, Compute}; + use sp_core::U256; + let mut nonce: U256 = U256::from(0); + std::thread::spawn(move || loop { + let worker = mining_worker.clone(); + let metadata = worker.metadata(); + if let Some(metadata) = metadata { + let compute = Compute { + difficulty: metadata.difficulty, + pre_hash: metadata.pre_hash, + nonce, + }; + let seal = compute.compute(); + if hash_meets_difficulty(&seal.work, seal.difficulty) { + nonce = U256::from(0); + let _ = futures::executor::block_on(worker.submit(seal.encode())); + } else { + nonce = nonce.saturating_add(U256::from(1)); + if nonce == U256::MAX { + nonce = U256::from(0); + } + } + } else { + std::thread::sleep(std::time::Duration::from_secs(1)); + } + }); + } + } + + network_starter.start_network(); + Ok(task_manager) } diff --git a/runtime/build.rs b/runtime/build.rs index 9b53d245..18a76f78 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -1,9 +1,9 @@ use substrate_wasm_builder::WasmBuilder; fn main() { - WasmBuilder::new() - .with_current_project() - .export_heap_base() - .import_memory() - .build() + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() } diff --git a/runtime/src/block_author.rs b/runtime/src/block_author.rs index a021b963..5be6ba1b 100644 --- a/runtime/src/block_author.rs +++ b/runtime/src/block_author.rs @@ -1,132 +1,129 @@ //! This pallet allows block authors to self-identify by providing an sr25519 public key -//! +//! //! The included trait allows other pallets to fetch the author's account as long as the //! runtime's AccountId type can be created from an sr25519 public key. +pub use pallet::*; +use parity_scale_codec::{Decode, Encode}; use sp_core::sr25519; -use sp_std::vec::Vec; +use sp_inherents::{InherentData, InherentIdentifier, IsFatalError}; use sp_runtime::RuntimeString; -use sp_inherents::{InherentIdentifier, IsFatalError, InherentData}; -use parity_scale_codec::{Encode, Decode}; - - -pub use pallet::*; +use sp_std::vec::Vec; #[frame_support::pallet(dev_mode)] pub mod pallet { - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use super::*; - - /// The BlockAuthor Inherent pallet. - #[pallet::pallet] - pub struct Pallet(PhantomData); - /// The pallet's configuration trait. Nothing to configure. - #[pallet::config] - pub trait Config: frame_system::Config { - fn on_author_set(_author_account: Self::AccountId) {} - } - - #[pallet::error] - pub enum Error { - /// Author already set in block. - AuthorAlreadySet, - } - - /// Author of current block. - #[pallet::storage] - pub type Author = StorageValue<_, sr25519::Public, OptionQuery>; - - #[pallet::call] - impl Pallet - where - ::AccountId: From - { - - /// Inherent to set the author of a block - #[pallet::weight(1_000_000)] - pub fn set_author(origin: OriginFor, author: sr25519::Public) -> DispatchResult - - { - ensure_none(origin)?; - ensure!(Author::::get().is_none(), Error::::AuthorAlreadySet); - - // Store the author in case other pallets want to fetch it and to let - // offchain tools inspect it - Author::::put(&author); - - // Call the hook - T::on_author_set(author.into()); - - Ok(()) - } - } - - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_initialize(_n: T::BlockNumber) -> Weight { - // Reset the author to None at the beginning of the block - Author::::kill(); - - // Return zero weight because we are not using weight-based - // transaction fees. - Weight::zero() - } - } - - #[pallet::inherent] - impl ProvideInherent for Pallet - where - ::AccountId: From - { - type Call = Call; - type Error = InherentError; - const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; - - fn is_inherent_required(_: &InherentData) -> Result, Self::Error> { - // Return Ok(Some(_)) unconditionally because this inherent is required in every block - // If it is not found, throw an AuthorInherentRequired error. - Ok(Some(InherentError::Other( - sp_runtime::RuntimeString::Borrowed("BlockAuthorInherentRequired"), - ))) - } - - fn create_inherent(data: &InherentData) -> Option { - // Grab the Vec labelled with "author_" from the map of all inherent data - let author_raw = data.get_data::(&INHERENT_IDENTIFIER) - .expect("Gets and decodes authorship inherent data")?; - - // Decode the Vec into an actual author - let author = sr25519::Public::decode(&mut &author_raw[..]) - .expect("Decodes author raw inherent data"); - - Some(Call::set_author{author}) - } - - fn is_inherent(call: &Self::Call) -> bool { - matches!(call, Call::set_author{..}) - } - } + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + use super::*; + + /// The BlockAuthor Inherent pallet. + #[pallet::pallet] + pub struct Pallet(PhantomData); + /// The pallet's configuration trait. Nothing to configure. + #[pallet::config] + pub trait Config: frame_system::Config { + fn on_author_set(_author_account: Self::AccountId) {} + } + + #[pallet::error] + pub enum Error { + /// Author already set in block. + AuthorAlreadySet, + } + + /// Author of current block. + #[pallet::storage] + pub type Author = StorageValue<_, sr25519::Public, OptionQuery>; + + #[pallet::call] + impl Pallet + where + ::AccountId: From, + { + /// Inherent to set the author of a block + #[pallet::weight(1_000_000)] + pub fn set_author(origin: OriginFor, author: sr25519::Public) -> DispatchResult { + ensure_none(origin)?; + ensure!(Author::::get().is_none(), Error::::AuthorAlreadySet); + + // Store the author in case other pallets want to fetch it and to let + // offchain tools inspect it + Author::::put(author); + + // Call the hook + T::on_author_set(author.into()); + + Ok(()) + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: T::BlockNumber) -> Weight { + // Reset the author to None at the beginning of the block + Author::::kill(); + + // Return zero weight because we are not using weight-based + // transaction fees. + Weight::zero() + } + } + + #[pallet::inherent] + impl ProvideInherent for Pallet + where + ::AccountId: From, + { + type Call = Call; + type Error = InherentError; + const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + + fn is_inherent_required(_: &InherentData) -> Result, Self::Error> { + // Return Ok(Some(_)) unconditionally because this inherent is required in every block + // If it is not found, throw an AuthorInherentRequired error. + Ok(Some(InherentError::Other( + sp_runtime::RuntimeString::Borrowed("BlockAuthorInherentRequired"), + ))) + } + + fn create_inherent(data: &InherentData) -> Option { + // Grab the Vec labelled with "author_" from the map of all inherent data + let author_raw = data + .get_data::(&INHERENT_IDENTIFIER) + .expect("Gets and decodes authorship inherent data")?; + + // Decode the Vec into an actual author + let author = sr25519::Public::decode(&mut &author_raw[..]) + .expect("Decodes author raw inherent data"); + + Some(Call::set_author { author }) + } + + fn is_inherent(call: &Self::Call) -> bool { + matches!(call, Call::set_author { .. }) + } + } } /// A trait to find the author (miner) of the block. pub trait BlockAuthor> { - fn block_author() -> Option; + fn block_author() -> Option; } impl> BlockAuthor for () { - fn block_author() -> Option { - None - } + fn block_author() -> Option { + None + } } -impl BlockAuthor for Pallet +impl BlockAuthor for Pallet where - ::AccountId: From + ::AccountId: From, { - fn block_author() -> Option { - Author::::get().map(|a| a.into()) - } + fn block_author() -> Option { + Author::::get().map(|a| a.into()) + } } pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"author__"; @@ -134,27 +131,27 @@ pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"author__"; #[derive(Encode)] #[cfg_attr(feature = "std", derive(Debug, Decode))] pub enum InherentError { - Other(RuntimeString), + Other(RuntimeString), } impl IsFatalError for InherentError { - fn is_fatal_error(&self) -> bool { - match *self { - InherentError::Other(_) => true, - } - } + fn is_fatal_error(&self) -> bool { + match *self { + InherentError::Other(_) => true, + } + } } impl InherentError { - /// Try to create an instance ouf of the given identifier and data. - #[cfg(feature = "std")] - pub fn try_from(id: &InherentIdentifier, data: &[u8]) -> Option { - if id == &INHERENT_IDENTIFIER { - ::decode(&mut &data[..]).ok() - } else { - None - } - } + /// Try to create an instance ouf of the given identifier and data. + #[cfg(feature = "std")] + pub fn try_from(id: &InherentIdentifier, data: &[u8]) -> Option { + if id == &INHERENT_IDENTIFIER { + ::decode(&mut &data[..]).ok() + } else { + None + } + } } /// The type of data that the inherent will contain. @@ -167,24 +164,26 @@ pub struct InherentDataProvider(pub InherentType); #[cfg(feature = "std")] #[async_trait::async_trait] impl sp_inherents::InherentDataProvider for InherentDataProvider { - async fn provide_inherent_data( - &self, - inherent_data: &mut InherentData, - ) -> Result<(), sp_inherents::Error> { - inherent_data.put_data(INHERENT_IDENTIFIER, &self.0) - } - - async fn try_handle_error( - &self, - identifier: &InherentIdentifier, - _error: &[u8], - ) -> Option> { - // Dont' process modules from other inherents - if *identifier != INHERENT_IDENTIFIER { - return None - } - - // All errors with the author inehrent are fatal - Some(Err(sp_inherents::Error::Application(Box::from(String::from("Error processing author inherent"))))) - } + async fn provide_inherent_data( + &self, + inherent_data: &mut InherentData, + ) -> Result<(), sp_inherents::Error> { + inherent_data.put_data(INHERENT_IDENTIFIER, &self.0) + } + + async fn try_handle_error( + &self, + identifier: &InherentIdentifier, + _error: &[u8], + ) -> Option> { + // Dont' process modules from other inherents + if *identifier != INHERENT_IDENTIFIER { + return None; + } + + // All errors with the author inehrent are fatal + Some(Err(sp_inherents::Error::Application(Box::from( + String::from("Error processing author inherent"), + )))) + } } diff --git a/runtime/src/difficulty.rs b/runtime/src/difficulty.rs index 9626b3f2..e2d86608 100644 --- a/runtime/src/difficulty.rs +++ b/runtime/src/difficulty.rs @@ -5,27 +5,28 @@ //! and worth-while experiment. The DAAs should be abstracted away with a trait. //! Some ideas: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3410460 -use core::cmp::{min, max}; -use parity_scale_codec::{Encode, Decode, MaxEncodedLen}; +use core::cmp::{max, min}; + +use frame_support::traits::Time; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::U256; -use frame_support::traits::Time; use sp_runtime::traits::UniqueSaturatedInto; #[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)] pub struct DifficultyAndTimestamp { - pub difficulty: Difficulty, - pub timestamp: M, + pub difficulty: Difficulty, + pub timestamp: M, } /// Move value linearly toward a goal pub fn damp(actual: u128, goal: u128, damp_factor: u128) -> u128 { - (actual + (damp_factor - 1) * goal) / damp_factor + (actual + (damp_factor - 1) * goal) / damp_factor } /// Limit value to be within some factor from a goal pub fn clamp(actual: u128, goal: u128, clamp_factor: u128) -> u128 { - max(goal / clamp_factor, min(actual, goal * clamp_factor)) + max(goal / clamp_factor, min(actual, goal * clamp_factor)) } const DIFFICULTY_ADJUST_WINDOW: u128 = 60; @@ -35,146 +36,150 @@ pub use pallet::*; #[frame_support::pallet(dev_mode)] pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - /// Pallet's configuration trait. - #[pallet::config] - pub trait Config: frame_system::Config { - /// A Source for timestamp data - type TimeProvider: Time; - /// The block time that the DAA will attempt to maintain - type TargetBlockTime: Get; - /// Dampening factor to use for difficulty adjustment - type DampFactor: Get; - /// Clamp factor to use for difficulty adjustment - /// Limit value to within this factor of goal. Recommended value: 2 - type ClampFactor: Get; - /// The maximum difficulty allowed. Recommended to use u128::max_value() - type MaxDifficulty: Get; - /// Minimum difficulty, enforced in difficulty retargetting - /// avoids getting stuck when trying to increase difficulty subject to dampening - /// Recommended to use same value as DampFactor - type MinDifficulty: Get; - } - - #[pallet::pallet] - pub struct Pallet(_); - - type DifficultyList = [Option::TimeProvider as Time>::Moment>>; 60]; - - /// Past difficulties and timestamps, from earliest to latest. - #[pallet::storage] - pub type PastDifficultiesAndTimestamps = StorageValue< - _, - DifficultyList, - ValueQuery, - EmptyList, - >; - - pub struct EmptyList(PhantomData); - impl Get> for EmptyList { - fn get() -> DifficultyList { - [None; DIFFICULTY_ADJUST_WINDOW as usize] - } - } - - /// Current difficulty. - #[pallet::storage] - #[pallet::getter(fn difficulty)] - pub type CurrentDifficulty = StorageValue<_, Difficulty, ValueQuery>; - - /// Initial difficulty. - #[pallet::storage] - pub type InitialDifficulty = StorageValue<_, Difficulty, ValueQuery>; - - #[pallet::genesis_config] - pub struct GenesisConfig { - pub initial_difficulty: Difficulty, - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - // Initialize the Current difficulty - CurrentDifficulty::::put(self.initial_difficulty); - - // Store the initial difficulty in storage because we will need it - // during the first DIFFICULTY_ADJUSTMENT_WINDOW blocks (see todo below). - InitialDifficulty::::put(self.initial_difficulty); - } - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { initial_difficulty: 4_000_000.into() } - } - } - - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(_n: T::BlockNumber) { - let mut data = PastDifficultiesAndTimestamps::::get(); - - for i in 1..data.len() { - data[i - 1] = data[i]; - } - - data[data.len() - 1] = Some(DifficultyAndTimestamp { - timestamp: T::TimeProvider::now(), - difficulty: Self::difficulty(), - }); - - let mut ts_delta = 0; - for i in 1..(DIFFICULTY_ADJUST_WINDOW as usize) { - let prev: Option = data[i - 1].map(|d| d.timestamp.unique_saturated_into()); - let cur: Option = data[i].map(|d| d.timestamp.unique_saturated_into()); - - let delta = match (prev, cur) { - (Some(prev), Some(cur)) => cur.saturating_sub(prev), - _ => T::TargetBlockTime::get(), - }; - ts_delta += delta; - } - - if ts_delta == 0 { - ts_delta = 1; - } - - let mut diff_sum = U256::zero(); - //TODO Could we just initialize every array cell to the initial difficulty to not need the - // separate storage item? - for i in 0..(DIFFICULTY_ADJUST_WINDOW as usize) { - let diff = match data[i].map(|d| d.difficulty) { - Some(diff) => diff, - None => InitialDifficulty::::get(), - }; - diff_sum += diff; - } - - if diff_sum < U256::from(T::MinDifficulty::get()) { - diff_sum = U256::from(T::MinDifficulty::get()); - } - - // Calculate the average length of the adjustment window - let adjustment_window = DIFFICULTY_ADJUST_WINDOW * T::TargetBlockTime::get(); - - // adjust time delta toward goal subject to dampening and clamping - let adj_ts = clamp( - damp(ts_delta, adjustment_window, T::DampFactor::get()), - adjustment_window, - T::ClampFactor::get(), - ); - - // minimum difficulty avoids getting stuck due to dampening - let difficulty = min(U256::from(T::MaxDifficulty::get()), - max(U256::from(T::MinDifficulty::get()), - diff_sum * U256::from(T::TargetBlockTime::get()) / U256::from(adj_ts))); - - >::put(data); - >::put(difficulty); - } - } + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + use super::*; + + /// Pallet's configuration trait. + #[pallet::config] + pub trait Config: frame_system::Config { + /// A Source for timestamp data + type TimeProvider: Time; + /// The block time that the DAA will attempt to maintain + type TargetBlockTime: Get; + /// Dampening factor to use for difficulty adjustment + type DampFactor: Get; + /// Clamp factor to use for difficulty adjustment + /// Limit value to within this factor of goal. Recommended value: 2 + type ClampFactor: Get; + /// The maximum difficulty allowed. Recommended to use u128::max_value() + type MaxDifficulty: Get; + /// Minimum difficulty, enforced in difficulty retargetting + /// avoids getting stuck when trying to increase difficulty subject to dampening + /// Recommended to use same value as DampFactor + type MinDifficulty: Get; + } + + #[pallet::pallet] + pub struct Pallet(_); + + type DifficultyList = + [Option::TimeProvider as Time>::Moment>>; 60]; + + /// Past difficulties and timestamps, from earliest to latest. + #[pallet::storage] + pub type PastDifficultiesAndTimestamps = + StorageValue<_, DifficultyList, ValueQuery, EmptyList>; + + pub struct EmptyList(PhantomData); + impl Get> for EmptyList { + fn get() -> DifficultyList { + [None; DIFFICULTY_ADJUST_WINDOW as usize] + } + } + + /// Current difficulty. + #[pallet::storage] + #[pallet::getter(fn difficulty)] + pub type CurrentDifficulty = StorageValue<_, Difficulty, ValueQuery>; + + /// Initial difficulty. + #[pallet::storage] + pub type InitialDifficulty = StorageValue<_, Difficulty, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub initial_difficulty: Difficulty, + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + // Initialize the Current difficulty + CurrentDifficulty::::put(self.initial_difficulty); + + // Store the initial difficulty in storage because we will need it + // during the first DIFFICULTY_ADJUSTMENT_WINDOW blocks (see todo below). + InitialDifficulty::::put(self.initial_difficulty); + } + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + initial_difficulty: 4_000_000.into(), + } + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: T::BlockNumber) { + let mut data = PastDifficultiesAndTimestamps::::get(); + + for i in 1..data.len() { + data[i - 1] = data[i]; + } + + data[data.len() - 1] = Some(DifficultyAndTimestamp { + timestamp: T::TimeProvider::now(), + difficulty: Self::difficulty(), + }); + + let mut ts_delta = 0; + for i in 1..(DIFFICULTY_ADJUST_WINDOW as usize) { + let prev: Option = data[i - 1].map(|d| d.timestamp.unique_saturated_into()); + let cur: Option = data[i].map(|d| d.timestamp.unique_saturated_into()); + + let delta = match (prev, cur) { + (Some(prev), Some(cur)) => cur.saturating_sub(prev), + _ => T::TargetBlockTime::get(), + }; + ts_delta += delta; + } + + if ts_delta == 0 { + ts_delta = 1; + } + + let mut diff_sum = U256::zero(); + //TODO Could we just initialize every array cell to the initial difficulty to not need the + // separate storage item? + for item in data.iter().take(DIFFICULTY_ADJUST_WINDOW as usize) { + let diff = match item.map(|d| d.difficulty) { + Some(diff) => diff, + None => InitialDifficulty::::get(), + }; + diff_sum += diff; + } + + if diff_sum < U256::from(T::MinDifficulty::get()) { + diff_sum = U256::from(T::MinDifficulty::get()); + } + + // Calculate the average length of the adjustment window + let adjustment_window = DIFFICULTY_ADJUST_WINDOW * T::TargetBlockTime::get(); + + // adjust time delta toward goal subject to dampening and clamping + let adj_ts = clamp( + damp(ts_delta, adjustment_window, T::DampFactor::get()), + adjustment_window, + T::ClampFactor::get(), + ); + + // minimum difficulty avoids getting stuck due to dampening + let difficulty = min( + U256::from(T::MaxDifficulty::get()), + max( + U256::from(T::MinDifficulty::get()), + diff_sum * U256::from(T::TargetBlockTime::get()) / U256::from(adj_ts), + ), + ); + + >::put(data); + >::put(difficulty); + } + } } diff --git a/runtime/src/faucet.rs b/runtime/src/faucet.rs index 7cc6b8ef..f358f1d1 100644 --- a/runtime/src/faucet.rs +++ b/runtime/src/faucet.rs @@ -1,47 +1,46 @@ //! A simple token faucet that gives the caller 5 tokens per call use frame_support::traits::Currency; - pub use pallet::*; #[frame_support::pallet(dev_mode)] pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - /// Pallet's configuration trait. - #[pallet::config] - pub trait Config: frame_system::Config { - /// The currency type in which the faucet provides token - type Currency: Currency; - - /// The amount of tokens that should be created for each call into the faucet - type DripAmount: Get>; - } - - type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::call] - impl Pallet { - - /// Claim a few tokens from the faucet - #[pallet::weight(1_000_000)] - pub fn claim(origin: OriginFor) -> DispatchResult - { - let caller = ensure_signed(origin)?; - - let _ = T::Currency::deposit_creating(&caller, T::DripAmount::get()); - - Ok(()) - } - } - - // #[pallet::hooks] - // impl Hooks> for Pallet { - // fn on_finalize(_n: T::BlockNumber) {} - // } + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + use super::*; + + /// Pallet's configuration trait. + #[pallet::config] + pub trait Config: frame_system::Config { + /// The currency type in which the faucet provides token + type Currency: Currency; + + /// The amount of tokens that should be created for each call into the faucet + type DripAmount: Get>; + } + + type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + /// Claim a few tokens from the faucet + #[pallet::weight(1_000_000)] + pub fn claim(origin: OriginFor) -> DispatchResult { + let caller = ensure_signed(origin)?; + + let _ = T::Currency::deposit_creating(&caller, T::DripAmount::get()); + + Ok(()) + } + } + + // #[pallet::hooks] + // impl Hooks> for Pallet { + // fn on_finalize(_n: T::BlockNumber) {} + // } } diff --git a/runtime/src/issuance.rs b/runtime/src/issuance.rs index c6140462..f71e37da 100644 --- a/runtime/src/issuance.rs +++ b/runtime/src/issuance.rs @@ -1,18 +1,20 @@ /// A trait for types that can provide the amount of issuance to award to the block /// author for the given block number. pub trait Issuance { - fn issuance(block: BlockNumber) -> Balance; + fn issuance(block: BlockNumber) -> Balance; } // Minimal implementations for when you don't actually want any issuance impl Issuance for () { - fn issuance(_block: u32) -> u128 { - 0 - } + fn issuance(_block: u32) -> u128 { + 0 + } } impl Issuance for () { - fn issuance(_block: u64) -> u128 { 0 } + fn issuance(_block: u64) -> u128 { + 0 + } } /// A type that provides block issuance according to bitcoin's rules @@ -27,16 +29,15 @@ const HALVING_INTERVAL: u32 = 210_000; const INITIAL_ISSUANCE: u32 = 50; impl Issuance for BitcoinHalving { + fn issuance(block: u32) -> u128 { + let halvings = block / HALVING_INTERVAL; + // Force block reward to zero when right shift is undefined. + if halvings >= 64 { + return 0; + } - fn issuance(block: u32) -> u128 { - let halvings = block / HALVING_INTERVAL; - // Force block reward to zero when right shift is undefined. - if halvings >= 64 { - return 0; - } - - // Subsidy is cut in half every 210,000 blocks which will occur - // approximately every 4 years. - (INITIAL_ISSUANCE >> halvings).into() - } + // Subsidy is cut in half every 210,000 blocks which will occur + // approximately every 4 years. + (INITIAL_ISSUANCE >> halvings).into() + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 6c500641..e3520ea1 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2,53 +2,45 @@ #![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"] +#![recursion_limit = "256"] // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use frame_support::traits::Currency; -use issuance::Issuance; -use sp_std::prelude::*; -use sp_core::{OpaqueMetadata, U256}; -use sp_runtime::{ - ApplyExtrinsicResult, - create_runtime_str, - generic, - MultiSignature, - traits::{ - AccountIdLookup, - BlakeTwo256, - Block as BlockT, - IdentifyAccount, - Verify, - }, - transaction_validity::{ - TransactionValidity, - TransactionSource, - }, +pub use frame_support::{ + construct_runtime, + dispatch::Callable, + parameter_types, + traits::{ConstU128, ConstU32, IsSubType}, + weights::{ + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, + }, + Weight, + }, + StorageValue, }; +use issuance::Issuance; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_timestamp::Call as TimestampCall; use sp_api::impl_runtime_apis; -use sp_version::RuntimeVersion; -#[cfg(feature = "std")] -use sp_version::NativeVersion; - +use sp_core::{OpaqueMetadata, U256}; // A few exports that help ease life for downstream crates. #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -pub use pallet_timestamp::Call as TimestampCall; -pub use pallet_balances::Call as BalancesCall; -pub use sp_runtime::{Permill, Perbill}; -pub use frame_support::{ - StorageValue, construct_runtime, parameter_types, - dispatch::Callable, - traits::{IsSubType, ConstU128, ConstU32}, - weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, - Weight, - }, +use sp_runtime::{ + create_runtime_str, generic, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, }; +pub use sp_runtime::{Perbill, Permill}; +use sp_std::prelude::*; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; /// An index to a block. pub type BlockNumber = u32; @@ -89,135 +81,135 @@ pub mod faucet; /// of data like extrinsics, allowing for them to continue syncing the network through upgrades /// to even the core data structures. pub mod opaque { - use super::*; + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; - pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + use super::*; - /// 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; + /// 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; } /// This runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("academy-pow"), - impl_name: create_runtime_str!("academy-pow"), - authoring_version: 1, - spec_version: 1, - impl_version: 1, - apis: RUNTIME_API_VERSIONS, - transaction_version: 1, - state_version: 1, + spec_name: create_runtime_str!("academy-pow"), + impl_name: create_runtime_str!("academy-pow"), + authoring_version: 1, + spec_version: 1, + 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(), - } + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } } const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); parameter_types! { - pub const BlockHashCount: BlockNumber = 2400; - pub const Version: RuntimeVersion = VERSION; - /// We allow for 2 seconds of compute with a 6 second average block time. - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::with_sensible_defaults( - Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), - NORMAL_DISPATCH_RATIO, - ); - pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength - ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); - pub const SS58Prefix: u8 = 42; + pub const BlockHashCount: BlockNumber = 2400; + pub const Version: RuntimeVersion = VERSION; + /// We allow for 2 seconds of compute with a 6 second average block time. + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::with_sensible_defaults( + Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + NORMAL_DISPATCH_RATIO, + ); + pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength + ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub const SS58Prefix: u8 = 42; } impl frame_system::Config for Runtime { - /// The basic call filter to use in dispatchable. - type BaseCallFilter = frame_support::traits::Everything; - /// Block & extrinsics weights: base values and limits. - type BlockWeights = BlockWeights; - /// The maximum length of a block (in bytes). - type BlockLength = BlockLength; - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The aggregated dispatch type that is available for extrinsics. - type RuntimeCall = RuntimeCall; - /// The lookup mechanism to get account ID from whatever is passed in dispatchers. - type Lookup = AccountIdLookup; - /// 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 RuntimeEvent = RuntimeEvent; - /// The ubiquitous origin type. - type RuntimeOrigin = RuntimeOrigin; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). - type BlockHashCount = BlockHashCount; - /// The weight of database operations that the runtime can invoke. - type DbWeight = RocksDbWeight; - /// Version of the runtime. - type Version = Version; - /// Converts a module to the index of the module in `construct_runtime!`. - /// - /// This type is being generated by `construct_runtime!`. - type PalletInfo = PalletInfo; - /// What to do if a new account is created. - type OnNewAccount = (); - /// What to do if an account is fully reaped from the system. - type OnKilledAccount = (); - /// The data to be stored in an account. - type AccountData = pallet_balances::AccountData; - /// Weight information for the extrinsics of this pallet. - type SystemWeightInfo = (); - /// This is used as an identifier of the chain. 42 is the generic substrate prefix. - type SS58Prefix = SS58Prefix; - /// The set code logic, just the default since we're not a parachain. - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; + /// The basic call filter to use in dispatchable. + type BaseCallFilter = frame_support::traits::Everything; + /// Block & extrinsics weights: base values and limits. + type BlockWeights = BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = BlockLength; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// 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 RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// Version of the runtime. + type Version = Version; + /// Converts a module to the index of the module in `construct_runtime!`. + /// + /// This type is being generated by `construct_runtime!`. + type PalletInfo = PalletInfo; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The set code logic, just the default since we're not a parachain. + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; } parameter_types! { - pub const MinimumPeriod: u64 = 1000; + pub const MinimumPeriod: u64 = 1000; } impl pallet_timestamp::Config for Runtime { - /// A timestamp: milliseconds since the unix epoch. - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); } impl pallet_balances::Config for Runtime { - type MaxLocks = ConstU32<50>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - /// The type for recording an account's balance. - type Balance = Balance; - /// The ubiquitous event type. - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU128<500>; - type AccountStore = System; - type WeightInfo = pallet_balances::weights::SubstrateWeight; - type FreezeIdentifier = (); - type MaxFreezes = (); - type HoldIdentifier = (); - type MaxHolds = (); + type MaxLocks = ConstU32<50>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU128<500>; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type FreezeIdentifier = (); + type MaxFreezes = (); + type HoldIdentifier = (); + type MaxHolds = (); } // impl pallet_sudo::Config for Runtime { @@ -226,59 +218,60 @@ impl pallet_balances::Config for Runtime { // } parameter_types! { - pub const TargetBlockTime: u128 = 3_000; - pub const DampFactor: u128 = 3; - pub const ClampFactor: u128 = 2; - pub const MaxDifficulty: u128 = u128::max_value(); + pub const TargetBlockTime: u128 = 3_000; + pub const DampFactor: u128 = 3; + pub const ClampFactor: u128 = 2; + pub const MaxDifficulty: u128 = u128::max_value(); } impl difficulty::Config for Runtime { - type TimeProvider = Timestamp; - type TargetBlockTime = TargetBlockTime; - type DampFactor = DampFactor; - type ClampFactor = ClampFactor; - type MaxDifficulty = MaxDifficulty; - // Setting min difficulty to damp factor per recommendation - type MinDifficulty = DampFactor; + type TimeProvider = Timestamp; + type TargetBlockTime = TargetBlockTime; + type DampFactor = DampFactor; + type ClampFactor = ClampFactor; + type MaxDifficulty = MaxDifficulty; + // Setting min difficulty to damp factor per recommendation + type MinDifficulty = DampFactor; } impl faucet::Config for Runtime { - // type Event = Event; - type Currency = Balances; + // type Event = Event; + type Currency = Balances; - // Each drip of the faucet give 5 tokens (with 12 decimals) - type DripAmount = ConstU128<5_000_000_000_000>; + // Each drip of the faucet give 5 tokens (with 12 decimals) + type DripAmount = ConstU128<5_000_000_000_000>; } impl block_author::Config for Runtime { - // Issue some new tokens to the block author - fn on_author_set(author_account: Self::AccountId) { - let block = System::block_number(); - let issuance = >::issuance(block); - // 12 decimals... right? - let issuance = issuance * 1_000_000_000_000; - // sp_std::if_std!{ - // println!("Depositing {issuance} into {author_account}"); - // } - let _ = Balances::deposit_creating(&author_account, issuance); - } + // Issue some new tokens to the block author + fn on_author_set(author_account: Self::AccountId) { + let block = System::block_number(); + let issuance = + >::issuance(block); + // 12 decimals... right? + let issuance = issuance * 1_000_000_000_000; + // sp_std::if_std!{ + // println!("Depositing {issuance} into {author_account}"); + // } + let _ = Balances::deposit_creating(&author_account, issuance); + } } construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = opaque::Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: frame_system, - // TODO remove timestamp silly - Timestamp: pallet_timestamp, - Balances: pallet_balances, - // Sudo: pallet_sudo, - DifficultyAdjustment: difficulty, - BlockAuthor: block_author, - Faucet: faucet, - } + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system, + // TODO remove timestamp silly + Timestamp: pallet_timestamp, + Balances: pallet_balances, + // Sudo: pallet_sudo, + DifficultyAdjustment: difficulty, + BlockAuthor: block_author, + Faucet: faucet, + } ); /// The address format for describing accounts. @@ -291,105 +284,106 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, ); /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, >; impl_runtime_apis! { - impl sp_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 sp_api::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() -> sp_std::vec::Vec { - Runtime::metadata_versions() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { - Executive::apply_extrinsic(extrinsic) - } - - fn finalize_block() -> ::Header { - Executive::finalize_block() - } - - fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { - data.create_extrinsics() - } - - fn check_inherents( - block: Block, - data: sp_inherents::InherentData, - ) -> sp_inherents::CheckInherentsResult { - data.check_extrinsics(&block) - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - source: TransactionSource, - tx: ::Extrinsic, - block_hash: ::Hash, - ) -> TransactionValidity { - Executive::validate_transaction(source, tx, block_hash) - } - } - - impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(header: &::Header) { - Executive::offchain_worker(header) - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_seed: Option>) -> Vec { - Vec::new() - } - - fn decode_session_keys( - _encoded: Vec, - ) -> Option, sp_core::crypto::KeyTypeId)>> { - None - } - } - - impl sp_consensus_pow::DifficultyApi for Runtime { - fn difficulty() -> U256 { - DifficultyAdjustment::difficulty() - } - } + impl sp_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 sp_api::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() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(_seed: Option>) -> Vec { + Vec::new() + } + + fn decode_session_keys( + _encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + None + } + } + + impl sp_consensus_pow::DifficultyApi for Runtime { + fn difficulty() -> U256 { + DifficultyAdjustment::difficulty() + } + } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..d5b89921 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +channel = "stable" +profile = "minimal" +targets = [ "wasm32-unknown-unknown" ] +components = [ "rustfmt", "clippy" ] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..af85000b --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,7 @@ +edition = "2021" +use_field_init_shorthand = true +reorder_modules = true + +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +reorder_imports = true