mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 09:51:02 +00:00
Traitify Runtime (#104)
* Factor out safe-mix and dispatch * Refactor dispatch into something more modular. * Fix wasm build. * Fix up timestamp * fix warnings. * Borked timestamp example * Fix build * Timestamp as skeleton for traity runtime. * New storage macro. * Dispatch module has traity API. * Move consensus module to new API * Refactoring and outer dispatch * Avoid unnecessary derives. * Abstract the low-level half of system. * nicer outer dispatch syntax. * Make runtime compile again (albeit in a heavily simplified state) * Reworking runtime and the upper levels of system. * Initial reworking of runtime: - Introduced executive module; - Introduced trait primitives module; - Provided an API endpoint. * Expose an additional function in system * Another couple of functions traitified in executive. * another function in executive traitified. * One more function traitified. * Finish traitifying executive! * Traitify session module. * Cleanups and ensure session gets run. * First part of traitification of staking module. * Bit more of staking traitified. * Additional stuff in staking. Fix up session. * Penultimate part of staking module. * Final part of staking (code) * Update demo runtime to include staking. * Final tweaks for staking integration. * Remove old runtime files. * Schedule staking. * Minor fixes * First bits of democracy. * Democracy module integrated. * Fix warning. * Traitify and integrate council module * Council voting. * Runtime binary and tweaks. * Binary update. * Fix `*Type` grumble. * Fix up genesis_map * Remove NonTrivialSlicable * Staking "test externalities" stuff along with refactor. * Add session test externalities constructor * Fixed executor tests. * Make one test in executive module work. * Remove test framework stuff into common module. * Enable other tests in executive * Session tests reinstated, minor refactoring of keyring. * Fix staking tests. * Fix up democracy tests. * First few tests in council. * Council tests reinstated :) * Avoid hardcoding blake2 into Header. * Fix last few tests. * Make all primitives generic. * Fix tests. * Refactor runtime to remove genesismap. * Streamline runtime more with macrofied config. * Clean paths * Fix warning. * Consolidate demo runtime crate. * Remove stale code. * Refactor away dodgy trait. * Add corresponding Aux type. * Fixes * Rename Digesty -> Digest * Rename Headery -> Header * Blocky -> Block * Fix wasm build. * kill warnings * more docs * minor cleanups
This commit is contained in:
Generated
+183
-2
@@ -228,7 +228,10 @@ dependencies = [
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-state-machine 0.1.0",
|
||||
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -242,6 +245,7 @@ dependencies = [
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-serializer 0.1.0",
|
||||
]
|
||||
@@ -252,17 +256,26 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"demo-primitives 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"integer-sqrt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-council 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
"substrate-runtime-executive 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -971,6 +984,11 @@ name = "num-traits"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.8.0"
|
||||
@@ -1162,6 +1180,7 @@ dependencies = [
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-network 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1491,6 +1510,13 @@ dependencies = [
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "safe-mix"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.2.0"
|
||||
@@ -1633,6 +1659,7 @@ dependencies = [
|
||||
"substrate-executor 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1780,6 +1807,78 @@ dependencies = [
|
||||
"substrate-rpc 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-consensus"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-council"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"integer-sqrt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-democracy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-executive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-io"
|
||||
version = "0.1.0"
|
||||
@@ -1794,6 +1893,58 @@ dependencies = [
|
||||
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"integer-sqrt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-session"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-staking"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-std"
|
||||
version = "0.1.0"
|
||||
@@ -1808,14 +1959,43 @@ name = "substrate-runtime-support"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"environmental 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-timestamp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-serializer"
|
||||
version = "0.1.0"
|
||||
@@ -2302,6 +2482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
|
||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
||||
"checksum num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9936036cc70fe4a8b2d338ab665900323290efb03983c86cbe235ae800ad8017"
|
||||
"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364"
|
||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||
"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22"
|
||||
"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
|
||||
|
||||
@@ -39,6 +39,15 @@ members = [
|
||||
"substrate/runtime-io",
|
||||
"substrate/runtime-std",
|
||||
"substrate/runtime-support",
|
||||
"substrate/runtime/consensus",
|
||||
"substrate/runtime/council",
|
||||
"substrate/runtime/democracy",
|
||||
"substrate/runtime/executive",
|
||||
"substrate/runtime/primitives",
|
||||
"substrate/runtime/session",
|
||||
"substrate/runtime/staking",
|
||||
"substrate/runtime/system",
|
||||
"substrate/runtime/timestamp",
|
||||
"substrate/serializer",
|
||||
"substrate/state-machine",
|
||||
"substrate/test-runtime",
|
||||
@@ -46,6 +55,7 @@ members = [
|
||||
"demo/primitives",
|
||||
"demo/executor",
|
||||
"demo/cli",
|
||||
"safe-mix",
|
||||
]
|
||||
exclude = [
|
||||
"polkadot/runtime/wasm",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd demo/runtime/wasm && ./build.sh && cd ../../..
|
||||
cd substrate/executor/wasm && ./build.sh && cd ../../..
|
||||
cd substrate/test-runtime/wasm && ./build.sh && cd ../../..
|
||||
cd polkadot/runtime/wasm && ./build.sh && cd ../../..
|
||||
cd demo/runtime/wasm && ./build.sh && cd ../../..
|
||||
|
||||
@@ -22,6 +22,7 @@ extern crate env_logger;
|
||||
extern crate ed25519;
|
||||
extern crate triehash;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_state_machine as state_machine;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_primitives as primitives;
|
||||
@@ -43,7 +44,9 @@ pub mod error;
|
||||
|
||||
use std::sync::Arc;
|
||||
use codec::Slicable;
|
||||
use demo_runtime::genesismap::{additional_storage_with_genesis, GenesisConfig};
|
||||
use runtime_io::with_externalities;
|
||||
use demo_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig,
|
||||
SessionConfig, StakingConfig, BuildExternalities};
|
||||
use client::genesis;
|
||||
|
||||
/// Parse command line arguments and start the node.
|
||||
@@ -71,32 +74,52 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
||||
let god_key = hex!["3d866ec8a9190c8343c2fc593d21d8a6d0c5c4763aaab2349de3a6111d64d124"];
|
||||
|
||||
let genesis_config = GenesisConfig {
|
||||
validators: vec![god_key.clone()],
|
||||
authorities: vec![god_key.clone()],
|
||||
balances: vec![(god_key.clone(), 1u64 << 63)].into_iter().collect(),
|
||||
block_time: 5, // 5 second block time.
|
||||
session_length: 720, // that's 1 hour per session.
|
||||
sessions_per_era: 24, // 24 hours per era.
|
||||
bonding_duration: 90, // 90 days per bond.
|
||||
launch_period: 120 * 24 * 14, // 2 weeks per public referendum
|
||||
voting_period: 120 * 24 * 28, // 4 weeks to discuss & vote on an active referendum
|
||||
minimum_deposit: 1000, // 1000 as the minimum deposit for a referendum
|
||||
candidacy_bond: 1000, // 1000 to become a council candidate
|
||||
voter_bond: 100, // 100 down to vote for a candidate
|
||||
present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation.
|
||||
carry_count: 24, // carry over the 24 runners-up to the next council election
|
||||
presentation_duration: 120 * 24, // one day for presenting winners.
|
||||
council_election_voting_period: 7 * 120 * 24, // one week period between possible council elections.
|
||||
council_term_duration: 180 * 120 * 24, // 180 day term duration for the council.
|
||||
desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit.
|
||||
inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped.
|
||||
cooloff_period: 90 * 120 * 24, // 90 day cooling off period if council member vetoes a proposal.
|
||||
council_proposal_voting_period: 7 * 120 * 24, // 7 day voting period for council members.
|
||||
consensus: Some(ConsensusConfig {
|
||||
authorities: vec![god_key.clone()],
|
||||
}),
|
||||
system: None,
|
||||
// block_time: 5, // 5 second block time.
|
||||
session: Some(SessionConfig {
|
||||
validators: vec![god_key.clone()],
|
||||
session_length: 720, // that's 1 hour per session.
|
||||
}),
|
||||
staking: Some(StakingConfig {
|
||||
current_era: 0,
|
||||
intentions: vec![],
|
||||
transaction_fee: 100,
|
||||
balances: vec![(god_key.clone(), 1u64 << 63)].into_iter().collect(),
|
||||
validator_count: 12,
|
||||
sessions_per_era: 24, // 24 hours per era.
|
||||
bonding_duration: 90, // 90 days per bond.
|
||||
}),
|
||||
democracy: Some(DemocracyConfig {
|
||||
launch_period: 120 * 24 * 14, // 2 weeks per public referendum
|
||||
voting_period: 120 * 24 * 28, // 4 weeks to discuss & vote on an active referendum
|
||||
minimum_deposit: 1000, // 1000 as the minimum deposit for a referendum
|
||||
}),
|
||||
council: Some(CouncilConfig {
|
||||
active_council: vec![],
|
||||
candidacy_bond: 1000, // 1000 to become a council candidate
|
||||
voter_bond: 100, // 100 down to vote for a candidate
|
||||
present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation.
|
||||
carry_count: 24, // carry over the 24 runners-up to the next council election
|
||||
presentation_duration: 120 * 24, // one day for presenting winners.
|
||||
approval_voting_period: 7 * 120 * 24, // one week period between possible council elections.
|
||||
term_duration: 180 * 120 * 24, // 180 day term duration for the council.
|
||||
desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit.
|
||||
inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped.
|
||||
|
||||
cooloff_period: 90 * 120 * 24, // 90 day cooling off period if council member vetoes a proposal.
|
||||
voting_period: 7 * 120 * 24, // 7 day voting period for council members.
|
||||
}),
|
||||
};
|
||||
let prepare_genesis = || {
|
||||
storage = genesis_config.genesis_map();
|
||||
storage = genesis_config.build_externalities();
|
||||
let block = genesis::construct_genesis_block(&storage);
|
||||
storage.extend(additional_storage_with_genesis(&block));
|
||||
with_externalities(&mut storage, ||
|
||||
// TODO: use api.rs to dispatch instead
|
||||
demo_runtime::System::initialise_genesis_state(&block.header)
|
||||
);
|
||||
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
|
||||
};
|
||||
let client = Arc::new(client::new_in_mem(executor, prepare_genesis)?);
|
||||
|
||||
@@ -19,3 +19,6 @@ demo-runtime = { path = "../runtime" }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
||||
substrate-runtime-staking = { path = "../../substrate/runtime/staking" }
|
||||
substrate-runtime-system = { path = "../../substrate/runtime/system" }
|
||||
|
||||
@@ -28,7 +28,10 @@ extern crate ed25519;
|
||||
extern crate triehash;
|
||||
|
||||
#[cfg(test)] extern crate substrate_keyring as keyring;
|
||||
#[cfg(test)] extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
#[cfg(test)] extern crate substrate_runtime_support as runtime_support;
|
||||
#[cfg(test)] extern crate substrate_runtime_staking as staking;
|
||||
#[cfg(test)] extern crate substrate_runtime_system as system;
|
||||
#[cfg(test)] #[macro_use] extern crate hex_literal;
|
||||
|
||||
native_executor_instance!(pub Executor, demo_runtime::api::dispatch, include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm"));
|
||||
@@ -43,12 +46,11 @@ mod tests {
|
||||
use runtime_support::{Hashable, StorageValue, StorageMap};
|
||||
use state_machine::{CodeExecutor, TestExternalities};
|
||||
use primitives::twox_128;
|
||||
use demo_primitives::{Hash, Header, BlockNumber, Digest};
|
||||
use demo_runtime::transaction::{Transaction, UncheckedTransaction};
|
||||
use demo_runtime::block::Block;
|
||||
use demo_runtime::runtime::staking::{FreeBalanceOf, balance};
|
||||
use demo_runtime::runtime::{staking, system};
|
||||
use demo_runtime::dispatch;
|
||||
use demo_primitives::{Hash, BlockNumber};
|
||||
use runtime_primitives::traits::Header as HeaderT;
|
||||
use {staking, system};
|
||||
use demo_runtime::{Header, Block, UncheckedExtrinsic, Extrinsic, Call, Concrete, Staking,
|
||||
BuildExternalities, GenesisConfig, SessionConfig, StakingConfig};
|
||||
use ed25519::{Public, Pair};
|
||||
|
||||
const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
@@ -61,121 +63,145 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
fn tx() -> UncheckedTransaction {
|
||||
let transaction = Transaction {
|
||||
fn xt() -> UncheckedExtrinsic {
|
||||
let extrinsic = Extrinsic {
|
||||
signed: Alice.into(),
|
||||
nonce: 0,
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Bob.into(), 69)),
|
||||
index: 0,
|
||||
function: Call::Staking(staking::Call::transfer::<Concrete>(Bob.into(), 69)),
|
||||
};
|
||||
let signature = Keyring::from_raw_public(transaction.signed).unwrap()
|
||||
.sign(&transaction.encode());
|
||||
let signature = Keyring::from_raw_public(extrinsic.signed).unwrap()
|
||||
.sign(&extrinsic.encode()).into();
|
||||
|
||||
UncheckedTransaction { transaction, signature }
|
||||
UncheckedExtrinsic { extrinsic, signature }
|
||||
}
|
||||
|
||||
fn from_block_number(n: u64) -> Header {
|
||||
Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&system::BlockHashAt::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "initialise_block", &vec![].and(&Header::from_block_number(1u64)));
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)));
|
||||
assert!(r.is_ok());
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&tx()));
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "apply_extrinsic", &vec![].and(&xt()));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_with_native_equivalent_code_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&system::BlockHashAt::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "initialise_block", &vec![].and(&Header::from_block_number(1u64)));
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)));
|
||||
assert!(r.is_ok());
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&tx()));
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "apply_extrinsic", &vec![].and(&xt()));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&system::BlockHashAt::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "initialise_block", &vec![].and(&Header::from_block_number(1u64)));
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)));
|
||||
assert!(r.is_ok());
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&tx()));
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "apply_extrinsic", &vec![].and(&xt()));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
assert_eq!(Staking::balance(&Alice), 42);
|
||||
assert_eq!(Staking::balance(&Bob), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_foreign_code_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&system::BlockHashAt::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "initialise_block", &vec![].and(&Header::from_block_number(1u64)));
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)));
|
||||
assert!(r.is_ok());
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&tx()));
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "apply_extrinsic", &vec![].and(&xt()));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
assert_eq!(Staking::balance(&Alice), 42);
|
||||
assert_eq!(Staking::balance(&Bob), 69);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
staking::testing::externalities(2, 2, 0)
|
||||
use keyring::Keyring::*;
|
||||
let three = [3u8; 32];
|
||||
GenesisConfig {
|
||||
consensus: Some(Default::default()),
|
||||
system: Some(Default::default()),
|
||||
session: Some(SessionConfig {
|
||||
session_length: 2,
|
||||
validators: vec![One.into(), Two.into(), three],
|
||||
}),
|
||||
staking: Some(StakingConfig {
|
||||
sessions_per_era: 2,
|
||||
current_era: 0,
|
||||
balances: vec![(Alice.into(), 111)],
|
||||
intentions: vec![Alice.into(), Bob.into(), Charlie.into()],
|
||||
validator_count: 3,
|
||||
bonding_duration: 0,
|
||||
transaction_fee: 1,
|
||||
}),
|
||||
democracy: Some(Default::default()),
|
||||
council: Some(Default::default()),
|
||||
}.build_externalities()
|
||||
}
|
||||
|
||||
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transaction>) -> (Vec<u8>, Hash) {
|
||||
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec<Extrinsic>) -> (Vec<u8>, Hash) {
|
||||
use triehash::ordered_trie_root;
|
||||
|
||||
let transactions = txs.into_iter().map(|transaction| {
|
||||
let signature = Pair::from(Keyring::from_public(Public::from_raw(transaction.signed)).unwrap())
|
||||
.sign(&transaction.encode());
|
||||
let extrinsics = extrinsics.into_iter().map(|extrinsic| {
|
||||
let signature = Pair::from(Keyring::from_public(Public::from_raw(extrinsic.signed)).unwrap())
|
||||
.sign(&extrinsic.encode()).into();
|
||||
|
||||
UncheckedTransaction { transaction, signature }
|
||||
UncheckedExtrinsic { extrinsic, signature }
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let transaction_root = ordered_trie_root(transactions.iter().map(Slicable::encode)).0.into();
|
||||
let extrinsics_root = ordered_trie_root(extrinsics.iter().map(Slicable::encode)).0.into();
|
||||
|
||||
let header = Header {
|
||||
parent_hash,
|
||||
number,
|
||||
state_root,
|
||||
transaction_root,
|
||||
digest: Digest { logs: vec![], },
|
||||
extrinsics_root,
|
||||
digest: Default::default(),
|
||||
};
|
||||
let hash = header.blake2_256();
|
||||
|
||||
(Block { header, transactions }.encode(), hash.into())
|
||||
(Block { header, extrinsics }.encode(), hash.into())
|
||||
}
|
||||
|
||||
fn block1() -> (Vec<u8>, Hash) {
|
||||
construct_block(
|
||||
1,
|
||||
[69u8; 32].into(),
|
||||
hex!("7a388ce5b4eeadbb9268ae96e8822b223f4fd1841327d99f4e1c21fad81f97f2").into(),
|
||||
vec![Transaction {
|
||||
hex!("4d58afeca0dec7604a0bcfb29573e6ad202efe65c8535b013c0c79b5a8c9114d").into(),
|
||||
vec![Extrinsic {
|
||||
signed: Alice.into(),
|
||||
nonce: 0,
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Bob.into(), 69)),
|
||||
index: 0,
|
||||
function: Call::Staking(staking::Call::transfer(Bob.into(), 69)),
|
||||
}]
|
||||
)
|
||||
}
|
||||
@@ -184,17 +210,17 @@ mod tests {
|
||||
construct_block(
|
||||
2,
|
||||
block1().1,
|
||||
hex!("e4eb71be8b816f2061f32f284e9b429562cdc1b82f11725e5f965ff23439f5e9").into(),
|
||||
hex!("218ce7bdf2350713aa52dbf0a12d1e8b0a3a9f1623e7c95aa4800886e96397d1").into(),
|
||||
vec![
|
||||
Transaction {
|
||||
Extrinsic {
|
||||
signed: Bob.into(),
|
||||
nonce: 0,
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Alice.into(), 5)),
|
||||
index: 0,
|
||||
function: Call::Staking(staking::Call::transfer(Alice.into(), 5)),
|
||||
},
|
||||
Transaction {
|
||||
Extrinsic {
|
||||
signed: Alice.into(),
|
||||
nonce: 1,
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Bob.into(), 15)),
|
||||
index: 1,
|
||||
function: Call::Staking(staking::Call::transfer(Bob.into(), 15)),
|
||||
}
|
||||
]
|
||||
)
|
||||
@@ -207,15 +233,15 @@ mod tests {
|
||||
Executor::new().call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 41);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
assert_eq!(Staking::balance(&Alice), 41);
|
||||
assert_eq!(Staking::balance(&Bob), 69);
|
||||
});
|
||||
|
||||
Executor::new().call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 30);
|
||||
assert_eq!(balance(&Bob), 78);
|
||||
assert_eq!(Staking::balance(&Alice), 30);
|
||||
assert_eq!(Staking::balance(&Bob), 78);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,50 +252,50 @@ mod tests {
|
||||
WasmExecutor.call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 41);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
assert_eq!(Staking::balance(&Alice), 41);
|
||||
assert_eq!(Staking::balance(&Bob), 69);
|
||||
});
|
||||
|
||||
WasmExecutor.call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 30);
|
||||
assert_eq!(balance(&Bob), 78);
|
||||
assert_eq!(Staking::balance(&Alice), 30);
|
||||
assert_eq!(Staking::balance(&Bob), 78);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&system::BlockHashAt::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&Header::from_block_number(1u64)));
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64)));
|
||||
assert!(r.is_ok());
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&tx()));
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt()));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&system::BlockHashAt::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm");
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&Header::from_block_number(1u64)));
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64)));
|
||||
assert!(r.is_ok());
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&tx()));
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt()));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
assert_eq!(Staking::balance(&Alice), 42);
|
||||
assert_eq!(Staking::balance(&Bob), 69);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ serde_derive = { version = "1.0", optional = true }
|
||||
substrate-codec = { path = "../../substrate/codec", default_features = false }
|
||||
substrate-primitives = { path = "../../substrate/primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../substrate/runtime-std", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-serializer = { path = "../../substrate/serializer" }
|
||||
@@ -20,6 +21,7 @@ std = [
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"serde_derive",
|
||||
"serde/std",
|
||||
]
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block and header type definitions.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use primitives::bytes;
|
||||
use primitives::H256;
|
||||
use rstd::vec::Vec;
|
||||
use codec::{Input, Slicable};
|
||||
|
||||
pub use primitives::block::Id;
|
||||
|
||||
/// Used to refer to a block number.
|
||||
pub type Number = u64;
|
||||
|
||||
/// Hash used to refer to a block hash.
|
||||
pub type HeaderHash = H256;
|
||||
|
||||
/// Execution log (event)
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Log(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8>);
|
||||
|
||||
impl Slicable for Log {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Vec::<u8>::decode(input).map(Log)
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.0.using_encoded(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for Log { }
|
||||
|
||||
/// The digest of a block, useful for light-clients.
|
||||
#[derive(Clone, Default, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Digest {
|
||||
/// All logs that have happened in the block.
|
||||
pub logs: Vec<Log>,
|
||||
}
|
||||
|
||||
impl Slicable for Digest {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Vec::<Log>::decode(input).map(|logs| Digest { logs })
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.logs.using_encoded(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Header for a block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Header {
|
||||
/// Block parent's hash.
|
||||
pub parent_hash: HeaderHash,
|
||||
/// Block number.
|
||||
pub number: Number,
|
||||
/// State root after this transition.
|
||||
pub state_root: H256,
|
||||
/// The root of the trie that represents this block's transactions, indexed by a 32-byte integer.
|
||||
pub transaction_root: H256,
|
||||
/// The digest of activity on the block.
|
||||
pub digest: Digest,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// Create a new instance with default fields except `number`, which is given as an argument.
|
||||
pub fn from_block_number(number: Number) -> Self {
|
||||
Header {
|
||||
parent_hash: Default::default(),
|
||||
number,
|
||||
state_root: Default::default(),
|
||||
transaction_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Header {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
parent_hash: try_opt!(Slicable::decode(input)),
|
||||
number: try_opt!(Slicable::decode(input)),
|
||||
state_root: try_opt!(Slicable::decode(input)),
|
||||
transaction_root: try_opt!(Slicable::decode(input)),
|
||||
digest: try_opt!(Slicable::decode(input)),
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
self.parent_hash.using_encoded(|s| v.extend(s));
|
||||
self.number.using_encoded(|s| v.extend(s));
|
||||
self.state_root.using_encoded(|s| v.extend(s));
|
||||
self.transaction_root.using_encoded(|s| v.extend(s));
|
||||
self.digest.using_encoded(|s| v.extend(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::Slicable;
|
||||
use substrate_serializer as ser;
|
||||
|
||||
#[test]
|
||||
fn test_header_serialization() {
|
||||
let header = Header {
|
||||
parent_hash: 5.into(),
|
||||
number: 67,
|
||||
state_root: 3.into(),
|
||||
transaction_root: 6.into(),
|
||||
digest: Digest { logs: vec![Log(vec![1])] },
|
||||
};
|
||||
|
||||
assert_eq!(ser::to_string_pretty(&header), r#"{
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005",
|
||||
"number": 67,
|
||||
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003",
|
||||
"transactionRoot": "0x0000000000000000000000000000000000000000000000000000000000000006",
|
||||
"digest": {
|
||||
"logs": [
|
||||
"0x01"
|
||||
]
|
||||
}
|
||||
}"#);
|
||||
|
||||
let v = header.encode();
|
||||
assert_eq!(Header::decode(&mut &v[..]).unwrap(), header);
|
||||
}
|
||||
}
|
||||
@@ -21,47 +21,32 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
#[cfg(feature = "std")] extern crate serde;
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate substrate_primitives as primitives;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_serializer;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
|
||||
macro_rules! try_opt {
|
||||
($e: expr) => {
|
||||
match $e {
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod block;
|
||||
|
||||
pub use self::block::{Header, Log, Digest};
|
||||
pub use self::block::Number as BlockNumber;
|
||||
/// An index to a block.
|
||||
pub type BlockNumber = u64;
|
||||
|
||||
/// Alias to Ed25519 pubkey that identifies an account on the relay chain. This will almost
|
||||
/// certainly continue to be the same as the substrate's `AuthorityId`.
|
||||
pub type AccountId = primitives::AuthorityId;
|
||||
|
||||
/// Balance of an account.
|
||||
pub type Balance = u64;
|
||||
|
||||
/// The Ed25519 pub key of an session that belongs to an authority of the relay chain. This is
|
||||
/// exactly equivalent to what the substrate calls an "authority".
|
||||
pub type SessionKey = primitives::AuthorityId;
|
||||
|
||||
/// Index of a transaction in the relay chain.
|
||||
pub type TxOrder = u64;
|
||||
pub type Index = u64;
|
||||
|
||||
/// A hash of some data used by the relay chain.
|
||||
pub type Hash = primitives::H256;
|
||||
|
||||
/// Alias to 520-bit hash when used in the context of a signature on the relay chain.
|
||||
pub type Signature = primitives::hash::H512;
|
||||
/// Alias to 512-bit hash when used in the context of a signature on the relay chain.
|
||||
pub type Signature = runtime_primitives::traits::Ed25519Signature;
|
||||
|
||||
@@ -9,25 +9,44 @@ hex-literal = "0.1.0"
|
||||
log = { version = "0.3", optional = true }
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { path = "../../safe-mix", default_features = false}
|
||||
substrate-codec = { path = "../../substrate/codec" }
|
||||
substrate-runtime-std = { path = "../../substrate/runtime-std" }
|
||||
substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
||||
substrate-runtime-support = { path = "../../substrate/runtime-support" }
|
||||
substrate-primitives = { path = "../../substrate/primitives" }
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
substrate-runtime-consensus = { path = "../../substrate/runtime/consensus" }
|
||||
substrate-runtime-council = { path = "../../substrate/runtime/council" }
|
||||
substrate-runtime-democracy = { path = "../../substrate/runtime/democracy" }
|
||||
substrate-runtime-executive = { path = "../../substrate/runtime/executive" }
|
||||
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
||||
substrate-runtime-session = { path = "../../substrate/runtime/session" }
|
||||
substrate-runtime-staking = { path = "../../substrate/runtime/staking" }
|
||||
substrate-runtime-system = { path = "../../substrate/runtime/system" }
|
||||
substrate-runtime-timestamp = { path = "../../substrate/runtime/timestamp" }
|
||||
demo-primitives = { path = "../primitives" }
|
||||
integer-sqrt = "0.1.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-council/std",
|
||||
"substrate-runtime-democracy/std",
|
||||
"substrate-runtime-executive/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-system/std",
|
||||
"substrate-runtime-timestamp/std",
|
||||
"demo-primitives/std",
|
||||
"serde_derive",
|
||||
"serde/std",
|
||||
"log"
|
||||
"log",
|
||||
"safe-mix/std"
|
||||
]
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use runtime::{system, consensus, session};
|
||||
|
||||
impl_stubs!(
|
||||
execute_block => |block| system::internal::execute_block(block),
|
||||
initialise_block => |header| system::internal::initialise_block(&header),
|
||||
execute_transaction => |utx| system::internal::execute_transaction(utx),
|
||||
finalise_block => |()| system::internal::finalise_block(),
|
||||
validator_count => |()| session::validator_count(),
|
||||
validators => |()| session::validators(),
|
||||
authorities => |()| consensus::authorities()
|
||||
);
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block and header type definitions.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Input, Slicable};
|
||||
use transaction::UncheckedTransaction;
|
||||
|
||||
pub use demo_primitives::block::{Header, Digest, Log, Number, HeaderHash};
|
||||
|
||||
/// The block "body": A bunch of transactions.
|
||||
pub type Body = Vec<UncheckedTransaction>;
|
||||
|
||||
/// A block on the chain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Block {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// All relay-chain transactions.
|
||||
pub transactions: Body,
|
||||
}
|
||||
|
||||
impl Slicable for Block {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let (header, transactions) = Slicable::decode(input)?;
|
||||
Some(Block { header, transactions })
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
v.extend(self.header.encode());
|
||||
v.extend(self.transactions.encode());
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Dispatch system. Just dispatches calls.
|
||||
|
||||
use runtime::{staking, democracy};
|
||||
pub use rstd::prelude::Vec;
|
||||
pub use codec::{Slicable, Input, NonTrivialSlicable};
|
||||
|
||||
/// Implement a dispatch module to create a pairing of a dispatch trait and enum.
|
||||
#[macro_export]
|
||||
macro_rules! impl_dispatch {
|
||||
(
|
||||
pub mod $mod_name:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
)
|
||||
= $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
pub mod $mod_name {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[repr(u32)]
|
||||
#[allow(non_camel_case_types)]
|
||||
enum Id {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name = $id,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Id {
|
||||
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
|
||||
fn from_u8(value: u8) -> Option<Id> {
|
||||
match value {
|
||||
$(
|
||||
$id => Some(Id::$fn_name),
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Call {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* )
|
||||
,)*
|
||||
}
|
||||
|
||||
pub trait Dispatch: Sized {
|
||||
$(
|
||||
fn $fn_name (self, $( $param_name: $param ),* );
|
||||
)*
|
||||
}
|
||||
|
||||
impl Call {
|
||||
pub fn dispatch<D: Dispatch>(self, d: D) {
|
||||
match self {
|
||||
$(
|
||||
Call::$fn_name( $( $param_name ),* ) =>
|
||||
d.$fn_name( $( $param_name ),* ),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::dispatch::Slicable for Call {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
let id = u8::decode(input).and_then(Id::from_u8)?;
|
||||
Some(match id {
|
||||
$(
|
||||
Id::$fn_name => {
|
||||
$(
|
||||
let $param_name = $crate::dispatch::Slicable::decode(input)?;
|
||||
)*
|
||||
Call :: $fn_name( $( $param_name ),* )
|
||||
}
|
||||
)*
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> $crate::dispatch::Vec<u8> {
|
||||
let mut v = $crate::dispatch::Vec::new();
|
||||
match *self {
|
||||
$(
|
||||
Call::$fn_name(
|
||||
$(
|
||||
ref $param_name
|
||||
),*
|
||||
) => {
|
||||
(Id::$fn_name as u8).using_encoded(|s| v.extend(s));
|
||||
$(
|
||||
$param_name.using_encoded(|s| v.extend(s));
|
||||
)*
|
||||
}
|
||||
)*
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(self.encode().as_slice())
|
||||
}
|
||||
}
|
||||
impl $crate::dispatch::NonTrivialSlicable for Call {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_meta_dispatch {
|
||||
(
|
||||
pub mod $super_name:ident;
|
||||
path $path:ident;
|
||||
trait $trait:ty;
|
||||
$(
|
||||
$camelcase:ident(mod $sub_name:ident) = $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
pub mod $super_name {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[repr(u32)]
|
||||
#[allow(non_camel_case_types)]
|
||||
enum Id {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$camelcase = $id,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Id {
|
||||
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
|
||||
fn from_u8(value: u8) -> Option<Id> {
|
||||
match value {
|
||||
$(
|
||||
$id => Some(Id::$camelcase),
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Call {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$camelcase ( $crate::runtime::$sub_name::$path::Call )
|
||||
,)*
|
||||
}
|
||||
|
||||
impl Call {
|
||||
pub fn dispatch(self, d: $trait) {
|
||||
match self {
|
||||
$(
|
||||
Call::$camelcase(x) => x.dispatch(d),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::dispatch::Slicable for Call {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
let id = u8::decode(input).and_then(Id::from_u8)?;
|
||||
Some(match id {
|
||||
$(
|
||||
Id::$camelcase =>
|
||||
Call::$camelcase( $crate::dispatch::Slicable::decode(input)? ),
|
||||
)*
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = $crate::dispatch::Vec::new();
|
||||
match *self {
|
||||
$(
|
||||
Call::$camelcase( ref sub ) => {
|
||||
(Id::$camelcase as u8).using_encoded(|s| v.extend(s));
|
||||
sub.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
)*
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(self.encode().as_slice())
|
||||
}
|
||||
}
|
||||
impl $crate::dispatch::NonTrivialSlicable for Call {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_meta_dispatch! {
|
||||
pub mod public;
|
||||
path public;
|
||||
trait staking::PublicPass;
|
||||
Session(mod session) = 1;
|
||||
Staking(mod staking) = 2;
|
||||
Timestamp(mod timestamp) = 3;
|
||||
Democracy(mod democracy) = 5;
|
||||
Council(mod council) = 6;
|
||||
CouncilVote(mod council) = 7;
|
||||
}
|
||||
|
||||
impl_meta_dispatch! {
|
||||
pub mod privileged;
|
||||
path privileged;
|
||||
trait democracy::PrivPass;
|
||||
System(mod system) = 0;
|
||||
Session(mod session) = 1;
|
||||
Staking(mod staking) = 2;
|
||||
Democracy(mod democracy) = 5;
|
||||
Council(mod council) = 6;
|
||||
CouncilVote(mod council) = 7;
|
||||
}
|
||||
|
||||
pub use self::privileged::Call as PrivCall;
|
||||
pub use self::public::Call as PubCall;
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tool for creating the genesis block.
|
||||
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use std::collections::HashMap;
|
||||
use runtime_io::twox_128;
|
||||
use runtime_support::{Hashable, StorageMap, StorageList, StorageValue};
|
||||
use primitives::Block;
|
||||
use demo_primitives::{BlockNumber, AccountId};
|
||||
use runtime::staking::Balance;
|
||||
use runtime::{staking, session, consensus, system, democracy, council, council_vote};
|
||||
|
||||
/// Configuration of a general Substrate Demo genesis block.
|
||||
pub struct GenesisConfig {
|
||||
pub validators: Vec<AccountId>,
|
||||
pub authorities: Vec<AccountId>,
|
||||
pub balances: Vec<(AccountId, Balance)>,
|
||||
pub block_time: u64,
|
||||
pub session_length: BlockNumber,
|
||||
pub sessions_per_era: BlockNumber,
|
||||
pub bonding_duration: BlockNumber,
|
||||
pub launch_period: BlockNumber,
|
||||
pub voting_period: BlockNumber,
|
||||
pub minimum_deposit: Balance,
|
||||
pub candidacy_bond: Balance,
|
||||
pub voter_bond: Balance,
|
||||
pub present_slash_per_voter: Balance,
|
||||
pub carry_count: u32,
|
||||
pub presentation_duration: BlockNumber,
|
||||
pub council_election_voting_period: BlockNumber,
|
||||
pub council_term_duration: BlockNumber,
|
||||
pub desired_seats: u32,
|
||||
pub inactive_grace_period: BlockNumber,
|
||||
pub cooloff_period: BlockNumber,
|
||||
pub council_proposal_voting_period: BlockNumber,
|
||||
}
|
||||
|
||||
impl GenesisConfig {
|
||||
pub fn new_simple(authorities_validators: Vec<AccountId>, balance: Balance) -> Self {
|
||||
GenesisConfig {
|
||||
validators: authorities_validators.clone(),
|
||||
authorities: authorities_validators.clone(),
|
||||
balances: authorities_validators.iter().map(|v| (v.clone(), balance)).collect(),
|
||||
block_time: 30, // 30 second block time.
|
||||
session_length: 120, // that's 1 hour per session.
|
||||
sessions_per_era: 24, // 24 hours per era.
|
||||
bonding_duration: 90, // 90 days per bond.
|
||||
launch_period: 120 * 24 * 14, // 2 weeks per public referendum
|
||||
voting_period: 120 * 24 * 28, // 4 weeks to discuss & vote on an active referendum
|
||||
minimum_deposit: 1000, // 1000 as the minimum deposit for a referendum
|
||||
candidacy_bond: 1000, // 1000 to become a council candidate
|
||||
voter_bond: 100, // 100 down to vote for a candidate
|
||||
present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation.
|
||||
carry_count: 24, // carry over the 24 runners-up to the next council election
|
||||
presentation_duration: 120 * 24, // one day for presenting winners.
|
||||
council_election_voting_period: 7 * 120 * 24, // one week period between possible council elections.
|
||||
council_term_duration: 180 * 120 * 24, // 180 day term duration for the council.
|
||||
desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit.
|
||||
inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped.
|
||||
cooloff_period: 90 * 120 * 24, // 90 day cooling off period if council member vetoes a proposal.
|
||||
council_proposal_voting_period: 7 * 120 * 24, // 7 day voting period for council members.
|
||||
}
|
||||
}
|
||||
|
||||
pub fn genesis_map(&self) -> HashMap<Vec<u8>, Vec<u8>> {
|
||||
let wasm_runtime = include_bytes!("../wasm/genesis.wasm").to_vec();
|
||||
vec![
|
||||
(session::SessionLength::key(), vec![].and(&self.session_length)),
|
||||
(session::Validators::key(), vec![].and(&self.validators)),
|
||||
|
||||
(&staking::Intention::len_key()[..], vec![].and(&0u32)),
|
||||
(&staking::SessionsPerEra::key()[..], vec![].and(&self.sessions_per_era)),
|
||||
(&staking::CurrentEra::key()[..], vec![].and(&0u64)),
|
||||
|
||||
(democracy::LaunchPeriod::key(), vec![].and(&self.launch_period)),
|
||||
(democracy::VotingPeriod::key(), vec![].and(&self.voting_period)),
|
||||
(democracy::MinimumDeposit::key(), vec![].and(&self.minimum_deposit)),
|
||||
|
||||
(council::CandidacyBond::key(), vec![].and(&self.candidacy_bond)),
|
||||
(council::VotingBond::key(), vec![].and(&self.voter_bond)),
|
||||
(council::PresentSlashPerVoter::key(), vec![].and(&self.present_slash_per_voter)),
|
||||
(council::CarryCount::key(), vec![].and(&self.carry_count)),
|
||||
(council::PresentationDuration::key(), vec![].and(&self.presentation_duration)),
|
||||
(council::VotingPeriod::key(), vec![].and(&self.council_election_voting_period)),
|
||||
(council::TermDuration::key(), vec![].and(&self.council_term_duration)),
|
||||
(council::DesiredSeats::key(), vec![].and(&self.desired_seats)),
|
||||
(council::InactiveGracePeriod::key(), vec![].and(&self.inactive_grace_period)),
|
||||
|
||||
(council_vote::CooloffPeriod::key(), vec![].and(&self.cooloff_period)),
|
||||
(council_vote::VotingPeriod::key(), vec![].and(&self.council_proposal_voting_period))
|
||||
].into_iter()
|
||||
.map(|(k, v)| (k.into(), v))
|
||||
.chain(self.balances.iter()
|
||||
.map(|&(account, balance)| (staking::FreeBalanceOf::key_for(&account), vec![].and(&balance)))
|
||||
)
|
||||
.map(|(k, v)| (twox_128(&k[..])[..].to_vec(), v.to_vec()))
|
||||
.chain(vec![
|
||||
(system::CODE.to_vec(), wasm_runtime),
|
||||
(consensus::AUTHORITY_COUNT[..].into(), vec![].and(&(self.authorities.len() as u32))),
|
||||
].into_iter())
|
||||
.chain(self.authorities.iter()
|
||||
.enumerate()
|
||||
.map(|(i, account)| ((i as u32).to_keyed_vec(consensus::AUTHORITY_AT), vec![].and(account)))
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn additional_storage_with_genesis(genesis_block: &Block) -> HashMap<Vec<u8>, Vec<u8>> {
|
||||
use codec::Slicable;
|
||||
map![
|
||||
system::BlockHashAt::key_for(&0) => genesis_block.header.blake2_256().encode()
|
||||
]
|
||||
}
|
||||
@@ -18,30 +18,150 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[allow(unused_imports)] #[macro_use] extern crate substrate_runtime_std as rstd;
|
||||
#[macro_use] extern crate substrate_runtime_io as runtime_io;
|
||||
#[macro_use] extern crate substrate_runtime_support as runtime_support;
|
||||
#[cfg(any(feature = "std", test))] extern crate substrate_keyring as keyring;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
|
||||
#[cfg(feature = "std")] #[macro_use] extern crate serde_derive;
|
||||
#[cfg(feature = "std")] extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg(feature = "std")] extern crate rustc_hex;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
#[cfg(feature = "std")] #[macro_use] extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_council as council;
|
||||
extern crate substrate_runtime_democracy as democracy;
|
||||
extern crate substrate_runtime_executive as executive;
|
||||
extern crate substrate_runtime_session as session;
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
extern crate substrate_runtime_system as system;
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
extern crate demo_primitives;
|
||||
|
||||
#[cfg(test)] #[macro_use] extern crate hex_literal;
|
||||
use rstd::prelude::*;
|
||||
use runtime_io::BlakeTwo256;
|
||||
use demo_primitives::{AccountId, Balance, BlockNumber, Hash, Index, SessionKey, Signature};
|
||||
use runtime_primitives::generic;
|
||||
use runtime_primitives::traits::{Identity, HasPublicAux};
|
||||
|
||||
extern crate integer_sqrt;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use runtime_primitives::BuildExternalities;
|
||||
|
||||
#[macro_use] pub mod dispatch;
|
||||
/// Concrete runtime type used to parameterize the various modules.
|
||||
pub struct Concrete;
|
||||
|
||||
pub mod safe_mix;
|
||||
pub mod block;
|
||||
pub mod transaction;
|
||||
pub mod runtime;
|
||||
pub mod api;
|
||||
impl HasPublicAux for Concrete {
|
||||
type PublicAux = AccountId;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")] pub mod genesismap;
|
||||
impl timestamp::Trait for Concrete {
|
||||
type Value = u64;
|
||||
}
|
||||
|
||||
/// Timestamp module for this concrete runtime.
|
||||
pub type Timestamp = timestamp::Module<Concrete>;
|
||||
|
||||
impl consensus::Trait for Concrete {
|
||||
type SessionKey = SessionKey;
|
||||
}
|
||||
|
||||
/// Consensus module for this concrete runtime.
|
||||
pub type Consensus = consensus::Module<Concrete>;
|
||||
|
||||
impl system::Trait for Concrete {
|
||||
type Index = Index;
|
||||
type BlockNumber = BlockNumber;
|
||||
type Hash = Hash;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Digest = generic::Digest<Vec<u8>>;
|
||||
type AccountId = AccountId;
|
||||
type Header = generic::Header<BlockNumber, Hash, Vec<u8>>;
|
||||
}
|
||||
|
||||
/// System module for this concrete runtime.
|
||||
pub type System = system::Module<Concrete>;
|
||||
|
||||
impl session::Trait for Concrete {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
}
|
||||
|
||||
/// Session module for this concrete runtime.
|
||||
pub type Session = session::Module<Concrete>;
|
||||
|
||||
impl staking::Trait for Concrete {
|
||||
type Balance = Balance;
|
||||
type DetermineContractAddress = BlakeTwo256;
|
||||
}
|
||||
|
||||
/// Staking module for this concrete runtime.
|
||||
pub type Staking = staking::Module<Concrete>;
|
||||
|
||||
impl democracy::Trait for Concrete {
|
||||
type Proposal = PrivCall;
|
||||
}
|
||||
|
||||
/// Democracy module for this concrete runtime.
|
||||
pub type Democracy = democracy::Module<Concrete>;
|
||||
|
||||
impl council::Trait for Concrete {}
|
||||
|
||||
/// Council module for this concrete runtime.
|
||||
pub type Council = council::Module<Concrete>;
|
||||
/// Council voting module for this concrete runtime.
|
||||
pub type CouncilVoting = council::voting::Module<Concrete>;
|
||||
|
||||
impl_outer_dispatch! {
|
||||
pub enum Call where aux: <Concrete as HasPublicAux>::PublicAux {
|
||||
Session = 1,
|
||||
Staking = 2,
|
||||
Timestamp = 3,
|
||||
Democracy = 5,
|
||||
Council = 6,
|
||||
CouncilVoting = 7,
|
||||
}
|
||||
|
||||
pub enum PrivCall {
|
||||
Consensus = 0,
|
||||
Session = 1,
|
||||
Staking = 2,
|
||||
Democracy = 5,
|
||||
Council = 6,
|
||||
CouncilVoting = 7,
|
||||
}
|
||||
}
|
||||
|
||||
/// Block header type as expected by this runtime.
|
||||
pub type Header = generic::Header<BlockNumber, Hash, Vec<u8>>;
|
||||
/// Block type as expected by this runtime.
|
||||
pub type Block = generic::Block<BlockNumber, Hash, Vec<u8>, AccountId, Index, Call, Signature>;
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
/// Extrinsic type as expected by this runtime.
|
||||
pub type Extrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||
/// Executive: handles dispatch to the various modules.
|
||||
pub type Executive = executive::Executive<Concrete, Block, Staking,
|
||||
(((((), Council), Democracy), Staking), Session)>;
|
||||
|
||||
impl_outer_config! {
|
||||
pub struct GenesisConfig for Concrete {
|
||||
ConsensusConfig => consensus,
|
||||
SystemConfig => system,
|
||||
SessionConfig => session,
|
||||
StakingConfig => staking,
|
||||
DemocracyConfig => democracy,
|
||||
CouncilConfig => council,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod api {
|
||||
impl_stubs!(
|
||||
authorities => |()| super::Consensus::authorities(),
|
||||
initialise_block => |header| super::Executive::initialise_block(&header),
|
||||
apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic),
|
||||
execute_block => |block| super::Executive::execute_block(block),
|
||||
finalise_block => |()| super::Executive::finalise_block(),
|
||||
validator_count => |()| super::Session::validator_count(),
|
||||
validators => |()| super::Session::validators()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Conensus module for runtime; manages the authority set ready for the native code.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use runtime_support::storage::unhashed::StorageVec;
|
||||
use demo_primitives::SessionKey;
|
||||
|
||||
pub const AUTHORITY_AT: &'static[u8] = b":auth:";
|
||||
pub const AUTHORITY_COUNT: &'static[u8] = b":auth:len";
|
||||
|
||||
struct AuthorityStorageVec {}
|
||||
impl StorageVec for AuthorityStorageVec {
|
||||
type Item = SessionKey;
|
||||
const PREFIX: &'static[u8] = AUTHORITY_AT;
|
||||
}
|
||||
|
||||
/// Get the current set of authorities. These are the session keys.
|
||||
pub fn authorities() -> Vec<SessionKey> {
|
||||
AuthorityStorageVec::items()
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
|
||||
/// Set the current set of authorities' session keys.
|
||||
///
|
||||
/// Called by `next_session` only.
|
||||
pub fn set_authorities(authorities: &[SessionKey]) {
|
||||
AuthorityStorageVec::set_items(authorities);
|
||||
}
|
||||
|
||||
/// Set a single authority by index.
|
||||
pub fn set_authority(index: u32, key: &SessionKey) {
|
||||
AuthorityStorageVec::set_item(index, key);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,493 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Council voting system.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use codec::{KeyedVec, Slicable, Input, NonTrivialSlicable};
|
||||
use runtime_support::Hashable;
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use demo_primitives::{AccountId, Hash, BlockNumber};
|
||||
use runtime::{system, democracy, council};
|
||||
use runtime::staking::{PublicPass, Balance};
|
||||
use runtime::democracy::PrivPass;
|
||||
use dispatch::PrivCall as Proposal;
|
||||
|
||||
type ProposalHash = [u8; 32];
|
||||
|
||||
storage_items! {
|
||||
pub CooloffPeriod get(cooloff_period): b"cov:cooloff" => required BlockNumber;
|
||||
pub VotingPeriod get(voting_period): b"cov:period" => required BlockNumber;
|
||||
pub Proposals get(proposals): b"cov:prs" => default Vec<(BlockNumber, ProposalHash)>; // ordered by expiry.
|
||||
pub ProposalOf get(proposal_of): b"cov:pro" => map [ ProposalHash => Proposal ];
|
||||
pub ProposalVoters get(proposal_voters): b"cov:voters:" => default map [ ProposalHash => Vec<AccountId> ];
|
||||
pub CouncilVoteOf get(vote_of): b"cov:vote:" => map [ (ProposalHash, AccountId) => bool ];
|
||||
pub VetoedProposal get(veto_of): b"cov:veto:" => map [ ProposalHash => (BlockNumber, Vec<AccountId>) ];
|
||||
}
|
||||
|
||||
pub fn is_vetoed<B: Borrow<ProposalHash>>(proposal: B) -> bool {
|
||||
VetoedProposal::get(proposal.borrow())
|
||||
.map(|(expiry, _): (BlockNumber, Vec<AccountId>)| system::block_number() < expiry)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn set_veto_of(proposal: &ProposalHash, expiry: BlockNumber, vetoers: Vec<AccountId>) {
|
||||
VetoedProposal::insert(proposal, (expiry, vetoers));
|
||||
}
|
||||
|
||||
fn kill_veto_of(proposal: &ProposalHash) {
|
||||
VetoedProposal::remove(proposal);
|
||||
}
|
||||
|
||||
pub fn will_still_be_councillor_at(who: &AccountId, n: BlockNumber) -> bool {
|
||||
council::active_council().iter()
|
||||
.find(|&&(ref a, _)| a == who)
|
||||
.map(|&(_, expires)| expires > n)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn is_councillor(who: &AccountId) -> bool {
|
||||
council::active_council().iter()
|
||||
.any(|&(ref a, _)| a == who)
|
||||
}
|
||||
|
||||
pub fn tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) {
|
||||
generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| CouncilVoteOf::get((*p, *w)))
|
||||
}
|
||||
|
||||
fn take_tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) {
|
||||
generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| CouncilVoteOf::take((*p, *w)))
|
||||
}
|
||||
|
||||
fn generic_tally<F: Fn(&AccountId, &ProposalHash) -> Option<bool>>(proposal_hash: &ProposalHash, vote_of: F) -> (u32, u32, u32) {
|
||||
let c = council::active_council();
|
||||
let (approve, reject) = c.iter()
|
||||
.filter_map(|&(ref a, _)| vote_of(a, proposal_hash))
|
||||
.map(|approve| if approve { (1, 0) } else { (0, 1) })
|
||||
.fold((0, 0), |(a, b), (c, d)| (a + c, b + d));
|
||||
(approve, reject, c.len() as u32 - approve - reject)
|
||||
}
|
||||
|
||||
fn set_proposals(p: &Vec<(BlockNumber, ProposalHash)>) {
|
||||
Proposals::put(p);
|
||||
}
|
||||
|
||||
fn take_proposal_if_expiring_at(n: BlockNumber) -> Option<(Proposal, ProposalHash)> {
|
||||
let mut proposals = proposals();
|
||||
match proposals.first() {
|
||||
Some(&(expiry, hash)) if expiry == n => {
|
||||
// yes this is horrible, but fixing it will need substantial work in storage.
|
||||
set_proposals(&proposals[1..].to_vec());
|
||||
let proposal = ProposalOf::take(hash).expect("all queued proposal hashes must have associated proposals");
|
||||
Some((proposal, hash))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn propose(proposal: Box<Proposal>) = 0;
|
||||
fn vote(proposal: ProposalHash, approve: bool) = 1;
|
||||
fn veto(proposal_hash: ProposalHash) = 2;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
fn propose(self, proposal: Box<Proposal>) {
|
||||
let expiry = system::block_number() + voting_period();
|
||||
assert!(will_still_be_councillor_at(&self, expiry));
|
||||
|
||||
let proposal_hash = proposal.blake2_256();
|
||||
|
||||
assert!(!is_vetoed(&proposal_hash));
|
||||
|
||||
let mut proposals = proposals();
|
||||
proposals.push((expiry, proposal_hash));
|
||||
proposals.sort_by_key(|&(expiry, _)| expiry);
|
||||
set_proposals(&proposals);
|
||||
|
||||
ProposalOf::insert(proposal_hash, *proposal);
|
||||
ProposalVoters::insert(proposal_hash, vec![*self]);
|
||||
CouncilVoteOf::insert((proposal_hash, *self), true);
|
||||
}
|
||||
|
||||
fn vote(self, proposal: ProposalHash, approve: bool) {
|
||||
if vote_of((*self, proposal)).is_none() {
|
||||
let mut voters = proposal_voters(&proposal);
|
||||
voters.push(*self);
|
||||
ProposalVoters::insert(proposal, voters);
|
||||
}
|
||||
CouncilVoteOf::insert((proposal, *self), approve);
|
||||
}
|
||||
|
||||
fn veto(self, proposal_hash: ProposalHash) {
|
||||
assert!(is_councillor(&self), "only councillors may veto council proposals");
|
||||
assert!(ProposalVoters::exists(&proposal_hash), "proposal must exist to be vetoed");
|
||||
|
||||
let mut existing_vetoers = veto_of(&proposal_hash)
|
||||
.map(|pair| pair.1)
|
||||
.unwrap_or_else(Vec::new);
|
||||
let insert_position = existing_vetoers.binary_search(&self)
|
||||
.expect_err("a councillor may not veto a proposal twice");
|
||||
existing_vetoers.insert(insert_position, *self);
|
||||
set_veto_of(&proposal_hash, system::block_number() + cooloff_period(), existing_vetoers);
|
||||
|
||||
set_proposals(&proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::<Vec<_>>());
|
||||
ProposalVoters::remove(proposal_hash);
|
||||
ProposalOf::remove(proposal_hash);
|
||||
for (c, _) in council::active_council() {
|
||||
CouncilVoteOf::remove((proposal_hash, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_cooloff_period(blocks: BlockNumber) = 0;
|
||||
fn set_voting_period(blocks: BlockNumber) = 1;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
fn set_cooloff_period(self, blocks: BlockNumber) {
|
||||
CooloffPeriod::put(blocks);
|
||||
}
|
||||
|
||||
fn set_voting_period(self, blocks: BlockNumber) {
|
||||
VotingPeriod::put(blocks);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
use runtime::democracy::privileged::Dispatch;
|
||||
use runtime::democracy::VoteThreshold;
|
||||
use runtime::democracy::internal::start_referendum;
|
||||
|
||||
pub fn end_block(now: BlockNumber) {
|
||||
while let Some((proposal, proposal_hash)) = take_proposal_if_expiring_at(now) {
|
||||
let tally = take_tally(&proposal_hash);
|
||||
if let &Proposal::Democracy(democracy::privileged::Call::cancel_referendum(ref_index)) = &proposal {
|
||||
if let (_, 0, 0) = tally {
|
||||
democracy::internal::cancel_referendum(ref_index);
|
||||
}
|
||||
} else {
|
||||
if tally.0 > tally.1 + tally.2 {
|
||||
kill_veto_of(&proposal_hash);
|
||||
match tally {
|
||||
(_, 0, 0) => start_referendum(proposal, VoteThreshold::SuperMajorityAgainst),
|
||||
_ => start_referendum(proposal, VoteThreshold::SimpleMajority),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod testing {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
use keyring::Keyring::{Alice, Bob, Charlie};
|
||||
use codec::Joiner;
|
||||
use runtime::{council, democracy};
|
||||
|
||||
pub fn externalities() -> TestExternalities {
|
||||
let expiry: BlockNumber = 10;
|
||||
let extras: TestExternalities = map![
|
||||
twox_128(council::ActiveCouncil::key()).to_vec() => vec![].and(&vec![
|
||||
(Alice.to_raw_public(), expiry),
|
||||
(Bob.into(), expiry),
|
||||
(Charlie.into(), expiry)
|
||||
]),
|
||||
twox_128(CooloffPeriod::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(VotingPeriod::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(democracy::VotingPeriod::key()).to_vec() => vec![].and(&3u64)
|
||||
];
|
||||
council::testing::externalities()
|
||||
.into_iter().chain(extras.into_iter()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use keyring::Keyring::{Alice, Bob, Charlie, Dave};
|
||||
use demo_primitives::AccountId;
|
||||
use runtime::democracy::VoteThreshold;
|
||||
use runtime::{staking, council, democracy};
|
||||
use super::public::Dispatch;
|
||||
use super::privileged::Dispatch as PrivDispatch;
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
testing::externalities()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_environment_works() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
assert_eq!(staking::bonding_duration(), 0);
|
||||
assert_eq!(cooloff_period(), 2);
|
||||
assert_eq!(voting_period(), 1);
|
||||
assert_eq!(will_still_be_councillor_at(&Alice, 1), true);
|
||||
assert_eq!(will_still_be_councillor_at(&Alice, 10), false);
|
||||
assert_eq!(will_still_be_councillor_at(&Dave, 10), false);
|
||||
assert_eq!(is_councillor(&Alice), true);
|
||||
assert_eq!(is_councillor(&Dave), false);
|
||||
assert_eq!(proposals(), Vec::<(BlockNumber, ProposalHash)>::new());
|
||||
assert_eq!(proposal_voters(ProposalHash::default()), Vec::<AccountId>::new());
|
||||
assert_eq!(is_vetoed(&ProposalHash::default()), false);
|
||||
assert_eq!(vote_of((*Alice, ProposalHash::default())), None);
|
||||
assert_eq!(tally(&ProposalHash::default()), (0, 0, 3));
|
||||
});
|
||||
}
|
||||
|
||||
fn sessions_per_era_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::privileged::Call::set_sessions_per_era(value))
|
||||
}
|
||||
|
||||
fn bonding_duration_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::privileged::Call::set_bonding_duration(value))
|
||||
}
|
||||
|
||||
fn cancel_referendum_proposal(id: u32) -> Proposal {
|
||||
Proposal::Democracy(democracy::privileged::Call::cancel_referendum(id))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn referendum_cancellation_should_work_when_unanimous() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
democracy::internal::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(cancellation));
|
||||
PublicPass::new(&Bob).vote(hash, true);
|
||||
PublicPass::new(&Charlie).vote(hash, true);
|
||||
assert_eq!(proposals(), vec![(2, hash)]);
|
||||
internal::end_block(1);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(democracy::active_referendums(), vec![]);
|
||||
assert_eq!(staking::bonding_duration(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn referendum_cancellation_should_fail_when_not_unanimous() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
democracy::internal::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(cancellation));
|
||||
PublicPass::new(&Bob).vote(hash, true);
|
||||
PublicPass::new(&Charlie).vote(hash, false);
|
||||
internal::end_block(1);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn referendum_cancellation_should_fail_when_abstentions() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
democracy::internal::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(cancellation));
|
||||
PublicPass::new(&Bob).vote(hash, true);
|
||||
internal::end_block(1);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums().len(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn double_veto_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
system::testing::set_block_number(3);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn retry_in_cooloff_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retry_after_cooloff_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
system::testing::set_block_number(3);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).vote(hash, false);
|
||||
PublicPass::new(&Charlie).vote(hash, true);
|
||||
internal::end_block(3);
|
||||
|
||||
system::testing::set_block_number(4);
|
||||
internal::end_block(4);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 7, bonding_duration_proposal(42), VoteThreshold::SimpleMajority)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alternative_double_veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
system::testing::set_block_number(3);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Charlie).veto(hash);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums().len(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_propose_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
assert_eq!(proposals().len(), 1);
|
||||
assert_eq!(proposal_voters(&hash), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of((hash, *Alice)), Some(true));
|
||||
assert_eq!(tally(&hash), (1, 0, 2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unvoted_proposal_should_expire_without_action() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
assert_eq!(tally(&proposal.blake2_256()), (1, 0, 2));
|
||||
internal::end_block(1);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums().len(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unanimous_proposal_should_expire_with_biased_referendum() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).vote(proposal.blake2_256(), true);
|
||||
PublicPass::new(&Charlie).vote(proposal.blake2_256(), true);
|
||||
assert_eq!(tally(&proposal.blake2_256()), (3, 0, 0));
|
||||
internal::end_block(1);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SuperMajorityAgainst)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn majority_proposal_should_expire_with_unbiased_referendum() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).vote(proposal.blake2_256(), true);
|
||||
PublicPass::new(&Charlie).vote(proposal.blake2_256(), false);
|
||||
assert_eq!(tally(&proposal.blake2_256()), (2, 1, 0));
|
||||
internal::end_block(1);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SimpleMajority)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn propose_by_public_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Dave).propose(Box::new(proposal));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,623 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Democratic system: Handles administration of general stakeholder voting.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use integer_sqrt::IntegerSquareRoot;
|
||||
use codec::{KeyedVec, Slicable, Input, NonTrivialSlicable};
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use demo_primitives::{AccountId, Hash, BlockNumber};
|
||||
use dispatch::PrivCall as Proposal;
|
||||
use runtime::{staking, system, session};
|
||||
use runtime::staking::{PublicPass, Balance};
|
||||
|
||||
/// A token for privileged dispatch. Can only be created in this module.
|
||||
pub struct PrivPass((),);
|
||||
|
||||
impl PrivPass {
|
||||
fn new() -> PrivPass { PrivPass((),) }
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test() -> PrivPass { PrivPass((),) }
|
||||
}
|
||||
|
||||
/// A proposal index.
|
||||
pub type PropIndex = u32;
|
||||
/// A referendum index.
|
||||
pub type ReferendumIndex = u32;
|
||||
|
||||
/// A means of determining if a vote is past pass threshold.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub enum VoteThreshold {
|
||||
/// A supermajority of approvals is needed to pass this vote.
|
||||
SuperMajorityApprove,
|
||||
/// A supermajority of rejects is needed to fail this vote.
|
||||
SuperMajorityAgainst,
|
||||
/// A simple majority of approvals is needed to pass this vote.
|
||||
SimpleMajority,
|
||||
}
|
||||
|
||||
impl Slicable for VoteThreshold {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u8::decode(input).and_then(|v| match v {
|
||||
0 => Some(VoteThreshold::SuperMajorityApprove),
|
||||
1 => Some(VoteThreshold::SuperMajorityAgainst),
|
||||
2 => Some(VoteThreshold::SimpleMajority),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
match *self {
|
||||
VoteThreshold::SuperMajorityApprove => 0u8,
|
||||
VoteThreshold::SuperMajorityAgainst => 1u8,
|
||||
VoteThreshold::SimpleMajority => 2u8,
|
||||
}.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl NonTrivialSlicable for VoteThreshold {}
|
||||
|
||||
trait Approved {
|
||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
||||
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||
/// overall outcome is in favour of approval.
|
||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool;
|
||||
}
|
||||
|
||||
impl Approved for VoteThreshold {
|
||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
||||
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||
/// overall outcome is in favour of approval.
|
||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool {
|
||||
let voters = approve + against;
|
||||
match *self {
|
||||
VoteThreshold::SuperMajorityApprove =>
|
||||
voters.integer_sqrt() * approve / electorate.integer_sqrt() > against,
|
||||
VoteThreshold::SuperMajorityAgainst =>
|
||||
approve > voters.integer_sqrt() * against / electorate.integer_sqrt(),
|
||||
VoteThreshold::SimpleMajority => approve > against,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storage_items! {
|
||||
// The number of (public) proposals that have been made so far.
|
||||
pub PublicPropCount get(public_prop_count): b"dem:ppc" => default PropIndex;
|
||||
// The public proposals. Unsorted.
|
||||
pub PublicProps get(public_props): b"dem:pub" => default Vec<(PropIndex, Proposal, AccountId)>;
|
||||
// Those who have locked a deposit.
|
||||
pub DepositOf get(deposit_lockers): b"dem:dep:" => map [ PropIndex => (Balance, Vec<AccountId>) ];
|
||||
// How often (in blocks) new public referenda are launched.
|
||||
pub LaunchPeriod get(launch_period): b"dem:lau" => required BlockNumber;
|
||||
// The minimum amount to be used as a deposit for a public referendum proposal.
|
||||
pub MinimumDeposit get(minimum_deposit): b"dem:min" => required Balance;
|
||||
|
||||
// How often (in blocks) to check for new votes.
|
||||
pub VotingPeriod get(voting_period): b"dem:per" => required BlockNumber;
|
||||
|
||||
// The next free referendum index, aka the number of referendums started so far.
|
||||
pub ReferendumCount get(next_free_ref_index): b"dem:rco" => default ReferendumIndex;
|
||||
// The next referendum index that should be tallied.
|
||||
pub NextTally get(next_tally): b"dem:nxt" => default ReferendumIndex;
|
||||
// Information concerning any given referendum.
|
||||
pub ReferendumInfoOf get(referendum_info): b"dem:pro:" => map [ ReferendumIndex => (BlockNumber, Proposal, VoteThreshold) ];
|
||||
|
||||
// Get the voters for the current proposal.
|
||||
pub VotersFor get(voters_for): b"dem:vtr:" => default map [ ReferendumIndex => Vec<AccountId> ];
|
||||
|
||||
// Get the vote, if Some, of `who`.
|
||||
pub VoteOf get(vote_of): b"dem:vot:" => map [ (ReferendumIndex, AccountId) => bool ];
|
||||
}
|
||||
|
||||
// public proposals
|
||||
|
||||
/// Get the amount locked in support of `proposal`; false if proposal isn't a valid proposal
|
||||
/// index.
|
||||
pub fn locked_for(proposal: PropIndex) -> Option<Balance> {
|
||||
deposit_lockers(proposal).map(|(d, l)| d * (l.len() as Balance))
|
||||
}
|
||||
|
||||
/// Return true if `ref_index` is an on-going referendum.
|
||||
pub fn is_active_referendum(ref_index: ReferendumIndex) -> bool {
|
||||
ReferendumInfoOf::exists(ref_index)
|
||||
}
|
||||
|
||||
/// Get all referendums currently active.
|
||||
pub fn active_referendums() -> Vec<(ReferendumIndex, BlockNumber, Proposal, VoteThreshold)> {
|
||||
let next = NextTally::get();
|
||||
let last = ReferendumCount::get();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| referendum_info(i).map(|(n, p, t)| (i, n, p, t)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get all referendums ready for tally at block `n`.
|
||||
pub fn maturing_referendums_at(n: BlockNumber) -> Vec<(ReferendumIndex, BlockNumber, Proposal, VoteThreshold)> {
|
||||
let next = NextTally::get();
|
||||
let last = ReferendumCount::get();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| referendum_info(i).map(|(n, p, t)| (i, n, p, t)))
|
||||
.take_while(|&(_, block_number, _, _)| block_number == n)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (staking::Balance, staking::Balance) {
|
||||
voters_for(ref_index).iter()
|
||||
.map(|a| (staking::balance(a), vote_of((ref_index, *a)).expect("all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed")))
|
||||
.map(|(bal, vote)| if vote { (bal, 0) } else { (0, bal) })
|
||||
.fold((0, 0), |(a, b), (c, d)| (a + c, b + d))
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn propose(proposal: Box<Proposal>, value: Balance) = 0;
|
||||
fn second(proposal: PropIndex) = 1;
|
||||
fn vote(ref_index: ReferendumIndex, approve_proposal: bool) = 2;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn propose(self, proposal: Box<Proposal>, value: Balance) {
|
||||
assert!(value >= minimum_deposit());
|
||||
assert!(staking::internal::deduct_unbonded(&self, value));
|
||||
|
||||
let index = PublicPropCount::get();
|
||||
PublicPropCount::put(index + 1);
|
||||
DepositOf::insert(index, (value, vec![*self]));
|
||||
|
||||
let mut props = public_props();
|
||||
props.push((index, (*proposal).clone(), *self));
|
||||
PublicProps::put(props);
|
||||
}
|
||||
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn second(self, proposal: PropIndex) {
|
||||
let mut deposit = DepositOf::get(proposal).expect("can only second an existing proposal");
|
||||
assert!(staking::internal::deduct_unbonded(&self, deposit.0));
|
||||
|
||||
deposit.1.push(*self);
|
||||
DepositOf::insert(proposal, deposit);
|
||||
}
|
||||
|
||||
/// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;
|
||||
/// false would be a vote to keep the status quo..
|
||||
fn vote(self, ref_index: ReferendumIndex, approve_proposal: bool) {
|
||||
if !is_active_referendum(ref_index) {
|
||||
panic!("vote given for invalid referendum.")
|
||||
}
|
||||
if staking::balance(&self) == 0 {
|
||||
panic!("transactor must have balance to signal approval.");
|
||||
}
|
||||
if !VoteOf::exists(&(ref_index, *self)) {
|
||||
let mut voters = voters_for(ref_index);
|
||||
voters.push(self.clone());
|
||||
VotersFor::insert(ref_index, voters);
|
||||
}
|
||||
VoteOf::insert(&(ref_index, *self), approve_proposal);
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn start_referendum(proposal: Box<Proposal>, vote_threshold: VoteThreshold) = 0;
|
||||
fn cancel_referendum(ref_index: ReferendumIndex) = 1;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
/// Start a referendum.
|
||||
fn start_referendum(self, proposal: Box<Proposal>, vote_threshold: VoteThreshold) {
|
||||
inject_referendum(system::block_number() + voting_period(), *proposal, vote_threshold);
|
||||
}
|
||||
|
||||
/// Remove a referendum.
|
||||
fn cancel_referendum(self, ref_index: ReferendumIndex) {
|
||||
clear_referendum(ref_index);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
use dispatch;
|
||||
|
||||
/// Can be called directly by the council.
|
||||
pub fn start_referendum(proposal: Proposal, vote_threshold: VoteThreshold) {
|
||||
inject_referendum(system::block_number() + voting_period(), proposal, vote_threshold);
|
||||
}
|
||||
|
||||
/// Remove a referendum.
|
||||
pub fn cancel_referendum(ref_index: ReferendumIndex) {
|
||||
clear_referendum(ref_index);
|
||||
}
|
||||
|
||||
/// Current era is ending; we should finish up any proposals.
|
||||
pub fn end_block(now: BlockNumber) {
|
||||
// pick out another public referendum if it's time.
|
||||
if now % launch_period() == 0 {
|
||||
let mut public_props = public_props();
|
||||
if let Some((winner_index, _)) = public_props.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|x| locked_for((x.1).0).expect("All current public proposals have an amount locked"))
|
||||
{
|
||||
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
||||
let (deposit, depositors): (Balance, Vec<AccountId>) =
|
||||
DepositOf::take(prop_index).expect("depositors always exist for current proposals");
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
staking::internal::refund(d, deposit);
|
||||
}
|
||||
PublicProps::put(public_props);
|
||||
inject_referendum(now + voting_period(), proposal, VoteThreshold::SuperMajorityApprove);
|
||||
}
|
||||
}
|
||||
|
||||
// tally up votes for any expiring referenda.
|
||||
for (index, _, proposal, vote_threshold) in maturing_referendums_at(now) {
|
||||
let (approve, against) = tally(index);
|
||||
let total_stake = staking::total_stake();
|
||||
clear_referendum(index);
|
||||
if vote_threshold.approved(approve, against, total_stake) {
|
||||
proposal.dispatch(PrivPass::new());
|
||||
}
|
||||
NextTally::put(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a referendum
|
||||
fn inject_referendum(
|
||||
end: BlockNumber,
|
||||
proposal: Proposal,
|
||||
vote_threshold: VoteThreshold
|
||||
) -> ReferendumIndex {
|
||||
let ref_index = next_free_ref_index();
|
||||
if ref_index > 0 && referendum_info(ref_index - 1).map(|i| i.0 > end).unwrap_or(false) {
|
||||
panic!("Cannot inject a referendum that ends earlier than preceeding referendum");
|
||||
}
|
||||
|
||||
ReferendumCount::put(ref_index + 1);
|
||||
ReferendumInfoOf::insert(ref_index, (end, proposal, vote_threshold));
|
||||
ref_index
|
||||
}
|
||||
|
||||
/// Remove all info on a referendum.
|
||||
fn clear_referendum(ref_index: ReferendumIndex) {
|
||||
ReferendumInfoOf::remove(ref_index);
|
||||
VotersFor::remove(ref_index);
|
||||
for v in voters_for(ref_index) {
|
||||
VoteOf::remove((ref_index, v));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod testing {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
use runtime_support::{StorageList, StorageValue, StorageMap};
|
||||
use codec::Joiner;
|
||||
use keyring::Keyring::*;
|
||||
use runtime::{session, staking};
|
||||
|
||||
pub fn externalities() -> TestExternalities {
|
||||
map![
|
||||
twox_128(session::SessionLength::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(session::Validators::key()).to_vec() => vec![].and(&vec![Alice.to_raw_public(), Bob.into(), Charlie.into()]),
|
||||
twox_128(&staking::Intention::len_key()).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&staking::Intention::key_for(0)).to_vec() => Alice.to_raw_public_vec(),
|
||||
twox_128(&staking::Intention::key_for(1)).to_vec() => Bob.to_raw_public_vec(),
|
||||
twox_128(&staking::Intention::key_for(2)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Alice)).to_vec() => vec![].and(&10u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Bob)).to_vec() => vec![].and(&20u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Charlie)).to_vec() => vec![].and(&30u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Dave)).to_vec() => vec![].and(&40u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Eve)).to_vec() => vec![].and(&50u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Ferdie)).to_vec() => vec![].and(&60u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*One)).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::TotalStake::key()).to_vec() => vec![].and(&210u64),
|
||||
twox_128(staking::SessionsPerEra::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::ValidatorCount::key()).to_vec() => vec![].and(&3u64),
|
||||
twox_128(staking::CurrentEra::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::BondingDuration::key()).to_vec() => vec![].and(&0u64),
|
||||
|
||||
twox_128(LaunchPeriod::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(VotingPeriod::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(MinimumDeposit::key()).to_vec() => vec![].and(&1u64)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use keyring::Keyring::*;
|
||||
use demo_primitives::AccountId;
|
||||
use dispatch::PrivCall as Proposal;
|
||||
use runtime::staking::PublicPass;
|
||||
use super::public::Dispatch;
|
||||
use super::privileged::Dispatch as PrivDispatch;
|
||||
use runtime::{staking, session, democracy};
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
testing::externalities()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn params_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(launch_period(), 1u64);
|
||||
assert_eq!(voting_period(), 1u64);
|
||||
assert_eq!(minimum_deposit(), 1u64);
|
||||
assert_eq!(next_free_ref_index(), 0u32);
|
||||
assert_eq!(staking::sessions_per_era(), 1u64);
|
||||
assert_eq!(staking::total_stake(), 210u64);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: test VoteThreshold
|
||||
|
||||
fn propose_sessions_per_era(who: &AccountId, value: u64, locked: Balance) {
|
||||
PublicPass::test(who).
|
||||
propose(Box::new(Proposal::Staking(staking::privileged::Call::set_sessions_per_era(value))), locked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_for_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
propose_sessions_per_era(&Alice, 2, 2u64);
|
||||
propose_sessions_per_era(&Alice, 4, 4u64);
|
||||
propose_sessions_per_era(&Alice, 3, 3u64);
|
||||
assert_eq!(locked_for(0), Some(2));
|
||||
assert_eq!(locked_for(1), Some(4));
|
||||
assert_eq!(locked_for(2), Some(3));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_proposal_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
propose_sessions_per_era(&Alice, 2, 1u64);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
let r = 0;
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
|
||||
assert_eq!(next_free_ref_index(), 1);
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of((r, *Alice)), Some(true));
|
||||
assert_eq!(tally(r), (10, 0));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
|
||||
assert_eq!(staking::era_length(), 2u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deposit_for_proposals_should_be_taken() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
propose_sessions_per_era(&Alice, 2, 5u64);
|
||||
PublicPass::test(&Bob).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
assert_eq!(staking::balance(&Alice), 5u64);
|
||||
assert_eq!(staking::balance(&Bob), 15u64);
|
||||
assert_eq!(staking::balance(&Eve), 35u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deposit_for_proposals_should_be_returned() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
propose_sessions_per_era(&Alice, 2, 5u64);
|
||||
PublicPass::test(&Bob).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
assert_eq!(staking::balance(&Alice), 10u64);
|
||||
assert_eq!(staking::balance(&Bob), 20u64);
|
||||
assert_eq!(staking::balance(&Eve), 50u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn proposal_with_deposit_below_minimum_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
propose_sessions_per_era(&Alice, 2, 0u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn poor_proposer_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
propose_sessions_per_era(&Alice, 2, 11u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn poor_seconder_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
propose_sessions_per_era(&Bob, 2, 11u64);
|
||||
PublicPass::test(&Alice).second(0);
|
||||
});
|
||||
}
|
||||
|
||||
fn propose_bonding_duration(who: &AccountId, value: u64, locked: Balance) {
|
||||
PublicPass::test(who).
|
||||
propose(Box::new(Proposal::Staking(staking::privileged::Call::set_bonding_duration(value))), locked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runners_up_should_come_after() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(0);
|
||||
propose_bonding_duration(&Alice, 2, 2u64);
|
||||
propose_bonding_duration(&Alice, 4, 4u64);
|
||||
propose_bonding_duration(&Alice, 3, 3u64);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
|
||||
system::testing::set_block_number(1);
|
||||
PublicPass::test(&Alice).vote(0, true);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
assert_eq!(staking::bonding_duration(), 4u64);
|
||||
|
||||
system::testing::set_block_number(2);
|
||||
PublicPass::test(&Alice).vote(1, true);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
assert_eq!(staking::bonding_duration(), 3u64);
|
||||
|
||||
system::testing::set_block_number(3);
|
||||
PublicPass::test(&Alice).vote(2, true);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
assert_eq!(staking::bonding_duration(), 2u64);
|
||||
});
|
||||
}
|
||||
|
||||
fn sessions_per_era_propsal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::privileged::Call::set_sessions_per_era(value))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_passing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of((r, *Alice)), Some(true));
|
||||
assert_eq!(tally(r), (10, 0));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
|
||||
assert_eq!(staking::era_length(), 2u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_referendum_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
PrivPass::test().cancel_referendum(r);
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_failing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, false);
|
||||
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of((r, *Alice)), Some(false));
|
||||
assert_eq!(tally(r), (0, 10));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn controversial_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
PublicPass::test(&Bob).vote(r, false);
|
||||
PublicPass::test(&Charlie).vote(r, false);
|
||||
PublicPass::test(&Dave).vote(r, true);
|
||||
PublicPass::test(&Eve).vote(r, false);
|
||||
PublicPass::test(&Ferdie).vote(r, true);
|
||||
|
||||
assert_eq!(tally(r), (110, 100));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
|
||||
assert_eq!(staking::era_length(), 2u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn controversial_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
system::testing::set_block_number(1);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Eve).vote(r, false);
|
||||
PublicPass::test(&Ferdie).vote(r, true);
|
||||
|
||||
assert_eq!(tally(r), (60, 50));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::total_stake(), 210u64);
|
||||
|
||||
system::testing::set_block_number(1);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Dave).vote(r, true);
|
||||
PublicPass::test(&Eve).vote(r, false);
|
||||
PublicPass::test(&Ferdie).vote(r, true);
|
||||
|
||||
assert_eq!(tally(r), (100, 50));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
|
||||
assert_eq!(staking::era_length(), 2u64);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! The Substrate Demo runtime.
|
||||
|
||||
#[allow(unused)]
|
||||
pub mod system;
|
||||
#[allow(unused)]
|
||||
pub mod consensus;
|
||||
#[allow(unused)]
|
||||
pub mod staking;
|
||||
#[allow(unused)]
|
||||
pub mod timestamp;
|
||||
#[allow(unused)]
|
||||
pub mod session;
|
||||
#[allow(unused)]
|
||||
pub mod democracy;
|
||||
#[allow(unused)]
|
||||
pub mod council;
|
||||
#[allow(unused)]
|
||||
pub mod council_vote;
|
||||
@@ -1,259 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Session manager: is told the validators and allows them to manage their session keys for the
|
||||
//! consensus module.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::KeyedVec;
|
||||
use runtime_support::{storage, StorageValue, StorageMap};
|
||||
use demo_primitives::{AccountId, SessionKey, BlockNumber};
|
||||
use runtime::{system, staking, consensus};
|
||||
use runtime::democracy::PrivPass;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
storage_items!{
|
||||
// The current set of validators.
|
||||
pub Validators get(validators): b"ses:val" => required Vec<AccountId>;
|
||||
// Current length of the session.
|
||||
pub SessionLength get(length): b"ses:len" => required BlockNumber;
|
||||
// Current index of the session.
|
||||
pub CurrentIndex get(current_index): b"ses:ind" => required BlockNumber;
|
||||
|
||||
// Block at which the session length last changed.
|
||||
LastLengthChange: b"ses:llc" => default BlockNumber;
|
||||
// The next key for a given validator.
|
||||
NextKeyFor: b"ses:nxt:" => map [ AccountId => SessionKey ];
|
||||
// The next session length.
|
||||
NextSessionLength: b"ses:nln" => BlockNumber;
|
||||
}
|
||||
|
||||
/// The number of validators currently.
|
||||
pub fn validator_count() -> u32 {
|
||||
Validators::get().len() as u32 // TODO: can probably optimised
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn set_key(key: SessionKey) = 0;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
|
||||
/// session.
|
||||
fn set_key(self, key: SessionKey) {
|
||||
// set new value for next session
|
||||
NextKeyFor::insert(*self, key)
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_length(new: BlockNumber) = 0;
|
||||
fn force_new_session() = 1;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
/// Set a new era length. Won't kick in until the next era change (at current length).
|
||||
fn set_length(self, new: BlockNumber) {
|
||||
NextSessionLength::put(new);
|
||||
}
|
||||
|
||||
/// Forces a new session.
|
||||
fn force_new_session(self) {
|
||||
internal::rotate_session();
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL API (available to other runtime modules)
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
|
||||
/// Set the current set of validators.
|
||||
///
|
||||
/// Called by `staking::next_era()` only. `next_session` should be called after this in order to
|
||||
/// update the session keys to the next validator set.
|
||||
pub fn set_validators(new: &[AccountId]) {
|
||||
Validators::put(&new.to_vec()); // TODO: optimise.
|
||||
consensus::internal::set_authorities(new);
|
||||
}
|
||||
|
||||
/// Hook to be called after transaction processing.
|
||||
pub fn check_rotate_session() {
|
||||
// do this last, after the staking system has had chance to switch out the authorities for the
|
||||
// new set.
|
||||
// check block number and call next_session if necessary.
|
||||
if (system::block_number() - LastLengthChange::get()) % length() == 0 {
|
||||
rotate_session();
|
||||
}
|
||||
}
|
||||
|
||||
/// Move onto next session: register the new authority set.
|
||||
pub fn rotate_session() {
|
||||
// Increment current session index.
|
||||
CurrentIndex::put(CurrentIndex::get() + 1);
|
||||
|
||||
// Enact era length change.
|
||||
if let Some(next_len) = NextSessionLength::take() {
|
||||
SessionLength::put(next_len);
|
||||
LastLengthChange::put(system::block_number());
|
||||
}
|
||||
|
||||
// Update any changes in session keys.
|
||||
validators().iter().enumerate().for_each(|(i, v)| {
|
||||
if let Some(n) = NextKeyFor::take(v) {
|
||||
consensus::internal::set_authority(i as u32, &n);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub mod testing {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use keyring::Keyring::*;
|
||||
use runtime::system;
|
||||
|
||||
pub fn externalities(session_length: u64) -> TestExternalities {
|
||||
let three = [3u8; 32];
|
||||
|
||||
let extras: TestExternalities = map![
|
||||
twox_128(SessionLength::key()).to_vec() => vec![].and(&session_length),
|
||||
twox_128(CurrentIndex::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(Validators::key()).to_vec() => vec![].and(&vec![One.into(), Two.into(), three])
|
||||
];
|
||||
system::testing::externalities().into_iter().chain(extras.into_iter()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::public::*;
|
||||
use super::privileged::Dispatch as PrivDispatch;
|
||||
use super::internal::*;
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use keyring::Keyring;
|
||||
use demo_primitives::AccountId;
|
||||
use runtime::{consensus, session};
|
||||
|
||||
fn simple_setup() -> TestExternalities {
|
||||
map![
|
||||
twox_128(SessionLength::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(CurrentIndex::key()).to_vec() => vec![].and(&0u64),
|
||||
// the validators (10, 20, ...)
|
||||
twox_128(Validators::key()).to_vec() => vec![].and(&vec![[10u8; 32], [20; 32]]),
|
||||
// initial session keys (11, 21, ...)
|
||||
b":auth:len".to_vec() => vec![].and(&2u32),
|
||||
0u32.to_keyed_vec(b":auth:") => vec![11; 32],
|
||||
1u32.to_keyed_vec(b":auth:") => vec![21; 32]
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_setup_should_work() {
|
||||
let mut t = simple_setup();
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
|
||||
assert_eq!(length(), 2u64);
|
||||
assert_eq!(validators(), vec![[10u8; 32], [20u8; 32]]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_length_change_should_work() {
|
||||
let mut t = simple_setup();
|
||||
with_externalities(&mut t, || {
|
||||
// Block 1: Change to length 3; no visible change.
|
||||
system::testing::set_block_number(1);
|
||||
PrivPass::test().set_length(3);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 2);
|
||||
assert_eq!(current_index(), 0);
|
||||
|
||||
// Block 2: Length now changed to 3. Index incremented.
|
||||
system::testing::set_block_number(2);
|
||||
PrivPass::test().set_length(3);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 3);
|
||||
assert_eq!(current_index(), 1);
|
||||
|
||||
// Block 3: Length now changed to 3. Index incremented.
|
||||
system::testing::set_block_number(3);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 3);
|
||||
assert_eq!(current_index(), 1);
|
||||
|
||||
// Block 4: Change to length 2; no visible change.
|
||||
system::testing::set_block_number(4);
|
||||
PrivPass::test().set_length(2);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 3);
|
||||
assert_eq!(current_index(), 1);
|
||||
|
||||
// Block 5: Length now changed to 2. Index incremented.
|
||||
system::testing::set_block_number(5);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 2);
|
||||
assert_eq!(current_index(), 2);
|
||||
|
||||
// Block 6: No change.
|
||||
system::testing::set_block_number(6);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 2);
|
||||
assert_eq!(current_index(), 2);
|
||||
|
||||
// Block 7: Next index.
|
||||
system::testing::set_block_number(7);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 2);
|
||||
assert_eq!(current_index(), 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_change_should_work() {
|
||||
let mut t = simple_setup();
|
||||
with_externalities(&mut t, || {
|
||||
// Block 1: No change
|
||||
system::testing::set_block_number(1);
|
||||
check_rotate_session();
|
||||
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
|
||||
|
||||
// Block 2: Session rollover, but no change.
|
||||
system::testing::set_block_number(2);
|
||||
check_rotate_session();
|
||||
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
|
||||
|
||||
// Block 3: Set new key for validator 2; no visible change.
|
||||
system::testing::set_block_number(3);
|
||||
PublicPass::test(&[20; 32]).set_key([22; 32]);
|
||||
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
|
||||
|
||||
check_rotate_session();
|
||||
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
|
||||
|
||||
// Block 4: Session rollover, authority 2 changes.
|
||||
system::testing::set_block_number(4);
|
||||
check_rotate_session();
|
||||
assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,885 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Staking manager: Handles balances and periodically determines the best set of validators.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::{ops, cmp};
|
||||
use rstd::cell::RefCell;
|
||||
use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||
use runtime_io::{print, blake2_256};
|
||||
use codec::{Slicable, Input, KeyedVec};
|
||||
use runtime_support::{storage, StorageValue, StorageList, StorageMap};
|
||||
use demo_primitives::{BlockNumber, AccountId};
|
||||
use runtime::{system, session, democracy};
|
||||
|
||||
/// The balance of an account.
|
||||
pub type Balance = u64;
|
||||
|
||||
/// The amount of bonding period left in an account. Measured in eras.
|
||||
pub type Bondage = u64;
|
||||
|
||||
storage_items! {
|
||||
// The length of the bonding duration in eras.
|
||||
pub BondingDuration get(bonding_duration): b"sta:loc" => required BlockNumber;
|
||||
// The length of a staking era in sessions.
|
||||
pub ValidatorCount get(validator_count): b"sta:vac" => required u32;
|
||||
// The length of a staking era in sessions.
|
||||
pub SessionsPerEra get(sessions_per_era): b"sta:spe" => required BlockNumber;
|
||||
// The total amount of stake on the system.
|
||||
pub TotalStake get(total_stake): b"sta:tot" => required Balance;
|
||||
// The fee to be paid for making a transaction.
|
||||
pub TransactionFee get(transaction_fee): b"sta:fee" => required Balance;
|
||||
|
||||
// The current era index.
|
||||
pub CurrentEra get(current_era): b"sta:era" => required BlockNumber;
|
||||
// All the accounts with a desire to stake.
|
||||
pub Intention: b"sta:wil:" => list [ AccountId ];
|
||||
// The next value of sessions per era.
|
||||
pub NextSessionsPerEra get(next_sessions_per_era): b"sta:nse" => BlockNumber;
|
||||
// The block number at which the era length last changed.
|
||||
pub LastEraLengthChange get(last_era_length_change): b"sta:lec" => default BlockNumber;
|
||||
|
||||
// The balance of a given account.
|
||||
pub FreeBalanceOf get(free_balance_of): b"sta:bal:" => default map [ AccountId => Balance ];
|
||||
|
||||
// The amount of the balance of a given account that is exterally reserved; this can still get
|
||||
// slashed, but gets slashed last of all.
|
||||
pub ReservedBalanceOf get(reserved_balance_of): b"sta:lbo:" => default map [ AccountId => Balance ];
|
||||
|
||||
// The block at which the `who`'s funds become entirely liquid.
|
||||
pub BondageOf get(bondage_of): b"sta:bon:" => default map [ AccountId => Bondage ];
|
||||
|
||||
// The code associated with an account.
|
||||
pub CodeOf: b"sta:cod:" => default map [ AccountId => Vec<u8> ]; // TODO Vec<u8> values should be optimised to not do a length prefix.
|
||||
|
||||
// The storage items associated with an account/key.
|
||||
pub StorageOf: b"sta:sto:" => map [ (AccountId, Vec<u8>) => Vec<u8> ]; // TODO: keys should also be able to take AsRef<KeyType> to ensure Vec<u8>s can be passed as &[u8]
|
||||
}
|
||||
|
||||
/// The length of a staking era in blocks.
|
||||
pub fn era_length() -> BlockNumber {
|
||||
SessionsPerEra::get() * session::length()
|
||||
}
|
||||
|
||||
/// The combined balance of `who`.
|
||||
pub fn balance(who: &AccountId) -> Balance {
|
||||
FreeBalanceOf::get(who) + ReservedBalanceOf::get(who)
|
||||
}
|
||||
|
||||
/// Some result as `slash(who, value)` (but without the side-effects) asuming there are no
|
||||
/// balance changes in the meantime.
|
||||
pub fn can_slash(who: &AccountId, value: Balance) -> bool {
|
||||
balance(who) >= value
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub enum LockStatus {
|
||||
Liquid,
|
||||
LockedUntil(BlockNumber),
|
||||
Staked,
|
||||
}
|
||||
|
||||
/// The block at which the `who`'s funds become entirely liquid.
|
||||
pub fn unlock_block(who: &AccountId) -> LockStatus {
|
||||
match BondageOf::get(who) {
|
||||
i if i == Bondage::max_value() => LockStatus::Staked,
|
||||
i if i <= system::block_number() => LockStatus::Liquid,
|
||||
i => LockStatus::LockedUntil(i),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PublicPass<'a> (&'a AccountId);
|
||||
|
||||
const NOBODY: AccountId = [0u8; 32];
|
||||
|
||||
impl<'a> PublicPass<'a> {
|
||||
pub fn new(transactor: &AccountId) -> PublicPass {
|
||||
let b = FreeBalanceOf::get(transactor);
|
||||
let transaction_fee = TransactionFee::get();
|
||||
assert!(b >= transaction_fee, "attempt to transact without enough funds to pay fee");
|
||||
FreeBalanceOf::insert(transactor, b - transaction_fee);
|
||||
PublicPass(transactor)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test(signed: &AccountId) -> PublicPass {
|
||||
PublicPass(signed)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn nobody() -> PublicPass<'static> {
|
||||
PublicPass(&NOBODY)
|
||||
}
|
||||
|
||||
/// Create a smart-contract account.
|
||||
pub fn create(self, code: &[u8], value: Balance) {
|
||||
// commit anything that made it this far to storage
|
||||
if let Some(commit) = private::effect_create(self.0, code, value, private::DirectExt) {
|
||||
private::commit_state(commit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ops::Deref for PublicPass<'a> {
|
||||
type Target = AccountId;
|
||||
fn deref(&self) -> &AccountId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn transfer(dest: AccountId, value: Balance) = 0;
|
||||
fn stake() = 1;
|
||||
fn unstake() = 2;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Transfer some unlocked staking balance to another staker.
|
||||
/// TODO: probably want to state gas-limit and gas-price.
|
||||
fn transfer(self, dest: AccountId, value: Balance) {
|
||||
// commit anything that made it this far to storage
|
||||
if let Some(commit) = private::effect_transfer(&self, &dest, value, private::DirectExt) {
|
||||
private::commit_state(commit);
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn stake(self) {
|
||||
let mut intentions = Intention::items();
|
||||
// can't be in the list twice.
|
||||
assert!(intentions.iter().find(|&t| *t == *self).is_none(), "Cannot stake if already staked.");
|
||||
intentions.push(self.clone());
|
||||
Intention::set_items(&intentions);
|
||||
BondageOf::insert(*self, u64::max_value());
|
||||
}
|
||||
|
||||
/// Retract the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn unstake(self) {
|
||||
let mut intentions = Intention::items();
|
||||
if let Some(position) = intentions.iter().position(|&t| t == *self) {
|
||||
intentions.swap_remove(position);
|
||||
} else {
|
||||
panic!("Cannot unstake if not already staked.");
|
||||
}
|
||||
Intention::set_items(&intentions);
|
||||
BondageOf::insert(*self, CurrentEra::get() + BondingDuration::get());
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_sessions_per_era(new: BlockNumber) = 0;
|
||||
fn set_bonding_duration(new: BlockNumber) = 1;
|
||||
fn set_validator_count(new: u32) = 2;
|
||||
fn force_new_era() = 3;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for democracy::PrivPass {
|
||||
/// Set the number of sessions in an era.
|
||||
fn set_sessions_per_era(self, new: BlockNumber) {
|
||||
NextSessionsPerEra::put(&new);
|
||||
}
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
fn set_bonding_duration(self, new: BlockNumber) {
|
||||
BondingDuration::put(&new);
|
||||
}
|
||||
|
||||
/// The length of a staking era in sessions.
|
||||
fn set_validator_count(self, new: u32) {
|
||||
ValidatorCount::put(&new);
|
||||
}
|
||||
|
||||
/// Force there to be a new era. This also forces a new session immediately after.
|
||||
fn force_new_era(self) {
|
||||
new_era();
|
||||
session::internal::rotate_session();
|
||||
}
|
||||
}
|
||||
|
||||
// Each identity's stake may be in one of three bondage states, given by an integer:
|
||||
// - n | n <= CurrentEra::get(): inactive: free to be transferred.
|
||||
// - ~0: active: currently representing a validator.
|
||||
// - n | n > CurrentEra::get(): deactivating: recently representing a validator and not yet
|
||||
// ready for transfer.
|
||||
|
||||
mod private {
|
||||
use super::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ChangeEntry {
|
||||
balance: Option<Balance>,
|
||||
code: Option<Vec<u8>>,
|
||||
storage: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl ChangeEntry {
|
||||
pub fn balance_changed(b: Balance) -> Self {
|
||||
ChangeEntry { balance: Some(b), code: None, storage: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
type State = BTreeMap<AccountId, ChangeEntry>;
|
||||
|
||||
pub trait Externalities {
|
||||
fn get_storage(&self, account: &AccountId, location: &[u8]) -> Option<Vec<u8>>;
|
||||
fn get_code(&self, account: &AccountId) -> Vec<u8>;
|
||||
fn get_balance(&self, account: &AccountId) -> Balance;
|
||||
}
|
||||
|
||||
struct Ext<F1, F3, F5> where
|
||||
F1 : Fn(&AccountId, &[u8]) -> Option<Vec<u8>>,
|
||||
F3 : Fn(&AccountId) -> Vec<u8>,
|
||||
F5 : Fn(&AccountId) -> Balance
|
||||
{
|
||||
do_get_storage: F1,
|
||||
do_get_code: F3,
|
||||
do_get_balance: F5,
|
||||
}
|
||||
|
||||
pub struct DirectExt;
|
||||
impl Externalities for DirectExt {
|
||||
fn get_storage(&self, account: &AccountId, location: &[u8]) -> Option<Vec<u8>> {
|
||||
StorageOf::get(&(*account, location.to_vec()))
|
||||
}
|
||||
fn get_code(&self, account: &AccountId) -> Vec<u8> {
|
||||
CodeOf::get(account)
|
||||
}
|
||||
fn get_balance(&self, account: &AccountId) -> Balance {
|
||||
FreeBalanceOf::get(account)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F1, F3, F5> Externalities for Ext<F1, F3, F5> where
|
||||
F1 : Fn(&AccountId, &[u8]) -> Option<Vec<u8>>,
|
||||
F3 : Fn(&AccountId) -> Vec<u8>,
|
||||
F5 : Fn(&AccountId) -> Balance
|
||||
{
|
||||
fn get_storage(&self, account: &AccountId, location: &[u8]) -> Option<Vec<u8>> {
|
||||
(self.do_get_storage)(account, location)
|
||||
}
|
||||
fn get_code(&self, account: &AccountId) -> Vec<u8> {
|
||||
(self.do_get_code)(account)
|
||||
}
|
||||
fn get_balance(&self, account: &AccountId) -> Balance {
|
||||
(self.do_get_balance)(account)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commit_state(s: State) {
|
||||
for (address, changed) in s.into_iter() {
|
||||
if let Some(balance) = changed.balance {
|
||||
FreeBalanceOf::insert(address, balance);
|
||||
}
|
||||
if let Some(code) = changed.code {
|
||||
CodeOf::insert(&address, &code);
|
||||
}
|
||||
for (k, v) in changed.storage.into_iter() {
|
||||
if let Some(value) = v {
|
||||
StorageOf::insert(&(address, k), &value);
|
||||
} else {
|
||||
StorageOf::remove(&(address, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_state(commit_state: State, local: &mut State) {
|
||||
for (address, changed) in commit_state.into_iter() {
|
||||
match local.entry(address) {
|
||||
Entry::Occupied(e) => {
|
||||
let mut value = e.into_mut();
|
||||
if changed.balance.is_some() {
|
||||
value.balance = changed.balance;
|
||||
}
|
||||
if changed.code.is_some() {
|
||||
value.code = changed.code;
|
||||
}
|
||||
value.storage.extend(changed.storage.into_iter());
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn effect_create<E: Externalities>(
|
||||
transactor: &AccountId,
|
||||
code: &[u8],
|
||||
value: Balance,
|
||||
ext: E
|
||||
) -> Option<State> {
|
||||
let from_balance = ext.get_balance(transactor);
|
||||
// TODO: a fee.
|
||||
assert!(from_balance >= value);
|
||||
|
||||
let mut dest_pre = blake2_256(code).to_vec();
|
||||
dest_pre.extend(&transactor[..]);
|
||||
let dest = blake2_256(&dest_pre);
|
||||
|
||||
// early-out if degenerate.
|
||||
if &dest == transactor {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut local = BTreeMap::new();
|
||||
|
||||
// two inserts are safe
|
||||
assert!(&dest != transactor);
|
||||
local.insert(dest, ChangeEntry { balance: Some(value), code: Some(code.to_vec()), storage: Default::default() });
|
||||
local.insert(transactor.clone(), ChangeEntry::balance_changed(from_balance - value));
|
||||
|
||||
Some(local)
|
||||
}
|
||||
|
||||
pub fn effect_transfer<E: Externalities>(
|
||||
transactor: &AccountId,
|
||||
dest: &AccountId,
|
||||
value: Balance,
|
||||
ext: E
|
||||
) -> Option<State> {
|
||||
let from_balance = ext.get_balance(transactor);
|
||||
assert!(from_balance >= value);
|
||||
|
||||
let to_balance = ext.get_balance(dest);
|
||||
assert!(BondageOf::get(transactor) <= BondageOf::get(dest));
|
||||
assert!(to_balance + value > to_balance); // no overflow
|
||||
|
||||
// TODO: a fee, based upon gaslimit/gasprice.
|
||||
// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime
|
||||
// code in contract itself and use that.
|
||||
|
||||
let local: RefCell<State> = RefCell::new(BTreeMap::new());
|
||||
|
||||
if transactor != dest {
|
||||
let mut local = local.borrow_mut();
|
||||
local.insert(transactor.clone(), ChangeEntry::balance_changed(from_balance - value));
|
||||
local.insert(dest.clone(), ChangeEntry::balance_changed(to_balance + value));
|
||||
}
|
||||
|
||||
let should_commit = {
|
||||
// Our local ext: Should be used for any transfers and creates that happen internally.
|
||||
let ext = || Ext {
|
||||
do_get_storage: |account: &AccountId, location: &[u8]|
|
||||
local.borrow().get(account)
|
||||
.and_then(|a| a.storage.get(location))
|
||||
.cloned()
|
||||
.unwrap_or_else(|| ext.get_storage(account, location)),
|
||||
do_get_code: |account: &AccountId|
|
||||
local.borrow().get(account)
|
||||
.and_then(|a| a.code.clone())
|
||||
.unwrap_or_else(|| ext.get_code(account)),
|
||||
do_get_balance: |account: &AccountId|
|
||||
local.borrow().get(account)
|
||||
.and_then(|a| a.balance)
|
||||
.unwrap_or_else(|| ext.get_balance(account)),
|
||||
};
|
||||
let mut transfer = |inner_dest: &AccountId, value: Balance| {
|
||||
if let Some(commit_state) = effect_transfer(dest, inner_dest, value, ext()) {
|
||||
merge_state(commit_state, &mut *local.borrow_mut());
|
||||
}
|
||||
};
|
||||
let mut create = |code: &[u8], value: Balance| {
|
||||
if let Some(commit_state) = effect_create(dest, code, value, ext()) {
|
||||
merge_state(commit_state, &mut *local.borrow_mut());
|
||||
}
|
||||
};
|
||||
let mut put_storage = |location: Vec<u8>, value: Option<Vec<u8>>| {
|
||||
local.borrow_mut()
|
||||
.entry(dest.clone())
|
||||
.or_insert(Default::default())
|
||||
.storage.insert(location, value);
|
||||
};
|
||||
|
||||
// TODO: logging (logs are just appended into a notable storage-based vector and cleared every
|
||||
// block).
|
||||
// TODO: execute code with ext(), put_storage, create and transfer as externalities.
|
||||
true
|
||||
};
|
||||
|
||||
if should_commit {
|
||||
Some(local.into_inner())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
|
||||
/// Hook to be called after to transaction processing.
|
||||
pub fn check_new_era() {
|
||||
// check block number and call new_era if necessary.
|
||||
if (system::block_number() - LastEraLengthChange::get()) % era_length() == 0 {
|
||||
new_era();
|
||||
}
|
||||
}
|
||||
|
||||
/// Deduct from an unbonded balance. true if it happened.
|
||||
pub fn deduct_unbonded(who: &AccountId, value: Balance) -> bool {
|
||||
if let LockStatus::Liquid = unlock_block(who) {
|
||||
let b = FreeBalanceOf::get(who);
|
||||
if b >= value {
|
||||
FreeBalanceOf::insert(who, &(b - value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Refund some balance.
|
||||
pub fn refund(who: &AccountId, value: Balance) {
|
||||
FreeBalanceOf::insert(who, &(FreeBalanceOf::get(who) + value))
|
||||
}
|
||||
|
||||
/// Will slash any balance, but prefer free over reserved.
|
||||
pub fn slash(who: &AccountId, value: Balance) -> bool {
|
||||
let free_balance = FreeBalanceOf::get(who);
|
||||
let free_slash = cmp::min(free_balance, value);
|
||||
FreeBalanceOf::insert(who, &(free_balance - free_slash));
|
||||
if free_slash < value {
|
||||
slash_reserved(who, value - free_slash)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves `value` from balance to reserved balance.
|
||||
pub fn reserve_balance(who: &AccountId, value: Balance) {
|
||||
let b = FreeBalanceOf::get(who);
|
||||
assert!(b >= value);
|
||||
FreeBalanceOf::insert(who, &(b - value));
|
||||
ReservedBalanceOf::insert(who, &(ReservedBalanceOf::get(who) + value));
|
||||
}
|
||||
|
||||
/// Moves `value` from reserved balance to balance.
|
||||
pub fn unreserve_balance(who: &AccountId, value: Balance) {
|
||||
let b = ReservedBalanceOf::get(who);
|
||||
let value = cmp::min(b, value);
|
||||
ReservedBalanceOf::insert(who, &(b - value));
|
||||
FreeBalanceOf::insert(who, &(FreeBalanceOf::get(who) + value));
|
||||
}
|
||||
|
||||
/// Moves `value` from reserved balance to balance.
|
||||
pub fn slash_reserved(who: &AccountId, value: Balance) -> bool {
|
||||
let b = ReservedBalanceOf::get(who);
|
||||
let slash = cmp::min(b, value);
|
||||
ReservedBalanceOf::insert(who, &(b - slash));
|
||||
value == slash
|
||||
}
|
||||
|
||||
/// Moves `value` from reserved balance to balance.
|
||||
pub fn transfer_reserved_balance(slashed: &AccountId, beneficiary: &AccountId, value: Balance) -> bool {
|
||||
let b = ReservedBalanceOf::get(slashed);
|
||||
let slash = cmp::min(b, value);
|
||||
ReservedBalanceOf::insert(slashed, &(b - slash));
|
||||
FreeBalanceOf::insert(beneficiary, &(FreeBalanceOf::get(beneficiary) + slash));
|
||||
slash == value
|
||||
}
|
||||
}
|
||||
|
||||
/// The era has changed - enact new staking set.
|
||||
///
|
||||
/// NOTE: This always happens immediately before a session change to ensure that new validators
|
||||
/// get a chance to set their session keys.
|
||||
fn new_era() {
|
||||
// Increment current era.
|
||||
CurrentEra::put(&(CurrentEra::get() + 1));
|
||||
|
||||
// Enact era length change.
|
||||
if let Some(next_spe) = NextSessionsPerEra::get() {
|
||||
if next_spe != SessionsPerEra::get() {
|
||||
SessionsPerEra::put(&next_spe);
|
||||
LastEraLengthChange::put(&system::block_number());
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate desired staking amounts and nominations and optimise to find the best
|
||||
// combination of validators, then use session::internal::set_validators().
|
||||
// for now, this just orders would-be stakers by their balances and chooses the top-most
|
||||
// ValidatorCount::get() of them.
|
||||
let mut intentions = Intention::items()
|
||||
.into_iter()
|
||||
.map(|v| (balance(&v), v))
|
||||
.collect::<Vec<_>>();
|
||||
intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1));
|
||||
session::internal::set_validators(
|
||||
&intentions.into_iter()
|
||||
.map(|(_, v)| v)
|
||||
.take(ValidatorCount::get() as usize)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub mod testing {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use keyring::Keyring::*;
|
||||
use runtime::session;
|
||||
use super::public::{Call, Dispatch};
|
||||
use super::privileged::{Dispatch as PrivDispatch, Call as PrivCall};
|
||||
|
||||
pub fn externalities(session_length: u64, sessions_per_era: u64, current_era: u64) -> TestExternalities {
|
||||
let extras: TestExternalities = map![
|
||||
twox_128(&Intention::len_key()).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&Intention::key_for(0)).to_vec() => Alice.to_raw_public_vec(),
|
||||
twox_128(&Intention::key_for(1)).to_vec() => Bob.to_raw_public_vec(),
|
||||
twox_128(&Intention::key_for(2)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(SessionsPerEra::key()).to_vec() => vec![].and(&sessions_per_era),
|
||||
twox_128(ValidatorCount::key()).to_vec() => vec![].and(&3u64),
|
||||
twox_128(BondingDuration::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(TransactionFee::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(CurrentEra::key()).to_vec() => vec![].and(¤t_era),
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
session::testing::externalities(session_length).into_iter().chain(extras.into_iter()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::internal::*;
|
||||
use super::privileged::*;
|
||||
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use keyring::Keyring::*;
|
||||
use demo_primitives::AccountId;
|
||||
use runtime::{staking, session};
|
||||
use runtime::democracy::PrivPass;
|
||||
use runtime::staking::public::{Call, Dispatch};
|
||||
use runtime::staking::privileged::{Call as PCall, Dispatch as PDispatch};
|
||||
|
||||
#[test]
|
||||
fn staking_should_work() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(session::SessionLength::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(session::Validators::key()).to_vec() => vec![].and(&vec![[10u8; 32], [20; 32]]),
|
||||
twox_128(CurrentEra::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(SessionsPerEra::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(ValidatorCount::key()).to_vec() => vec![].and(&2u32),
|
||||
twox_128(BondingDuration::key()).to_vec() => vec![].and(&3u64),
|
||||
twox_128(TotalStake::key()).to_vec() => vec![].and(&100u64),
|
||||
twox_128(TransactionFee::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![].and(&10u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Bob)).to_vec() => vec![].and(&20u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Charlie)).to_vec() => vec![].and(&30u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Dave)).to_vec() => vec![].and(&40u64)
|
||||
];
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(era_length(), 2u64);
|
||||
assert_eq!(ValidatorCount::get(), 2);
|
||||
assert_eq!(BondingDuration::get(), 3);
|
||||
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
|
||||
|
||||
// Block 1: Add three validators. No obvious change.
|
||||
system::testing::set_block_number(1);
|
||||
public::Call::stake().dispatch(PublicPass::new(&Alice));
|
||||
PublicPass::new(&Bob).stake();
|
||||
PublicPass::new(&Dave).stake();
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
|
||||
|
||||
// Block 2: New validator set now.
|
||||
system::testing::set_block_number(2);
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![Dave.to_raw_public(), Bob.into()]);
|
||||
|
||||
// Block 3: Unstake highest, introduce another staker. No change yet.
|
||||
system::testing::set_block_number(3);
|
||||
PublicPass::new(&Charlie).stake();
|
||||
PublicPass::new(&Dave).unstake();
|
||||
check_new_era();
|
||||
|
||||
// Block 4: New era - validators change.
|
||||
system::testing::set_block_number(4);
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![Charlie.to_raw_public(), Bob.into()]);
|
||||
|
||||
// Block 5: Transfer stake from highest to lowest. No change yet.
|
||||
system::testing::set_block_number(5);
|
||||
PublicPass::new(&Dave).transfer(Alice.to_raw_public(), 40);
|
||||
check_new_era();
|
||||
|
||||
// Block 6: Lowest now validator.
|
||||
system::testing::set_block_number(6);
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![Alice.to_raw_public(), Charlie.into()]);
|
||||
|
||||
// Block 7: Unstake three. No change yet.
|
||||
system::testing::set_block_number(7);
|
||||
PublicPass::new(&Charlie).unstake();
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![Alice.to_raw_public(), Charlie.into()]);
|
||||
|
||||
// Block 8: Back to one and two.
|
||||
system::testing::set_block_number(8);
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![Alice.to_raw_public(), Bob.into()]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_eras_work() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(session::SessionLength::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(SessionsPerEra::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(ValidatorCount::key()).to_vec() => vec![].and(&2u32),
|
||||
twox_128(CurrentEra::key()).to_vec() => vec![].and(&0u64)
|
||||
];
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(era_length(), 2u64);
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 0u64);
|
||||
|
||||
// Block 1: No change.
|
||||
system::testing::set_block_number(1);
|
||||
check_new_era();
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 0u64);
|
||||
|
||||
// Block 2: Simple era change.
|
||||
system::testing::set_block_number(2);
|
||||
check_new_era();
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 1u64);
|
||||
|
||||
// Block 3: Schedule an era length change; no visible changes.
|
||||
system::testing::set_block_number(3);
|
||||
PrivPass::test().set_sessions_per_era(3);
|
||||
check_new_era();
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 1u64);
|
||||
|
||||
// Block 4: Era change kicks in.
|
||||
system::testing::set_block_number(4);
|
||||
check_new_era();
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 2u64);
|
||||
|
||||
// Block 5: No change.
|
||||
system::testing::set_block_number(5);
|
||||
check_new_era();
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 2u64);
|
||||
|
||||
// Block 6: No change.
|
||||
system::testing::set_block_number(6);
|
||||
check_new_era();
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 2u64);
|
||||
|
||||
// Block 7: Era increment.
|
||||
system::testing::set_block_number(7);
|
||||
check_new_era();
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 3u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_works() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 42);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(FreeBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(balance(&Bob), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_works() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 112);
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn staking_balance_transfer_when_bonded_panics() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
PublicPass::new(&Alice).stake();
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserving_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
|
||||
assert_eq!(balance(&Alice), 111);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 111);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
|
||||
reserve_balance(&Alice, 69);
|
||||
|
||||
assert_eq!(balance(&Alice), 111);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn staking_balance_transfer_when_reserved_panics() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 69);
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deducting_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
assert!(deduct_unbonded(&Alice, 69));
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deducting_balance_should_fail_when_bonded() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![].and(&111u64),
|
||||
twox_128(&BondageOf::key_for(*Alice)).to_vec() => vec![].and(&2u64)
|
||||
];
|
||||
with_externalities(&mut t, || {
|
||||
system::testing::set_block_number(1);
|
||||
assert_eq!(unlock_block(&Alice), LockStatus::LockedUntil(2));
|
||||
assert!(!deduct_unbonded(&Alice, 69));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refunding_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 42);
|
||||
refund(&Alice, 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 111);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 69);
|
||||
assert!(slash(&Alice, 69));
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 42);
|
||||
reserve_balance(&Alice, 21);
|
||||
assert!(!slash(&Alice, 69));
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreserving_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 111);
|
||||
unreserve_balance(&Alice, 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_reserved_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 111);
|
||||
assert!(slash_reserved(&Alice, 42));
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 42);
|
||||
assert!(!slash_reserved(&Alice, 69));
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_reserved_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 111);
|
||||
assert!(transfer_reserved_balance(&Alice, &Bob, 42));
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Bob), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 42);
|
||||
assert!(!transfer_reserved_balance(&Alice, &Bob, 69));
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(ReservedBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Bob), 42);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
|
||||
//! and depositing logs.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::mem;
|
||||
use runtime_io::{print, storage_root, enumerated_trie_root};
|
||||
use codec::{KeyedVec, Slicable};
|
||||
use runtime_support::{Hashable, storage, StorageValue, StorageMap};
|
||||
use demo_primitives::{AccountId, Hash, TxOrder, BlockNumber, Header, Log};
|
||||
use block::{self, Block};
|
||||
use transaction::UncheckedTransaction;
|
||||
use runtime::{staking, session};
|
||||
use runtime::democracy::PrivPass;
|
||||
use dispatch;
|
||||
use safe_mix::TripletMix;
|
||||
|
||||
storage_items! {
|
||||
pub Nonce get(nonce): b"sys:non" => default map [ AccountId => TxOrder ];
|
||||
pub BlockHashAt get(block_hash): b"sys:old" => required map [ BlockNumber => Hash ];
|
||||
RandomSeed get(random_seed): b"sys:rnd" => required Hash;
|
||||
// The current block number being processed. Set by `execute_block`.
|
||||
Number get(block_number): b"sys:num" => required BlockNumber;
|
||||
ParentHash get(parent_hash): b"sys:pha" => required Hash;
|
||||
TransactionsRoot get(transactions_root): b"sys:txr" => required Hash;
|
||||
Digest: b"sys:dig" => default block::Digest;
|
||||
}
|
||||
|
||||
pub const CODE: &'static[u8] = b":code";
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_code(new: Vec<u8>) = 0;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
/// Set the new code.
|
||||
fn set_code(self, new: Vec<u8>) {
|
||||
storage::unhashed::put_raw(CODE, &new);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
|
||||
struct CheckedTransaction(UncheckedTransaction);
|
||||
|
||||
/// Deposits a log and ensures it matches the blocks log data.
|
||||
pub fn deposit_log(log: Log) {
|
||||
let mut l = Digest::get();
|
||||
l.logs.push(log);
|
||||
Digest::put(l);
|
||||
}
|
||||
|
||||
/// Actually execute all transitioning for `block`.
|
||||
pub fn execute_block(mut block: Block) {
|
||||
initialise_block(&block.header);
|
||||
|
||||
// any initial checks
|
||||
initial_checks(&block);
|
||||
|
||||
// execute transactions
|
||||
block.transactions.iter().cloned().for_each(super::execute_transaction);
|
||||
|
||||
// post-transactional book-keeping.
|
||||
staking::internal::check_new_era();
|
||||
session::internal::check_rotate_session();
|
||||
|
||||
// any final checks
|
||||
final_checks(&block);
|
||||
|
||||
// any stuff that we do after taking the storage root.
|
||||
post_finalise(&block.header);
|
||||
}
|
||||
|
||||
/// Start the execution of a particular block.
|
||||
pub fn initialise_block(mut header: &Header) {
|
||||
// populate environment from header.
|
||||
Number::put(header.number);
|
||||
ParentHash::put(header.parent_hash);
|
||||
TransactionsRoot::put(header.transaction_root);
|
||||
RandomSeed::put(calculate_random());
|
||||
}
|
||||
|
||||
/// Execute a transaction outside of the block execution function.
|
||||
/// This doesn't attempt to validate anything regarding the block.
|
||||
pub fn execute_transaction(utx: UncheckedTransaction) {
|
||||
super::execute_transaction(utx);
|
||||
}
|
||||
|
||||
/// Finalise the block - it is up the caller to ensure that all header fields are valid
|
||||
/// except state-root.
|
||||
pub fn finalise_block() -> Header {
|
||||
staking::internal::check_new_era();
|
||||
session::internal::check_rotate_session();
|
||||
|
||||
RandomSeed::kill();
|
||||
let header = Header {
|
||||
number: Number::take(),
|
||||
digest: Digest::take(),
|
||||
parent_hash: ParentHash::take(),
|
||||
transaction_root: TransactionsRoot::take(),
|
||||
state_root: storage_root().into(),
|
||||
};
|
||||
|
||||
post_finalise(&header);
|
||||
|
||||
header
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_transaction(utx: UncheckedTransaction) {
|
||||
use ::transaction;
|
||||
|
||||
// Verify the signature is good.
|
||||
let tx = match transaction::check(utx) {
|
||||
Ok(tx) => tx,
|
||||
Err(_) => panic!("All transactions should be properly signed"),
|
||||
};
|
||||
|
||||
{
|
||||
// check nonce
|
||||
let expected_nonce: TxOrder = Nonce::get(&tx.signed);
|
||||
assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce");
|
||||
|
||||
// increment nonce in storage
|
||||
Nonce::insert(&tx.signed, &(expected_nonce + 1));
|
||||
}
|
||||
|
||||
// decode parameters and dispatch
|
||||
let tx = tx.drain().transaction;
|
||||
tx.function.dispatch(staking::PublicPass::new(&tx.signed));
|
||||
}
|
||||
|
||||
fn initial_checks(block: &Block) {
|
||||
let ref header = block.header;
|
||||
|
||||
// check parent_hash is correct.
|
||||
assert!(
|
||||
header.number > 0 && BlockHashAt::get(&(header.number - 1)) == header.parent_hash,
|
||||
"Parent hash should be valid."
|
||||
);
|
||||
|
||||
// check transaction trie root represents the transactions.
|
||||
let txs = block.transactions.iter().map(Slicable::encode).collect::<Vec<_>>();
|
||||
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
|
||||
let txs_root = enumerated_trie_root(&txs).into();
|
||||
info_expect_equal_hash(&header.transaction_root, &txs_root);
|
||||
assert!(header.transaction_root == txs_root, "Transaction trie root must be valid.");
|
||||
}
|
||||
|
||||
fn final_checks(block: &Block) {
|
||||
let ref header = block.header;
|
||||
|
||||
// check digest
|
||||
assert!(header.digest == Digest::get());
|
||||
|
||||
// remove temporaries.
|
||||
kill_temps();
|
||||
|
||||
// check storage root.
|
||||
let storage_root = storage_root().into();
|
||||
info_expect_equal_hash(&header.state_root, &storage_root);
|
||||
assert!(header.state_root == storage_root, "Storage root must match that calculated.");
|
||||
}
|
||||
|
||||
fn kill_temps() {
|
||||
Number::kill();
|
||||
ParentHash::kill();
|
||||
RandomSeed::kill();
|
||||
Digest::kill();
|
||||
TransactionsRoot::kill();
|
||||
}
|
||||
|
||||
fn post_finalise(header: &Header) {
|
||||
// store the header hash in storage; we can't do it before otherwise there would be a
|
||||
// cyclic dependency.
|
||||
BlockHashAt::insert(&header.number, &header.blake2_256().into());
|
||||
}
|
||||
|
||||
fn calculate_random() -> Hash {
|
||||
let c = block_number() - 1;
|
||||
(0..81)
|
||||
.map(|i| if c >= i { block_hash(c - i) } else { Default::default() })
|
||||
.triplet_mix()
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
if given != expected {
|
||||
println!("Hash: given={}, expected={}", HexDisplay::from(&given.0), HexDisplay::from(&expected.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
|
||||
if given != expected {
|
||||
print("Hash not equal");
|
||||
print(&given.0[..]);
|
||||
print(&expected.0[..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub mod testing {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
use codec::Joiner;
|
||||
|
||||
pub fn externalities() -> TestExternalities {
|
||||
map![
|
||||
twox_128(&BlockHashAt::key_for(&0)).to_vec() => [69u8; 32].encode(),
|
||||
twox_128(Number::key()).to_vec() => 1u64.encode(),
|
||||
twox_128(ParentHash::key()).to_vec() => [69u8; 32].encode(),
|
||||
twox_128(RandomSeed::key()).to_vec() => [0u8; 32].encode()
|
||||
]
|
||||
}
|
||||
|
||||
pub fn set_block_number(n: BlockNumber) {
|
||||
Number::put(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::internal::*;
|
||||
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use runtime_support::StorageValue;
|
||||
use codec::{Joiner, KeyedVec, Slicable};
|
||||
use keyring::Keyring::*;
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use demo_primitives::{Header, Digest};
|
||||
use transaction::{UncheckedTransaction, Transaction};
|
||||
use runtime::staking;
|
||||
use dispatch::public::Call as PubCall;
|
||||
use runtime::staking::public::Call as StakingCall;
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_dispatch_works() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*One)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![10u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(&BlockHashAt::key_for(&0)).to_vec() => [69u8; 32].encode()
|
||||
];
|
||||
|
||||
let tx = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: One.into(),
|
||||
nonce: 0,
|
||||
function: PubCall::Staking(StakingCall::transfer(Two.into(), 69)),
|
||||
},
|
||||
signature: hex!("3a682213cb10e8e375fe0817fe4d220a4622d910088809ed7fc8b4ea3871531dbadb22acfedd28a100a0b7bd2d274e0ff873655b13c88f4640b5569db3222706").into(),
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
internal::initialise_block(&Header::from_block_number(1));
|
||||
internal::execute_transaction(tx);
|
||||
assert_eq!(staking::balance(&One), 32);
|
||||
assert_eq!(staking::balance(&Two), 69);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
staking::testing::externalities(2, 2, 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_import_works() {
|
||||
let mut t = new_test_ext();
|
||||
|
||||
let h = Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("cc3f1f5db826013193e502c76992b5e933b12367e37a269a9822b89218323e9f").into(),
|
||||
transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
|
||||
let b = Block {
|
||||
header: h,
|
||||
transactions: vec![],
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
execute_block(b);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn block_import_of_bad_state_root_fails() {
|
||||
let mut t = new_test_ext();
|
||||
|
||||
let h = Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: [0u8; 32].into(),
|
||||
transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
|
||||
let b = Block {
|
||||
header: h,
|
||||
transactions: vec![],
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
execute_block(b);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn block_import_of_bad_transaction_root_fails() {
|
||||
let mut t = new_test_ext();
|
||||
|
||||
let h = Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("1ab2dbb7d4868a670b181327b0b6a58dc64b10cfb9876f737a5aa014b8da31e0").into(),
|
||||
transaction_root: [0u8; 32].into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
|
||||
let b = Block {
|
||||
header: h,
|
||||
transactions: vec![],
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
execute_block(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Timestamp manager: just handles the current timestamp.
|
||||
|
||||
use runtime_support::storage::StorageValue;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
pub type Timestamp = u64;
|
||||
|
||||
storage_items! {
|
||||
pub Now: b"tim:val" => required Timestamp;
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn set(now: Timestamp) = 0;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Set the current time.
|
||||
fn set(self, now: Timestamp) {
|
||||
Now::put(&now);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::public::*;
|
||||
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use runtime_support::storage::StorageValue;
|
||||
use runtime::timestamp;
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use demo_primitives::AccountId;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
#[test]
|
||||
fn timestamp_works() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(Now::key()).to_vec() => vec![].and(&42u64)
|
||||
];
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(Now::get(), 42);
|
||||
PublicPass::nobody().set(69);
|
||||
assert_eq!(Now::get(), 69);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction type.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::ops;
|
||||
use codec::{Input, Slicable};
|
||||
use demo_primitives::{AccountId, TxOrder, Signature};
|
||||
use dispatch::PubCall;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
/// A vetted and verified transaction from the external world.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Transaction {
|
||||
/// Who signed it (note this is not a signature).
|
||||
pub signed: AccountId,
|
||||
/// The number of transactions have come before from the same signer.
|
||||
pub nonce: TxOrder,
|
||||
/// The function that should be called.
|
||||
pub function: PubCall,
|
||||
}
|
||||
|
||||
impl Slicable for Transaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Transaction {
|
||||
signed: Slicable::decode(input)?,
|
||||
nonce: Slicable::decode(input)?,
|
||||
function: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
self.signed.using_encoded(|s| v.extend(s));
|
||||
self.nonce.using_encoded(|s| v.extend(s));
|
||||
self.function.using_encoded(|s| v.extend(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for Transaction {}
|
||||
|
||||
/// A transactions right from the external world. Unchecked.
|
||||
#[derive(Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct UncheckedTransaction {
|
||||
/// The actual transaction information.
|
||||
pub transaction: Transaction,
|
||||
/// The signature; should be an Ed25519 signature applied to the serialised `transaction` field.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl Slicable for UncheckedTransaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of u32, which has the total number of bytes following (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = Slicable::decode(input)?;
|
||||
|
||||
Some(UncheckedTransaction {
|
||||
transaction: Slicable::decode(input)?,
|
||||
signature: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
// need to prefix with the total length as u32 to ensure it's binary comptible with
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.transaction.signed.using_encoded(|s| v.extend(s));
|
||||
self.transaction.nonce.using_encoded(|s| v.extend(s));
|
||||
self.transaction.function.using_encoded(|s| v.extend(s));
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
length.using_encoded(|s| v[0..4].copy_from_slice(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for UncheckedTransaction {}
|
||||
|
||||
impl PartialEq for UncheckedTransaction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl fmt::Debug for UncheckedTransaction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedTransaction({:?})", self.transaction)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-safe indicator that a transaction has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct CheckedTransaction(UncheckedTransaction);
|
||||
|
||||
impl CheckedTransaction {
|
||||
/// Get a reference to the checked signature.
|
||||
pub fn signature(&self) -> &Signature {
|
||||
&self.0.signature
|
||||
}
|
||||
|
||||
/// Get the inner object.
|
||||
pub fn drain(self) -> UncheckedTransaction {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for CheckedTransaction {
|
||||
type Target = Transaction;
|
||||
|
||||
fn deref(&self) -> &Transaction {
|
||||
&self.0.transaction
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the signature on a transaction.
|
||||
///
|
||||
/// On failure, return the transaction back.
|
||||
pub fn check(tx: UncheckedTransaction) -> Result<CheckedTransaction, UncheckedTransaction> {
|
||||
let msg = ::codec::Slicable::encode(&tx.transaction);
|
||||
if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) {
|
||||
Ok(CheckedTransaction(tx))
|
||||
} else {
|
||||
Err(tx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use primitives;
|
||||
use codec::Slicable;
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use dispatch::public::Call;
|
||||
use runtime::timestamp::public::Call as TimestampCall;
|
||||
|
||||
#[test]
|
||||
fn serialize_unchecked() {
|
||||
let tx = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: [1; 32],
|
||||
nonce: 999u64,
|
||||
function: Call::Timestamp(TimestampCall::set(135135)),
|
||||
},
|
||||
signature: primitives::hash::H512([0; 64]),
|
||||
};
|
||||
// 71000000
|
||||
// 0101010101010101010101010101010101010101010101010101010101010101
|
||||
// e703000000000000
|
||||
// 00
|
||||
// df0f0200
|
||||
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
let v = Slicable::encode(&tx);
|
||||
println!("{}", HexDisplay::from(&v));
|
||||
assert_eq!(UncheckedTransaction::decode(&mut &v[..]).unwrap(), tx);
|
||||
}
|
||||
}
|
||||
Generated
+188
-1
@@ -96,6 +96,7 @@ dependencies = [
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
]
|
||||
|
||||
@@ -105,11 +106,21 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"demo-primitives 0.1.0",
|
||||
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
|
||||
"safe-mix 0.1.0",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-council 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
"substrate-runtime-executive 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -249,6 +260,11 @@ name = "integer-sqrt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/paritytech/integer-sqrt-rs.git#f4cf61482096dc98c1273f46a10849d182b4c23c"
|
||||
|
||||
[[package]]
|
||||
name = "integer-sqrt"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "isatty"
|
||||
version = "0.1.6"
|
||||
@@ -337,6 +353,11 @@ name = "nodrop"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.8.0"
|
||||
@@ -546,6 +567,13 @@ dependencies = [
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "safe-mix"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "0.3.3"
|
||||
@@ -605,6 +633,15 @@ dependencies = [
|
||||
"substrate-runtime-std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-keyring"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-primitives"
|
||||
version = "0.1.0"
|
||||
@@ -622,6 +659,74 @@ dependencies = [
|
||||
"uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-consensus"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-council"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-democracy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-executive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-io"
|
||||
version = "0.1.0"
|
||||
@@ -636,6 +741,58 @@ dependencies = [
|
||||
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-session"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-staking"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-std"
|
||||
version = "0.1.0"
|
||||
@@ -650,14 +807,42 @@ name = "substrate-runtime-support"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"environmental 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 0.1.0",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-timestamp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-state-machine"
|
||||
version = "0.1.0"
|
||||
@@ -837,6 +1022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd546ef520ab3745f1aae5f2cdc6de9e6498e94d1ab138b9eb3ddfbf335847fb"
|
||||
"checksum hex-literal-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2ea76da4c7f1a54d01d54985566d3fdd960b2bbd7b970da024821c883c2d9631"
|
||||
"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "<none>"
|
||||
"checksum integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7536fe7f78abedb82f609d87f46f0e0ca0ad31e84597deb8dabd8ed9ad047257"
|
||||
"checksum isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f2a233726c7bb76995cec749d59582e5664823b7245d4970354408f1d79a7a2"
|
||||
"checksum keccak-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f300c1f149cd9ca5214eed24f6e713a597517420fb8b15499824aa916259ec1"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
@@ -848,6 +1034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
|
||||
"checksum memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "013b7e4c5e10c764936ebc6bd3662d8e3c92292d267bf6a42ef3f5cad9c793ee"
|
||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
||||
"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364"
|
||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||
"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22"
|
||||
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
||||
|
||||
@@ -7,22 +7,42 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" }
|
||||
safe-mix = { path = "../../../safe-mix", default-features = false }
|
||||
substrate-codec = { path = "../../../substrate/codec", default-features = false }
|
||||
substrate-primitives = { path = "../../../substrate/primitives", default-features = false }
|
||||
substrate-runtime-std = { path = "../../../substrate/runtime-std", default-features = false }
|
||||
substrate-runtime-io = { path = "../../../substrate/runtime-io", default-features = false }
|
||||
substrate-runtime-support = { path = "../../../substrate/runtime-support", default-features = false }
|
||||
substrate-primitives = { path = "../../../substrate/primitives", default-features = false }
|
||||
substrate-runtime-consensus = { path = "../../../substrate/runtime/consensus", default-features = false }
|
||||
substrate-runtime-council = { path = "../../../substrate/runtime/council", default-features = false }
|
||||
substrate-runtime-democracy = { path = "../../../substrate/runtime/democracy", default-features = false }
|
||||
substrate-runtime-executive = { path = "../../../substrate/runtime/executive", default-features = false }
|
||||
substrate-runtime-primitives = { path = "../../../substrate/runtime/primitives", default-features = false }
|
||||
substrate-runtime-session = { path = "../../../substrate/runtime/session", default-features = false }
|
||||
substrate-runtime-staking = { path = "../../../substrate/runtime/staking", default-features = false }
|
||||
substrate-runtime-system = { path = "../../../substrate/runtime/system", default-features = false }
|
||||
substrate-runtime-timestamp = { path = "../../../substrate/runtime/timestamp", default-features = false }
|
||||
demo-primitives = { path = "../../primitives", default-features = false }
|
||||
integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
std = [
|
||||
"safe-mix/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-council/std",
|
||||
"substrate-runtime-democracy/std",
|
||||
"substrate-runtime-executive/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-system/std",
|
||||
"substrate-runtime-timestamp/std",
|
||||
"demo-primitives/std",
|
||||
]
|
||||
|
||||
|
||||
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
@@ -12,7 +12,7 @@ log = "0.3"
|
||||
hex-literal = "0.1"
|
||||
triehash = "0.1"
|
||||
ed25519 = { path = "../../substrate/ed25519" }
|
||||
app_dirs = "1.2"
|
||||
app_dirs = "1.2.1"
|
||||
substrate-client = { path = "../../substrate/client" }
|
||||
substrate-codec = { path = "../../substrate/codec" }
|
||||
substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
||||
|
||||
@@ -18,8 +18,8 @@ polkadot-transaction-pool = { path = "../transaction-pool" }
|
||||
substrate-bft = { path = "../../substrate/bft" }
|
||||
substrate-codec = { path = "../../substrate/codec" }
|
||||
substrate-primitives = { path = "../../substrate/primitives" }
|
||||
substrate-runtime-support = { path = "../../substrate/runtime-support" }
|
||||
substrate-network = { path = "../../substrate/network" }
|
||||
|
||||
tokio-core = "0.1.12"
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
substrate-client = { path = "../../substrate/client" }
|
||||
|
||||
@@ -41,6 +41,7 @@ extern crate polkadot_transaction_pool as transaction_pool;
|
||||
extern crate substrate_bft as bft;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_network;
|
||||
|
||||
extern crate tokio_core;
|
||||
@@ -58,6 +59,7 @@ use std::sync::Arc;
|
||||
use codec::Slicable;
|
||||
use table::{Table, Context as TableContextTrait};
|
||||
use table::generic::Statement as GenericStatement;
|
||||
use runtime_support::Hashable;
|
||||
use polkadot_api::{PolkadotApi, BlockBuilder};
|
||||
use polkadot_primitives::{Hash, Timestamp};
|
||||
use polkadot_primitives::block::Block as PolkadotBlock;
|
||||
@@ -480,7 +482,7 @@ impl<C: PolkadotApi, N: Network> bft::ProposerFactory for ProposerFactory<C, N>
|
||||
type Error = Error;
|
||||
|
||||
fn init(&self, parent_header: &SubstrateHeader, authorities: &[AuthorityId], sign_with: Arc<ed25519::Pair>) -> Result<Self::Proposer, Error> {
|
||||
let parent_hash = parent_header.hash();
|
||||
let parent_hash = parent_header.blake2_256().into();
|
||||
|
||||
let checked_id = self.client.check_id(BlockId::Hash(parent_hash))?;
|
||||
let duty_roster = self.client.duty_roster(&checked_id)?;
|
||||
|
||||
@@ -26,6 +26,7 @@ use parking_lot::Mutex;
|
||||
use substrate_network as net;
|
||||
use tokio_core::reactor;
|
||||
use client::BlockchainEvents;
|
||||
use runtime_support::Hashable;
|
||||
use primitives::{Hash, AuthorityId};
|
||||
use primitives::block::{Id as BlockId, HeaderHash, Header};
|
||||
use polkadot_primitives::parachain::{BlockData, Extrinsic, CandidateReceipt};
|
||||
@@ -154,7 +155,7 @@ impl Service {
|
||||
};
|
||||
let bft_service = BftService::new(client.clone(), key, factory);
|
||||
let build_bft = |header: &Header| -> Result<_, Error> {
|
||||
let hash = header.hash();
|
||||
let hash = header.blake2_256().into();
|
||||
let authorities = client.authorities(&BlockId::Hash(hash))?;
|
||||
let input = network.bft_messages()
|
||||
.filter_map(move |message| {
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Slicable for Log {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for Log { }
|
||||
|
||||
|
||||
/// The digest of a block, useful for light-clients.
|
||||
#[derive(Clone, Default, PartialEq, Eq)]
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#[cfg(feature = "std")]
|
||||
use primitives::bytes;
|
||||
use primitives;
|
||||
use codec::{Input, Slicable, NonTrivialSlicable};
|
||||
use codec::{Input, Slicable};
|
||||
use rstd::cmp::{PartialOrd, Ord, Ordering};
|
||||
use rstd::vec::Vec;
|
||||
use ::Hash;
|
||||
@@ -59,7 +59,7 @@ pub enum Chain {
|
||||
|
||||
impl Slicable for Chain {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let disc = try_opt!(u8::decode(input));
|
||||
let disc = input.read_byte()?;
|
||||
|
||||
match disc {
|
||||
0 => Some(Chain::Relay),
|
||||
@@ -71,9 +71,9 @@ impl Slicable for Chain {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
match *self {
|
||||
Chain::Relay => { 0u8.using_encoded(|s| v.extend(s)); }
|
||||
Chain::Relay => { v.push(0); }
|
||||
Chain::Parachain(id) => {
|
||||
1u8.using_encoded(|s| v.extend(s));
|
||||
v.push(1u8);
|
||||
id.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ impl Slicable for Chain {
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for Chain { }
|
||||
|
||||
|
||||
/// The duty roster specifying what jobs each validator must do.
|
||||
#[derive(Clone, PartialEq)]
|
||||
@@ -317,7 +317,7 @@ impl Slicable for Statement {
|
||||
}
|
||||
|
||||
fn decode<I: Input>(value: &mut I) -> Option<Self> {
|
||||
match u8::decode(value) {
|
||||
match value.read_byte() {
|
||||
Some(x) if x == StatementKind::Candidate as u8 => {
|
||||
Slicable::decode(value).map(Statement::Candidate)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ pub enum Proposal {
|
||||
|
||||
impl Slicable for Proposal {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let id = try_opt!(u8::decode(input).and_then(InternalFunctionId::from_u8));
|
||||
let id = InternalFunctionId::from_u8(input.read_byte()?)?;
|
||||
let function = match id {
|
||||
InternalFunctionId::SystemSetCode =>
|
||||
Proposal::SystemSetCode(try_opt!(Slicable::decode(input))),
|
||||
@@ -119,33 +119,33 @@ impl Slicable for Proposal {
|
||||
let mut v = Vec::new();
|
||||
match *self {
|
||||
Proposal::SystemSetCode(ref data) => {
|
||||
(InternalFunctionId::SystemSetCode as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::SystemSetCode as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::SessionSetLength(ref data) => {
|
||||
(InternalFunctionId::SessionSetLength as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::SessionSetLength as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::SessionForceNewSession => {
|
||||
(InternalFunctionId::SessionForceNewSession as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::SessionForceNewSession as u8);
|
||||
}
|
||||
Proposal::StakingSetSessionsPerEra(ref data) => {
|
||||
(InternalFunctionId::StakingSetSessionsPerEra as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::StakingSetSessionsPerEra as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::StakingSetBondingDuration(ref data) => {
|
||||
(InternalFunctionId::StakingSetBondingDuration as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::StakingSetBondingDuration as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::StakingSetValidatorCount(ref data) => {
|
||||
(InternalFunctionId::StakingSetValidatorCount as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::StakingSetValidatorCount as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::StakingForceNewEra => {
|
||||
(InternalFunctionId::StakingForceNewEra as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::StakingForceNewEra as u8);
|
||||
}
|
||||
Proposal::GovernanceSetApprovalPpmRequired(ref data) => {
|
||||
(InternalFunctionId::GovernanceSetApprovalPpmRequired as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(InternalFunctionId::GovernanceSetApprovalPpmRequired as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ impl Function {
|
||||
|
||||
impl Slicable for Function {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let id = try_opt!(u8::decode(input).and_then(FunctionId::from_u8));
|
||||
let id = FunctionId::from_u8(input.read_byte()?)?;
|
||||
Some(match id {
|
||||
FunctionId::TimestampSet =>
|
||||
Function::Inherent(InherentFunction::TimestampSet(try_opt!(Slicable::decode(input)))),
|
||||
@@ -293,34 +293,34 @@ impl Slicable for Function {
|
||||
let mut v = Vec::new();
|
||||
match *self {
|
||||
Function::Inherent(InherentFunction::TimestampSet(ref data)) => {
|
||||
(FunctionId::TimestampSet as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::TimestampSet as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::SessionSetKey(ref data) => {
|
||||
(FunctionId::SessionSetKey as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::SessionSetKey as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::StakingStake => {
|
||||
(FunctionId::StakingStake as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::StakingStake as u8);
|
||||
}
|
||||
Function::StakingUnstake => {
|
||||
(FunctionId::StakingUnstake as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::StakingUnstake as u8);
|
||||
}
|
||||
Function::ReportMisbehavior(ref report) => {
|
||||
(FunctionId::StakingReportMisbehavior as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::StakingReportMisbehavior as u8);
|
||||
report.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::StakingTransfer(ref to, ref amount) => {
|
||||
(FunctionId::StakingTransfer as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::StakingTransfer as u8);
|
||||
to.using_encoded(|s| v.extend(s));
|
||||
amount.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::GovernancePropose(ref data) => {
|
||||
(FunctionId::GovernancePropose as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::GovernancePropose as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::GovernanceApprove(ref data) => {
|
||||
(FunctionId::GovernanceApprove as u8).using_encoded(|s| v.extend(s));
|
||||
v.push(FunctionId::GovernanceApprove as u8);
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
}
|
||||
@@ -365,7 +365,7 @@ impl Slicable for Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for Transaction {}
|
||||
|
||||
|
||||
/// A transactions right from the external world. Unchecked.
|
||||
#[derive(Eq, Clone)]
|
||||
@@ -441,7 +441,7 @@ impl Slicable for UncheckedTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for UncheckedTransaction {}
|
||||
|
||||
|
||||
impl PartialEq for UncheckedTransaction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
|
||||
+2
-1
@@ -654,8 +654,9 @@ name = "substrate-runtime-support"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"environmental 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
|
||||
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
@@ -4,7 +4,7 @@ set -e
|
||||
|
||||
REPO="github.com/paritytech/polkadot-wasm-bin.git"
|
||||
REPO_AUTH="${GH_TOKEN}:@${REPO}"
|
||||
SRCS=( "polkadot/runtime/wasm" "substrate/executor/wasm" "substrate/test-runtime/wasm" )
|
||||
SRCS=( "polkadot/runtime/wasm" "substrate/executor/wasm" "demo/runtime/wasm" "substrate/test-runtime/wasm" )
|
||||
DST=".wasm-binaries"
|
||||
TARGET="wasm32-unknown-unknown"
|
||||
UTCDATE=`date -u "+%Y%m%d.%H%M%S.0"`
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "safe-mix"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
nightly = []
|
||||
strict = []
|
||||
@@ -0,0 +1,14 @@
|
||||
//! Set a nightly feature
|
||||
|
||||
extern crate rustc_version;
|
||||
use rustc_version::{version, version_meta, Channel};
|
||||
|
||||
fn main() {
|
||||
// Assert we haven't travelled back in time
|
||||
assert!(version().unwrap().major >= 1);
|
||||
|
||||
// Set cfg flags depending on release channel
|
||||
if let Channel::Nightly = version_meta().unwrap().channel {
|
||||
println!("cargo:rustc-cfg=feature=\"nightly\"");
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,14 @@
|
||||
//!
|
||||
//! Described in http://www.cs.huji.ac.il/~nati/PAPERS/coll_coin_fl.pdf
|
||||
|
||||
use rstd::ops::{BitAnd, BitOr};
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::ops::{BitAnd, BitOr};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use core::ops::{BitAnd, BitOr};
|
||||
|
||||
pub const MAX_DEPTH: usize = 17;
|
||||
|
||||
@@ -7,6 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
futures = "0.1.17"
|
||||
substrate-codec = { path = "../codec" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-runtime-support = { path = "../runtime-support" }
|
||||
ed25519 = { path = "../ed25519" }
|
||||
tokio-timer = "0.1.2"
|
||||
parking_lot = "0.4"
|
||||
|
||||
@@ -21,6 +21,7 @@ pub mod generic;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate ed25519;
|
||||
extern crate tokio_timer;
|
||||
extern crate parking_lot;
|
||||
@@ -37,6 +38,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use codec::Slicable;
|
||||
use ed25519::LocalizedSignature;
|
||||
use runtime_support::Hashable;
|
||||
use primitives::bft::{Message as PrimitiveMessage, Action as PrimitiveAction, Justification as PrimitiveJustification};
|
||||
use primitives::block::{Block, Id as BlockId, Header, HeaderHash};
|
||||
use primitives::AuthorityId;
|
||||
@@ -176,7 +178,7 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
}
|
||||
|
||||
fn candidate_digest(&self, proposal: &Block) -> HeaderHash {
|
||||
proposal.header.hash()
|
||||
proposal.header.blake2_256().into()
|
||||
}
|
||||
|
||||
fn sign_local(&self, message: Message) -> LocalizedMessage {
|
||||
@@ -334,7 +336,7 @@ impl<P, I> BftService<P, I>
|
||||
InStream: Stream<Item=Communication, Error=<<P as ProposerFactory>::Proposer as Proposer>::Error>,
|
||||
OutSink: Sink<SinkItem=Communication, SinkError=<<P as ProposerFactory>::Proposer as Proposer>::Error>,
|
||||
{
|
||||
let hash = header.hash();
|
||||
let hash = header.blake2_256().into();
|
||||
let mut _preempted_consensus = None; // defers drop of live to the end.
|
||||
|
||||
let authorities = self.client.authorities(&BlockId::Hash(hash))?;
|
||||
@@ -521,7 +523,7 @@ pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: HeaderHa
|
||||
|
||||
match message {
|
||||
::generic::Message::Propose(r, proposal) => {
|
||||
let header_hash = proposal.header.hash();
|
||||
let header_hash: HeaderHash = proposal.header.blake2_256().into();
|
||||
let action_header = PrimitiveAction::ProposeHeader(r as u32, header_hash.clone());
|
||||
let action_propose = PrimitiveAction::Propose(r as u32, proposal.clone());
|
||||
|
||||
@@ -664,11 +666,11 @@ mod tests {
|
||||
let service = make_service(client);
|
||||
|
||||
let first = Header::from_block_number(2);
|
||||
let first_hash = first.hash();
|
||||
let first_hash = first.blake2_256().into();
|
||||
|
||||
let mut second = Header::from_block_number(3);
|
||||
second.parent_hash = first_hash;
|
||||
let second_hash = second.hash();
|
||||
let second_hash = second.blake2_256().into();
|
||||
|
||||
let bft = service.build_upon(&first, stream::empty(), Output(Default::default())).unwrap();
|
||||
assert!(service.live_agreements.lock().contains_key(&first_hash));
|
||||
|
||||
@@ -271,7 +271,7 @@ impl<B, E> Client<B, E> where
|
||||
) -> error::Result<JustifiedHeader> {
|
||||
let authorities = self.authorities_at(&BlockId::Hash(header.parent_hash))?;
|
||||
let just = bft::check_justification(&authorities[..], header.parent_hash, justification)
|
||||
.map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.hash())))?;
|
||||
.map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.blake2_256().into())))?;
|
||||
Ok(JustifiedHeader {
|
||||
header,
|
||||
justification: just,
|
||||
@@ -453,7 +453,7 @@ mod tests {
|
||||
// TODO: remove this in favor of custom verification pipelines for the
|
||||
// client
|
||||
fn justify(header: &block::Header) -> bft::UncheckedJustification {
|
||||
let hash = header.hash();
|
||||
let hash = header.blake2_256().into();
|
||||
let authorities = vec![
|
||||
Keyring::Alice.into(),
|
||||
Keyring::Bob.into(),
|
||||
|
||||
@@ -27,6 +27,6 @@ mod slicable;
|
||||
mod joiner;
|
||||
mod keyedvec;
|
||||
|
||||
pub use self::slicable::{Input, Slicable, NonTrivialSlicable};
|
||||
pub use self::slicable::{Input, Slicable};
|
||||
pub use self::joiner::Joiner;
|
||||
pub use self::keyedvec::KeyedVec;
|
||||
|
||||
@@ -24,6 +24,16 @@ use super::joiner::Joiner;
|
||||
pub trait Input {
|
||||
/// Read into the provided input slice. Returns the number of bytes read.
|
||||
fn read(&mut self, into: &mut [u8]) -> usize;
|
||||
|
||||
/// Read a single byte from the input.
|
||||
fn read_byte(&mut self) -> Option<u8> {
|
||||
let mut buf = [0u8];
|
||||
match self.read(&mut buf[..]) {
|
||||
0 => None,
|
||||
1 => Some(buf[0]),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Input for &'a [u8] {
|
||||
@@ -51,29 +61,25 @@ pub trait Slicable: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to mark that a type is not trivially (essentially "in place") serialisable.
|
||||
// TODO: under specialization, remove this and simply specialize in place serializable types.
|
||||
pub trait NonTrivialSlicable: Slicable {}
|
||||
|
||||
impl Slicable for Option<bool> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u8::decode(input).and_then(|v| match v {
|
||||
match input.read_byte()? {
|
||||
0 => Some(Some(false)),
|
||||
1 => Some(Some(true)),
|
||||
2 => Some(None),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
match *self {
|
||||
f(&[match *self {
|
||||
Some(false) => 0u8,
|
||||
Some(true) => 1u8,
|
||||
None => 2u8,
|
||||
}.using_encoded(f)
|
||||
}])
|
||||
}
|
||||
}
|
||||
impl NonTrivialSlicable for Option<bool> {}
|
||||
|
||||
|
||||
impl<T: Slicable> Slicable for Box<T> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
@@ -108,89 +114,7 @@ impl Slicable for Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement for all primitives.
|
||||
impl Slicable for Vec<u64> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u32::decode(input).and_then(move |len| {
|
||||
let len = len as usize;
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
for _ in 0..len {
|
||||
vec.push(u64::decode(input)?);
|
||||
}
|
||||
Some(vec)
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let len = self.len();
|
||||
assert!(len <= u32::max_value() as usize, "Attempted to serialize vec with too many elements.");
|
||||
|
||||
// TODO: optimise - no need to create a new vec and copy - can just reserve and encode in place
|
||||
let mut r: Vec<u8> = Vec::new().and(&(len as u32));
|
||||
for i in self.iter() {
|
||||
r.extend(&i.encode());
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use a BitVec-like representation.
|
||||
impl Slicable for Vec<bool> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
<Vec<u8>>::decode(input).map(|a| a.into_iter().map(|b| b != 0).collect())
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
<Vec<u8>>::encode(&self.iter().map(|&b| if b {1} else {0}).collect())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_vec_simple_array {
|
||||
($($size:expr),*) => {
|
||||
$(
|
||||
impl<T> Slicable for Vec<[T; $size]>
|
||||
where [T; $size]: Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u32::decode(input).and_then(move |len| {
|
||||
let mut r = Vec::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
r.push(match Slicable::decode(input) {
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
});
|
||||
}
|
||||
|
||||
Some(r)
|
||||
})
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(&self.encode())
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
use rstd::iter::Extend;
|
||||
|
||||
let len = self.len();
|
||||
assert!(len <= u32::max_value() as usize, "Attempted to serialize vec with too many elements.");
|
||||
|
||||
let mut r: Vec<u8> = Vec::new().and(&(len as u32));
|
||||
for item in self {
|
||||
r.extend(item.encode());
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_vec_simple_array!(1, 2, 4, 8, 16, 32, 64);
|
||||
|
||||
impl<T: Slicable> NonTrivialSlicable for Vec<T> where Vec<T>: Slicable {}
|
||||
|
||||
impl<T: NonTrivialSlicable> Slicable for Vec<T> {
|
||||
impl<T: Slicable> Slicable for Vec<T> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u32::decode(input).and_then(move |len| {
|
||||
let mut r = Vec::with_capacity(len as usize);
|
||||
@@ -219,6 +143,8 @@ impl<T: NonTrivialSlicable> Slicable for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Slicable for () {
|
||||
fn decode<I: Input>(_: &mut I) -> Option<()> {
|
||||
Some(())
|
||||
@@ -248,7 +174,7 @@ macro_rules! tuple_impl {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$one: NonTrivialSlicable> NonTrivialSlicable for ($one,) { }
|
||||
|
||||
};
|
||||
($first:ident, $($rest:ident,)+) => {
|
||||
impl<$first: Slicable, $($rest: Slicable),+>
|
||||
@@ -284,11 +210,6 @@ macro_rules! tuple_impl {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first: Slicable, $($rest: Slicable),+>
|
||||
NonTrivialSlicable
|
||||
for ($first, $($rest),+)
|
||||
{ }
|
||||
|
||||
tuple_impl!($($rest,)+);
|
||||
}
|
||||
}
|
||||
@@ -297,7 +218,7 @@ macro_rules! tuple_impl {
|
||||
mod inner_tuple_impl {
|
||||
use rstd::vec::Vec;
|
||||
|
||||
use super::{Input, Slicable, NonTrivialSlicable};
|
||||
use super::{Input, Slicable};
|
||||
tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,);
|
||||
}
|
||||
|
||||
@@ -399,7 +320,7 @@ macro_rules! impl_non_endians {
|
||||
}
|
||||
|
||||
impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize);
|
||||
impl_non_endians!(u8, i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
|
||||
impl_non_endians!(i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
|
||||
[u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40],
|
||||
[u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128], bool);
|
||||
|
||||
|
||||
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
@@ -162,7 +162,7 @@ impl Peer {
|
||||
}
|
||||
|
||||
fn justify(header: &primitives::block::Header) -> bft::UncheckedJustification {
|
||||
let hash = header.hash();
|
||||
let hash = header.blake2_256().into();
|
||||
let authorities = vec![ Keyring::Alice.into() ];
|
||||
|
||||
bft::UncheckedJustification {
|
||||
|
||||
@@ -23,7 +23,7 @@ use ::{AuthorityId, Signature};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
#[repr(u8)]
|
||||
#[repr(i8)]
|
||||
enum ActionKind {
|
||||
Propose = 1,
|
||||
ProposeHeader = 2,
|
||||
@@ -83,27 +83,27 @@ impl Slicable for Action {
|
||||
}
|
||||
|
||||
fn decode<I: Input>(value: &mut I) -> Option<Self> {
|
||||
match u8::decode(value) {
|
||||
Some(x) if x == ActionKind::Propose as u8 => {
|
||||
match i8::decode(value) {
|
||||
Some(x) if x == ActionKind::Propose as i8 => {
|
||||
let (round, block) = try_opt!(Slicable::decode(value));
|
||||
Some(Action::Propose(round, block))
|
||||
}
|
||||
Some(x) if x == ActionKind::ProposeHeader as u8 => {
|
||||
Some(x) if x == ActionKind::ProposeHeader as i8 => {
|
||||
let (round, hash) = try_opt!(Slicable::decode(value));
|
||||
|
||||
Some(Action::ProposeHeader(round, hash))
|
||||
}
|
||||
Some(x) if x == ActionKind::Prepare as u8 => {
|
||||
Some(x) if x == ActionKind::Prepare as i8 => {
|
||||
let (round, hash) = try_opt!(Slicable::decode(value));
|
||||
|
||||
Some(Action::Prepare(round, hash))
|
||||
}
|
||||
Some(x) if x == ActionKind::Commit as u8 => {
|
||||
Some(x) if x == ActionKind::Commit as i8 => {
|
||||
let (round, hash) = try_opt!(Slicable::decode(value));
|
||||
|
||||
Some(Action::Commit(round, hash))
|
||||
}
|
||||
Some(x) if x == ActionKind::AdvanceRound as u8 => {
|
||||
Some(x) if x == ActionKind::AdvanceRound as i8 => {
|
||||
Slicable::decode(value).map(Action::AdvanceRound)
|
||||
}
|
||||
_ => None,
|
||||
@@ -169,7 +169,7 @@ impl Slicable for Justification {
|
||||
}
|
||||
|
||||
// single-byte code to represent misbehavior kind.
|
||||
#[repr(u8)]
|
||||
#[repr(i8)]
|
||||
enum MisbehaviorCode {
|
||||
/// BFT: double prepare.
|
||||
BftDoublePrepare = 0x11,
|
||||
@@ -178,7 +178,7 @@ enum MisbehaviorCode {
|
||||
}
|
||||
|
||||
impl MisbehaviorCode {
|
||||
fn from_u8(x: u8) -> Option<Self> {
|
||||
fn from_i8(x: i8) -> Option<Self> {
|
||||
match x {
|
||||
0x11 => Some(MisbehaviorCode::BftDoublePrepare),
|
||||
0x12 => Some(MisbehaviorCode::BftDoubleCommit),
|
||||
@@ -220,7 +220,7 @@ impl Slicable for MisbehaviorReport {
|
||||
|
||||
match self.misbehavior {
|
||||
MisbehaviorKind::BftDoublePrepare(ref round, (ref h_a, ref s_a), (ref h_b, ref s_b)) => {
|
||||
(MisbehaviorCode::BftDoublePrepare as u8).using_encoded(|s| v.extend(s));
|
||||
(MisbehaviorCode::BftDoublePrepare as i8).using_encoded(|s| v.extend(s));
|
||||
round.using_encoded(|s| v.extend(s));
|
||||
h_a.using_encoded(|s| v.extend(s));
|
||||
s_a.using_encoded(|s| v.extend(s));
|
||||
@@ -228,7 +228,7 @@ impl Slicable for MisbehaviorReport {
|
||||
s_b.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
MisbehaviorKind::BftDoubleCommit(ref round, (ref h_a, ref s_a), (ref h_b, ref s_b)) => {
|
||||
(MisbehaviorCode::BftDoubleCommit as u8).using_encoded(|s| v.extend(s));
|
||||
(MisbehaviorCode::BftDoubleCommit as i8).using_encoded(|s| v.extend(s));
|
||||
round.using_encoded(|s| v.extend(s));
|
||||
h_a.using_encoded(|s| v.extend(s));
|
||||
s_a.using_encoded(|s| v.extend(s));
|
||||
@@ -245,7 +245,7 @@ impl Slicable for MisbehaviorReport {
|
||||
let parent_number = ::block::Number::decode(input)?;
|
||||
let target = AuthorityId::decode(input)?;
|
||||
|
||||
let misbehavior = match u8::decode(input).and_then(MisbehaviorCode::from_u8)? {
|
||||
let misbehavior = match i8::decode(input).and_then(MisbehaviorCode::from_i8)? {
|
||||
MisbehaviorCode::BftDoublePrepare => {
|
||||
MisbehaviorKind::BftDoublePrepare(
|
||||
u32::decode(input)?,
|
||||
|
||||
@@ -21,7 +21,7 @@ use rstd::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use bytes;
|
||||
use Hash;
|
||||
use codec::{Input, Slicable, NonTrivialSlicable};
|
||||
use codec::{Input, Slicable};
|
||||
|
||||
/// Used to refer to a block number.
|
||||
pub type Number = u64;
|
||||
@@ -47,10 +47,6 @@ impl Slicable for Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for Transaction { }
|
||||
|
||||
|
||||
|
||||
/// Execution log (event)
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
@@ -66,10 +62,6 @@ impl Slicable for Log {
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for Log { }
|
||||
|
||||
|
||||
|
||||
/// The digest of a block, useful for light-clients.
|
||||
#[derive(Clone, Default, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
@@ -88,11 +80,9 @@ impl Slicable for Digest {
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for Digest { }
|
||||
|
||||
/// Generic types to be specialised later.
|
||||
pub mod generic {
|
||||
use super::{Header, Slicable, Input, NonTrivialSlicable, Vec};
|
||||
use super::{Header, Slicable, Input, Vec};
|
||||
|
||||
/// A Block - this is generic for later specialisation in particular runtimes.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
@@ -107,8 +97,8 @@ pub mod generic {
|
||||
impl<T: PartialEq + Eq + Clone> Slicable for Block<T> where Vec<T>: Slicable {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Block {
|
||||
header: try_opt!(Slicable::decode(input)),
|
||||
transactions: try_opt!(Slicable::decode(input)),
|
||||
header: Slicable::decode(input)?,
|
||||
transactions: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -119,8 +109,6 @@ pub mod generic {
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Eq + Clone> NonTrivialSlicable for Block<T> where Vec<T>: Slicable { }
|
||||
}
|
||||
|
||||
/// The body of a block is just a bunch of transactions.
|
||||
@@ -162,22 +150,16 @@ impl Header {
|
||||
digest: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the blake2-256 hash of this header.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn hash(&self) -> HeaderHash {
|
||||
::hashing::blake2_256(Slicable::encode(self).as_slice()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Header {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
parent_hash: try_opt!(Slicable::decode(input)),
|
||||
number: try_opt!(Slicable::decode(input)),
|
||||
state_root: try_opt!(Slicable::decode(input)),
|
||||
transaction_root: try_opt!(Slicable::decode(input)),
|
||||
digest: try_opt!(Slicable::decode(input)),
|
||||
parent_hash: Slicable::decode(input)?,
|
||||
number: Slicable::decode(input)?,
|
||||
state_root: Slicable::decode(input)?,
|
||||
transaction_root: Slicable::decode(input)?,
|
||||
digest: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ mod tests;
|
||||
|
||||
pub use self::hash::{H160, H256};
|
||||
pub use self::uint::{U256, U512};
|
||||
pub use block::{Block, Header};
|
||||
pub use block::{Block, Header, Digest};
|
||||
|
||||
/// General hash type.
|
||||
pub type Hash = H256;
|
||||
|
||||
@@ -29,3 +29,37 @@ include!("../with_std.rs");
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
include!("../without_std.rs");
|
||||
|
||||
/// Abstraction around hashing
|
||||
pub trait Hashing {
|
||||
/// The hash type produced.
|
||||
type Output;
|
||||
|
||||
/// Produce the hash of some byte-slice.
|
||||
fn hash(s: &[u8]) -> Self::Output;
|
||||
/// Produce the hash of some codec-encodable value.
|
||||
fn hash_of<S: codec::Slicable>(s: &S) -> Self::Output {
|
||||
codec::Slicable::using_encoded(s, Self::hash)
|
||||
}
|
||||
/// Produce the patricia-trie root of a mapping from indices to byte slices.
|
||||
fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output;
|
||||
|
||||
/// Acquire the global storage root.
|
||||
fn storage_root() -> Self::Output;
|
||||
}
|
||||
|
||||
/// Blake2-256 Hashing implementation.
|
||||
pub struct BlakeTwo256;
|
||||
|
||||
impl Hashing for BlakeTwo256 {
|
||||
type Output = primitives::H256;
|
||||
fn hash(s: &[u8]) -> Self::Output {
|
||||
blake2_256(s).into()
|
||||
}
|
||||
fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output {
|
||||
enumerated_trie_root(items).into()
|
||||
}
|
||||
fn storage_root() -> Self::Output {
|
||||
storage_root().into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
#[macro_use]
|
||||
extern crate environmental;
|
||||
|
||||
extern crate substrate_state_machine;
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
extern crate substrate_state_machine;
|
||||
extern crate triehash;
|
||||
extern crate ed25519;
|
||||
|
||||
@@ -156,12 +158,6 @@ macro_rules! impl_stubs {
|
||||
mod std_tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! map {
|
||||
($( $name:expr => $value:expr ),*) => (
|
||||
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_works() {
|
||||
let mut t = TestExternalities::new();
|
||||
|
||||
@@ -4,11 +4,11 @@ extern crate rustc_version;
|
||||
use rustc_version::{version, version_meta, Channel};
|
||||
|
||||
fn main() {
|
||||
// Assert we haven't travelled back in time
|
||||
assert!(version().unwrap().major >= 1);
|
||||
// Assert we haven't travelled back in time
|
||||
assert!(version().unwrap().major >= 1);
|
||||
|
||||
// Set cfg flags depending on release channel
|
||||
if let Channel::Nightly = version_meta().unwrap().channel {
|
||||
println!("cargo:rustc-cfg=feature=\"nightly\"");
|
||||
}
|
||||
// Set cfg flags depending on release channel
|
||||
if let Channel::Nightly = version_meta().unwrap().channel {
|
||||
println!("cargo:rustc-cfg=feature=\"nightly\"");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,13 @@
|
||||
#![cfg_attr(feature = "std", doc = "Polkadot runtime standard library as compiled when linked with Rust's standard library.")]
|
||||
#![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")]
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! map {
|
||||
($( $name:expr => $value:expr ),*) => (
|
||||
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
include!("../with_std.rs");
|
||||
|
||||
@@ -38,4 +45,6 @@ include!("../without_std.rs");
|
||||
pub mod prelude {
|
||||
pub use ::vec::Vec;
|
||||
pub use ::boxed::Box;
|
||||
pub use ::cmp::{Eq, PartialEq};
|
||||
pub use ::clone::Clone;
|
||||
}
|
||||
|
||||
@@ -17,15 +17,19 @@
|
||||
pub use std::borrow;
|
||||
pub use std::boxed;
|
||||
pub use std::cell;
|
||||
pub use std::clone;
|
||||
pub use std::cmp;
|
||||
pub use std::fmt;
|
||||
pub use std::iter;
|
||||
pub use std::marker;
|
||||
pub use std::mem;
|
||||
pub use std::num;
|
||||
pub use std::ops;
|
||||
pub use std::ptr;
|
||||
pub use std::rc;
|
||||
pub use std::slice;
|
||||
pub use std::vec;
|
||||
|
||||
pub mod collections {
|
||||
pub use std::collections::btree_map;
|
||||
}
|
||||
|
||||
@@ -25,16 +25,20 @@ extern crate pwasm_alloc;
|
||||
pub use alloc::boxed;
|
||||
pub use alloc::rc;
|
||||
pub use alloc::vec;
|
||||
pub mod collections {
|
||||
pub use alloc::btree_map;
|
||||
}
|
||||
pub use core::borrow;
|
||||
pub use core::cell;
|
||||
pub use core::clone;
|
||||
pub use core::cmp;
|
||||
pub use core::fmt;
|
||||
pub use core::intrinsics;
|
||||
pub use core::iter;
|
||||
pub use core::marker;
|
||||
pub use core::mem;
|
||||
pub use core::num;
|
||||
pub use core::ops;
|
||||
pub use core::ptr;
|
||||
pub use core::slice;
|
||||
pub use core::fmt;
|
||||
|
||||
pub mod collections {
|
||||
pub use alloc::btree_map;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
ed25519 = { path = "../ed25519", optional = true }
|
||||
hex-literal = { version = "0.1.0", optional = true }
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-runtime-std = { path = "../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../runtime-io", default_features = false }
|
||||
environmental = { path = "../environmental", optional = true }
|
||||
substrate-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-codec = { path = "../codec", default_features = false }
|
||||
|
||||
@@ -17,7 +18,8 @@ default = ["std"]
|
||||
std = [
|
||||
"ed25519",
|
||||
"hex-literal",
|
||||
"environmental",
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-codec/std",
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Dispatch system. Just dispatches calls.
|
||||
|
||||
pub use rstd::prelude::{Vec, Clone, Eq, PartialEq};
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::fmt;
|
||||
pub use rstd::marker::PhantomData;
|
||||
#[cfg(feature = "std")]
|
||||
use serde;
|
||||
pub use codec::{Slicable, Input};
|
||||
|
||||
pub trait Dispatchable {
|
||||
type Trait;
|
||||
fn dispatch(self);
|
||||
}
|
||||
|
||||
pub trait AuxDispatchable {
|
||||
type Aux;
|
||||
type Trait;
|
||||
fn dispatch(self, aux: &Self::Aux);
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait AuxCallable {
|
||||
type Call: AuxDispatchable + Slicable + ::serde::Serialize + Clone + PartialEq + Eq;
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait AuxCallable {
|
||||
type Call: AuxDispatchable + Slicable + Clone + PartialEq + Eq;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Callable {
|
||||
type Call: Dispatchable + Slicable + ::serde::Serialize + Clone + PartialEq + Eq;
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait Callable {
|
||||
type Call: Dispatchable + Slicable + Clone + PartialEq + Eq;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Parameter: Slicable + serde::Serialize + Clone + Eq + fmt::Debug {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> Parameter for T where T: Slicable + serde::Serialize + Clone + Eq + fmt::Debug {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait Parameter: Slicable + Clone + Eq {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> Parameter for T where T: Slicable + Clone + Eq {}
|
||||
|
||||
|
||||
/// Declare a struct for this module, then implement dispatch logic to create a pairing of several
|
||||
/// dispatch traits and enums.
|
||||
#[macro_export]
|
||||
macro_rules! decl_module {
|
||||
(
|
||||
pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
pub struct $mod_type<$trait_instance: $trait_name>($crate::dispatch::PhantomData<$trait_instance>);
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
(
|
||||
struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
struct $mod_type<$trait_instance: $trait_name>($crate::dispatch::PhantomData<$trait_instance>);
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement several dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
#[macro_export]
|
||||
macro_rules! decl_dispatch {
|
||||
// WITHOUT AUX
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
pub enum $call_type:ident {
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
)
|
||||
= $id:expr ;
|
||||
)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
__decl_dispatch_module_without_aux! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
pub enum $call_type;
|
||||
$(
|
||||
fn $fn_name( $( $param_name: $param ),* ) = $id;
|
||||
)*
|
||||
}
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
// WITH AUX
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
pub enum $call_type:ident where aux: $aux_type:ty {
|
||||
$(
|
||||
fn $fn_name:ident(aux
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
)
|
||||
= $id:expr ;
|
||||
)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
__decl_dispatch_module_with_aux! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
pub enum $call_type where aux: $aux_type;
|
||||
$(
|
||||
fn $fn_name(aux $(, $param_name: $param )*)= $id;
|
||||
)*
|
||||
}
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
// BASE CASE
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
) => {
|
||||
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
|
||||
pub fn aux_dispatch<D: $crate::dispatch::AuxDispatchable<Trait = $trait_instance>>(d: D, aux: &D::Aux) {
|
||||
d.dispatch(aux);
|
||||
}
|
||||
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(d: D) {
|
||||
d.dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Implement a single dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
macro_rules! __decl_dispatch_module_without_aux {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
pub enum $call_type:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
)
|
||||
= $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
__decl_dispatch_module_common! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
pub enum $call_type;
|
||||
$( fn $fn_name( $( $param_name : $param ),* ) = $id ; )*
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Dispatchable
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
type Trait = $trait_instance;
|
||||
fn dispatch(self) {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$fn_name( $( $param_name ),* ) =>
|
||||
<$mod_type<$trait_instance>>::$fn_name( $( $param_name ),* ),
|
||||
)*
|
||||
$call_type::__PhantomItem(_) => { panic!("__PhantomItem should never be used.") },
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Callable
|
||||
for $mod_type<$trait_instance>
|
||||
{
|
||||
type Call = $call_type<$trait_instance>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Implement a single dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
macro_rules! __decl_dispatch_module_with_aux {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
pub enum $call_type:ident where aux: $aux_type:ty;
|
||||
$(
|
||||
fn $fn_name:ident(aux
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
)
|
||||
= $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
__decl_dispatch_module_common! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
pub enum $call_type;
|
||||
$( fn $fn_name( $( $param_name : $param ),* ) = $id ; )*
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::AuxDispatchable
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
type Trait = $trait_instance;
|
||||
type Aux = $aux_type;
|
||||
fn dispatch(self, aux: &Self::Aux) {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$fn_name( $( $param_name ),* ) =>
|
||||
<$mod_type<$trait_instance>>::$fn_name( aux $(, $param_name )* ),
|
||||
)*
|
||||
$call_type::__PhantomItem(_) => { panic!("__PhantomItem should never be used.") },
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::AuxCallable
|
||||
for $mod_type<$trait_instance>
|
||||
{
|
||||
type Call = $call_type<$trait_instance>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Implement a single dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
macro_rules! __decl_dispatch_module_common {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
pub enum $call_type:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
)
|
||||
= $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
#[cfg_attr(feature = "std", derive(Serialize))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum $call_type<$trait_instance: $trait_name> {
|
||||
__PhantomItem($crate::dispatch::PhantomData<$trait_instance>),
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* ),
|
||||
)*
|
||||
}
|
||||
|
||||
// manual implementation of clone/eq/partialeq because using derive erroneously requires
|
||||
// clone/eq/partialeq from T.
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Clone
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) =>
|
||||
$call_type::$fn_name( $( $param_name.clone() ),* )
|
||||
,)*
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::PartialEq
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) => {
|
||||
let self_params = ( $( $param_name, )* );
|
||||
if let $call_type::$fn_name( $( ref $param_name ),* ) = *other {
|
||||
self_params == ( $( $param_name, )* )
|
||||
} else {
|
||||
if let $call_type::__PhantomItem(_) = *other {
|
||||
unreachable!()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Eq
|
||||
for $call_type<$trait_instance>
|
||||
{}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::fmt::Debug
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn fmt(&self, f: &mut $crate::dispatch::fmt::Formatter) -> Result<(), $crate::dispatch::fmt::Error> {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) =>
|
||||
write!(f, "{}{:?}",
|
||||
stringify!($fn_name),
|
||||
( $( $param_name.clone(), )* )
|
||||
)
|
||||
,)*
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Slicable for $call_type<$trait_instance> {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
match input.read_byte()? {
|
||||
$(
|
||||
$id => {
|
||||
$(
|
||||
let $param_name = $crate::dispatch::Slicable::decode(input)?;
|
||||
)*
|
||||
Some($call_type:: $fn_name( $( $param_name ),* ))
|
||||
}
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(&self) -> $crate::dispatch::Vec<u8> {
|
||||
let mut v = $crate::dispatch::Vec::new();
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name(
|
||||
$(
|
||||
ref $param_name
|
||||
),*
|
||||
) => {
|
||||
v.push($id as u8);
|
||||
$(
|
||||
$param_name.using_encoded(|s| v.extend(s));
|
||||
)*
|
||||
}
|
||||
)*
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(self.encode().as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IsSubType<T: Callable> {
|
||||
fn is_sub_type(&self) -> Option<&<T as Callable>::Call>;
|
||||
}
|
||||
pub trait IsAuxSubType<T: AuxCallable> {
|
||||
fn is_aux_sub_type(&self) -> Option<&<T as AuxCallable>::Call>;
|
||||
}
|
||||
|
||||
/// Implement a meta-dispatch module to dispatch to other dispatchers.
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_dispatch {
|
||||
() => ();
|
||||
(
|
||||
pub enum $call_type:ident where aux: $aux:ty {
|
||||
$(
|
||||
$camelcase:ident = $id:expr,
|
||||
)*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum $call_type {
|
||||
$(
|
||||
$camelcase ( <$camelcase as $crate::dispatch::AuxCallable>::Call )
|
||||
,)*
|
||||
}
|
||||
impl_outer_dispatch_common! { $call_type, $($camelcase = $id,)* }
|
||||
impl $crate::dispatch::AuxDispatchable for $call_type {
|
||||
type Aux = $aux;
|
||||
type Trait = $call_type;
|
||||
fn dispatch(self, aux: &$aux) {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$camelcase(call) => call.dispatch(&aux),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl $crate::dispatch::IsAuxSubType<$camelcase> for $call_type {
|
||||
fn is_aux_sub_type(&self) -> Option<&<$camelcase as $crate::dispatch::AuxCallable>::Call> {
|
||||
if let $call_type::$camelcase ( ref r ) = *self {
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
impl_outer_dispatch!{ $($rest)* }
|
||||
};
|
||||
(
|
||||
pub enum $call_type:ident {
|
||||
$(
|
||||
$camelcase:ident = $id:expr,
|
||||
)*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum $call_type {
|
||||
$(
|
||||
$camelcase ( <$camelcase as $crate::dispatch::Callable>::Call )
|
||||
,)*
|
||||
}
|
||||
impl_outer_dispatch_common! { $call_type, $($camelcase = $id,)* }
|
||||
impl $crate::dispatch::Dispatchable for $call_type {
|
||||
type Trait = $call_type;
|
||||
fn dispatch(self) {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$camelcase(call) => call.dispatch(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl $crate::dispatch::IsSubType<$camelcase> for $call_type {
|
||||
fn is_sub_type(&self) -> Option<&<$camelcase as $crate::dispatch::Callable>::Call> {
|
||||
if let $call_type::$camelcase ( ref r ) = *self {
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
impl_outer_dispatch!{ $($rest)* }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement a meta-dispatch module to dispatch to other dispatchers.
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_dispatch_common {
|
||||
(
|
||||
$call_type:ident, $( $camelcase:ident = $id:expr, )*
|
||||
) => {
|
||||
impl $crate::dispatch::Slicable for $call_type {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
match input.read_byte()? {
|
||||
$(
|
||||
$id =>
|
||||
Some($call_type::$camelcase( $crate::dispatch::Slicable::decode(input)? )),
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(&self) -> $crate::dispatch::Vec<u8> {
|
||||
let mut v = $crate::dispatch::Vec::new();
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$camelcase( ref sub ) => {
|
||||
v.push($id as u8);
|
||||
sub.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
)*
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(self.encode().as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,16 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(unused_imports)] #[macro_use] // can be removed when fixed: https://github.com/rust-lang/rust/issues/43497
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use serde_derive::*;
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_primitives as primitives;
|
||||
@@ -26,8 +36,10 @@ extern crate substrate_primitives as primitives;
|
||||
pub extern crate substrate_codec as codec;
|
||||
pub use self::storage::generator::Storage as GenericStorage;
|
||||
|
||||
pub mod dispatch;
|
||||
pub mod storage;
|
||||
mod hashable;
|
||||
|
||||
pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap};
|
||||
pub use self::hashable::Hashable;
|
||||
pub use self::dispatch::{Parameter, Dispatchable, Callable, AuxDispatchable, AuxCallable, IsSubType, IsAuxSubType};
|
||||
|
||||
@@ -50,6 +50,8 @@ use codec;
|
||||
use rstd::vec::Vec;
|
||||
#[doc(hidden)]
|
||||
pub use rstd::borrow::Borrow;
|
||||
#[doc(hidden)]
|
||||
pub use rstd::marker::PhantomData;
|
||||
|
||||
/// Abstraction around storage.
|
||||
pub trait Storage {
|
||||
@@ -384,52 +386,52 @@ macro_rules! storage_items {
|
||||
};
|
||||
|
||||
// maps
|
||||
($name:ident : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
($name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
(pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
($name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
(pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
($name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
(pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
|
||||
($name:ident get($getfn:ident) : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
($name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
($name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
($name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
@@ -447,6 +449,544 @@ macro_rules! storage_items {
|
||||
() => ()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_storage_item {
|
||||
// generator for values.
|
||||
(($($vis:tt)*) ($traittype:ident as $traitinstance:ident) ($get_fn:ident) ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => {
|
||||
__decl_storage_item!{ ($($vis)*) ($traittype as $traitinstance) () ($gettype) ($getter) ($taker) $name : $key => $ty }
|
||||
};
|
||||
(($($vis:tt)*) ($traittype:ident as $traitinstance:ident) () ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => {
|
||||
$($vis)* struct $name<$traitinstance: $traittype>($crate::storage::generator::PhantomData<$traitinstance>);
|
||||
|
||||
impl<$traitinstance: $traittype> $crate::storage::generator::StorageValue<$ty> for $name<$traitinstance> {
|
||||
type Query = $gettype;
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8] {
|
||||
$key
|
||||
}
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
fn get<S: $crate::GenericStorage>(storage: &S) -> Self::Query {
|
||||
storage.$getter($key)
|
||||
}
|
||||
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
fn take<S: $crate::GenericStorage>(storage: &S) -> Self::Query {
|
||||
storage.$taker($key)
|
||||
}
|
||||
}
|
||||
};
|
||||
// generator for maps.
|
||||
(($($vis:tt)*) ($traittype:ident as $traitinstance:ident) ($get_fn:ident) ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => {
|
||||
__decl_storage_item!{ ($($vis)*) ($traittype as $traitinstance) () ($gettype) ($getter) ($taker) $name : $prefix => map [$kty => $ty] }
|
||||
};
|
||||
(($($vis:tt)*) ($traittype:ident as $traitinstance:ident) () ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => {
|
||||
$($vis)* struct $name<$traitinstance: $traittype>($crate::storage::generator::PhantomData<$traitinstance>);
|
||||
|
||||
impl<$traitinstance: $traittype> $crate::storage::generator::StorageMap<$kty, $ty> for $name<$traitinstance> {
|
||||
type Query = $gettype;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8] {
|
||||
$prefix
|
||||
}
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn key_for(x: &$kty) -> Vec<u8> {
|
||||
let mut key = $prefix.to_vec();
|
||||
key.extend($crate::codec::Slicable::encode(x));
|
||||
key
|
||||
}
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Self::Query {
|
||||
let key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key);
|
||||
storage.$getter(&key[..])
|
||||
}
|
||||
|
||||
/// Take the value, reading and removing it.
|
||||
fn take<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Self::Query {
|
||||
let key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key);
|
||||
storage.$taker(&key[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: revisit this idiom once we get `type`s in `impl`s.
|
||||
/*impl<T: Trait> Module<T> {
|
||||
type Now = super::Now<T>;
|
||||
}*/
|
||||
|
||||
/// Declares strongly-typed wrappers around codec-compatible types in storage.
|
||||
///
|
||||
/// For now we implement a convenience trait with pre-specialised associated types, one for each
|
||||
/// storage item. This allows you to gain access to publicly visisible storage items from a
|
||||
/// module type. Currently you must disambiguate by using `<Module as Store>::Item` rather than
|
||||
/// the simpler `Module::Item`. Hopefully the rust guys with fix this soon.
|
||||
#[macro_export]
|
||||
macro_rules! decl_storage {
|
||||
(
|
||||
trait $storetype:ident for $modulename:ident<$traitinstance:ident: $traittype:ident>;
|
||||
$($t:tt)*
|
||||
) => {
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
trait $storetype {
|
||||
__decl_store_items!($($t)*);
|
||||
}
|
||||
impl<$traitinstance: $traittype> $storetype for $modulename<$traitinstance> {
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
}
|
||||
impl<$traitinstance: $traittype> $modulename<$traitinstance> {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
}
|
||||
};
|
||||
(
|
||||
pub trait $storetype:ident for $modulename:ident<$traitinstance:ident: $traittype:ident>;
|
||||
$($t:tt)*
|
||||
) => {
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
pub trait $storetype {
|
||||
__decl_store_items!($($t)*);
|
||||
}
|
||||
impl<$traitinstance: $traittype> $storetype for $modulename<$traitinstance> {
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
}
|
||||
impl<$traitinstance: $traittype> $modulename<$traitinstance> {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_store_item {
|
||||
($name:ident) => { type $name; }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_store_items {
|
||||
// simple values
|
||||
($name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
|
||||
($name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
|
||||
// maps
|
||||
($name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
|
||||
($name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_store_item!($name); __decl_store_items!($($t)*);
|
||||
};
|
||||
|
||||
// exit
|
||||
() => ()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_store_fn {
|
||||
($traitinstance:ident $name:ident $get_fn:ident ($gettype:ty) $key:expr => $ty:ty) => {
|
||||
pub fn $get_fn() -> $gettype {
|
||||
<$name<$traitinstance> as $crate::storage::generator::StorageValue<$ty>> :: get(&$crate::storage::RuntimeStorage)
|
||||
}
|
||||
};
|
||||
($traitinstance:ident $name:ident $get_fn:ident ($gettype:ty) $prefix:expr => map [$kty:ty => $ty:ty]) => {
|
||||
pub fn $get_fn<K: $crate::storage::generator::Borrow<$kty>>(key: K) -> $gettype {
|
||||
<$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>> :: get(key.borrow(), &$crate::storage::RuntimeStorage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_store_fns {
|
||||
// simple values
|
||||
($traitinstance:ident $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $gettype $key => $ty);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $key => $ty);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $key => $ty);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $key => $ty);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $key => $ty);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $key => $ty);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
// maps
|
||||
($traitinstance:ident $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $prefix:expr => map [$kty => $ty]);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $prefix:expr => map [$kty => $ty]);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $prefix:expr => map [$kty => $ty]);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $prefix:expr => map [$kty => $ty]);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $prefix:expr => map [$kty => $ty]);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_fn!($traitinstance $name $getfn ($ty) $prefix:expr => map [$kty => $ty]);
|
||||
__impl_store_fns!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
// exit
|
||||
($traitinstance:ident) => ()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_store_item {
|
||||
($name:ident $traitinstance:ident) => { type $name = $name<$traitinstance>; }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_store_items {
|
||||
// simple values
|
||||
($traitinstance:ident $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
// maps
|
||||
($traitinstance:ident $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
($traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__impl_store_item!($name $traitinstance);
|
||||
__impl_store_items!($traitinstance $($t)*);
|
||||
};
|
||||
|
||||
// exit
|
||||
($traitinstance:ident) => ()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_storage_items {
|
||||
// simple values
|
||||
($traittype:ident $traitinstance:ident $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) () (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) () (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) () ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) () ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) () ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) () ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
|
||||
($traittype:ident $traitinstance:ident $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) ($getfn) ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) ($getfn) ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
|
||||
// maps
|
||||
($traittype:ident $traitinstance:ident $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) () (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) () (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) () ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) () ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) () ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) () ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
|
||||
($traittype:ident $traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) ($getfn) ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!(() ($traittype as $traitinstance) ($getfn) ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
($traittype:ident $traitinstance:ident pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => {
|
||||
__decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
__decl_storage_items!($traittype $traitinstance $($t)*);
|
||||
};
|
||||
|
||||
// exit
|
||||
($traittype:ident $traitinstance:ident) => ()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "substrate-runtime-consensus"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
]
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Conensus module for runtime; manages the authority set ready for the native code.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_codec as codec;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use runtime_support::{storage, Parameter};
|
||||
use runtime_support::storage::unhashed::StorageVec;
|
||||
|
||||
pub const AUTHORITY_AT: &'static[u8] = b":auth:";
|
||||
pub const AUTHORITY_COUNT: &'static[u8] = b":auth:len";
|
||||
|
||||
struct AuthorityStorageVec<S: codec::Slicable + Default>(rstd::marker::PhantomData<S>);
|
||||
impl<S: codec::Slicable + Default> StorageVec for AuthorityStorageVec<S> {
|
||||
type Item = S;
|
||||
const PREFIX: &'static [u8] = AUTHORITY_AT;
|
||||
}
|
||||
|
||||
pub const CODE: &'static [u8] = b":code";
|
||||
|
||||
pub trait Trait {
|
||||
type SessionKey: Parameter + Default;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub enum PrivCall {
|
||||
fn set_code(new: Vec<u8>) = 0;
|
||||
fn dummy() = 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Get the current set of authorities. These are the session keys.
|
||||
pub fn authorities() -> Vec<T::SessionKey> {
|
||||
AuthorityStorageVec::<T::SessionKey>::items()
|
||||
}
|
||||
|
||||
/// Set the new code.
|
||||
fn set_code(new: Vec<u8>) {
|
||||
storage::unhashed::put_raw(CODE, &new);
|
||||
}
|
||||
|
||||
fn dummy() {}
|
||||
|
||||
/// Set the current set of authorities' session keys.
|
||||
///
|
||||
/// Called by `next_session` only.
|
||||
pub fn set_authorities(authorities: &[T::SessionKey]) {
|
||||
AuthorityStorageVec::<T::SessionKey>::set_items(authorities);
|
||||
}
|
||||
|
||||
/// Set a single authority by index.
|
||||
pub fn set_authority(index: u32, key: &T::SessionKey) {
|
||||
AuthorityStorageVec::<T::SessionKey>::set_item(index, key);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub struct GenesisConfig<T: Trait> {
|
||||
pub authorities: Vec<T::SessionKey>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
authorities: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
|
||||
{
|
||||
fn build_externalities(self) -> runtime_io::TestExternalities {
|
||||
use codec::{Slicable, KeyedVec};
|
||||
let auth_count = self.authorities.len() as u32;
|
||||
let mut r: runtime_io::TestExternalities = self.authorities.into_iter().enumerate().map(|(i, v)|
|
||||
((i as u32).to_keyed_vec(b":auth:"), v.encode())
|
||||
).collect();
|
||||
r.insert(b":auth:len".to_vec(), auth_count.encode());
|
||||
r
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
[package]
|
||||
name = "substrate-runtime-council"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
integer-sqrt = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-consensus = { path = "../consensus", default_features = false }
|
||||
substrate-runtime-democracy = { path = "../democracy", default_features = false }
|
||||
substrate-runtime-session = { path = "../session", default_features = false }
|
||||
substrate-runtime-staking = { path = "../staking", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-democracy/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,457 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Council voting system.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use primitives::traits::{Executable, RefInto};
|
||||
use runtime_io::Hashing;
|
||||
use runtime_support::{StorageValue, StorageMap, IsSubType};
|
||||
use {system, democracy};
|
||||
use super::{Trait, Module as Council};
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn propose(aux, proposal: Box<T::Proposal>) = 0;
|
||||
fn vote(aux, proposal: T::Hash, approve: bool) = 1;
|
||||
fn veto(aux, proposal_hash: T::Hash) = 2;
|
||||
}
|
||||
pub enum PrivCall {
|
||||
fn set_cooloff_period(blocks: T::BlockNumber) = 0;
|
||||
fn set_voting_period(blocks: T::BlockNumber) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait>;
|
||||
|
||||
pub CooloffPeriod get(cooloff_period): b"cov:cooloff" => required T::BlockNumber;
|
||||
pub VotingPeriod get(voting_period): b"cov:period" => required T::BlockNumber;
|
||||
pub Proposals get(proposals): b"cov:prs" => default Vec<(T::BlockNumber, T::Hash)>; // ordered by expiry.
|
||||
pub ProposalOf get(proposal_of): b"cov:pro" => map [ T::Hash => T::Proposal ];
|
||||
pub ProposalVoters get(proposal_voters): b"cov:voters:" => default map [ T::Hash => Vec<T::AccountId> ];
|
||||
pub CouncilVoteOf get(vote_of): b"cov:vote:" => map [ (T::Hash, T::AccountId) => bool ];
|
||||
pub VetoedProposal get(veto_of): b"cov:veto:" => map [ T::Hash => (T::BlockNumber, Vec<T::AccountId>) ];
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
pub fn is_vetoed<B: Borrow<T::Hash>>(proposal: B) -> bool {
|
||||
Self::veto_of(proposal.borrow())
|
||||
.map(|(expiry, _): (T::BlockNumber, Vec<T::AccountId>)| <system::Module<T>>::block_number() < expiry)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn will_still_be_councillor_at(who: &T::AccountId, n: T::BlockNumber) -> bool {
|
||||
<Council<T>>::active_council().iter()
|
||||
.find(|&&(ref a, _)| a == who)
|
||||
.map(|&(_, expires)| expires > n)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn is_councillor(who: &T::AccountId) -> bool {
|
||||
<Council<T>>::active_council().iter()
|
||||
.any(|&(ref a, _)| a == who)
|
||||
}
|
||||
|
||||
pub fn tally(proposal_hash: &T::Hash) -> (u32, u32, u32) {
|
||||
Self::generic_tally(proposal_hash, |w: &T::AccountId, p: &T::Hash| Self::vote_of((*p, w.clone())))
|
||||
}
|
||||
|
||||
// Dispatch
|
||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>) {
|
||||
let expiry = <system::Module<T>>::block_number() + Self::voting_period();
|
||||
assert!(Self::will_still_be_councillor_at(aux.ref_into(), expiry));
|
||||
|
||||
let proposal_hash = T::Hashing::hash_of(&proposal);
|
||||
|
||||
assert!(!Self::is_vetoed(&proposal_hash));
|
||||
|
||||
let mut proposals = Self::proposals();
|
||||
proposals.push((expiry, proposal_hash));
|
||||
proposals.sort_by_key(|&(expiry, _)| expiry);
|
||||
Self::set_proposals(&proposals);
|
||||
|
||||
<ProposalOf<T>>::insert(proposal_hash, *proposal);
|
||||
<ProposalVoters<T>>::insert(proposal_hash, vec![aux.ref_into().clone()]);
|
||||
<CouncilVoteOf<T>>::insert((proposal_hash, aux.ref_into().clone()), true);
|
||||
}
|
||||
|
||||
fn vote(aux: &T::PublicAux, proposal: T::Hash, approve: bool) {
|
||||
if Self::vote_of((proposal, aux.ref_into().clone())).is_none() {
|
||||
let mut voters = Self::proposal_voters(&proposal);
|
||||
voters.push(aux.ref_into().clone());
|
||||
<ProposalVoters<T>>::insert(proposal, voters);
|
||||
}
|
||||
<CouncilVoteOf<T>>::insert((proposal, aux.ref_into().clone()), approve);
|
||||
}
|
||||
|
||||
fn veto(aux: &T::PublicAux, proposal_hash: T::Hash) {
|
||||
assert!(Self::is_councillor(aux.ref_into()), "only councillors may veto council proposals");
|
||||
assert!(<ProposalVoters<T>>::exists(&proposal_hash), "proposal must exist to be vetoed");
|
||||
|
||||
let mut existing_vetoers = Self::veto_of(&proposal_hash)
|
||||
.map(|pair| pair.1)
|
||||
.unwrap_or_else(Vec::new);
|
||||
let insert_position = existing_vetoers.binary_search(aux.ref_into())
|
||||
.expect_err("a councillor may not veto a proposal twice");
|
||||
existing_vetoers.insert(insert_position, aux.ref_into().clone());
|
||||
Self::set_veto_of(&proposal_hash, <system::Module<T>>::block_number() + Self::cooloff_period(), existing_vetoers);
|
||||
|
||||
Self::set_proposals(&Self::proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::<Vec<_>>());
|
||||
<ProposalVoters<T>>::remove(proposal_hash);
|
||||
<ProposalOf<T>>::remove(proposal_hash);
|
||||
for (c, _) in <Council<T>>::active_council() {
|
||||
<CouncilVoteOf<T>>::remove((proposal_hash, c));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_cooloff_period(blocks: T::BlockNumber) {
|
||||
<CooloffPeriod<T>>::put(blocks);
|
||||
}
|
||||
|
||||
fn set_voting_period(blocks: T::BlockNumber) {
|
||||
<VotingPeriod<T>>::put(blocks);
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
|
||||
fn set_veto_of(proposal: &T::Hash, expiry: T::BlockNumber, vetoers: Vec<T::AccountId>) {
|
||||
<VetoedProposal<T>>::insert(proposal, (expiry, vetoers));
|
||||
}
|
||||
|
||||
fn kill_veto_of(proposal: &T::Hash) {
|
||||
<VetoedProposal<T>>::remove(proposal);
|
||||
}
|
||||
|
||||
fn take_tally(proposal_hash: &T::Hash) -> (u32, u32, u32) {
|
||||
Self::generic_tally(proposal_hash, |w: &T::AccountId, p: &T::Hash| <CouncilVoteOf<T>>::take((*p, w.clone())))
|
||||
}
|
||||
|
||||
fn generic_tally<F: Fn(&T::AccountId, &T::Hash) -> Option<bool>>(proposal_hash: &T::Hash, vote_of: F) -> (u32, u32, u32) {
|
||||
let c = <Council<T>>::active_council();
|
||||
let (approve, reject) = c.iter()
|
||||
.filter_map(|&(ref a, _)| vote_of(a, proposal_hash))
|
||||
.map(|approve| if approve { (1, 0) } else { (0, 1) })
|
||||
.fold((0, 0), |(a, b), (c, d)| (a + c, b + d));
|
||||
(approve, reject, c.len() as u32 - approve - reject)
|
||||
}
|
||||
|
||||
fn set_proposals(p: &Vec<(T::BlockNumber, T::Hash)>) {
|
||||
<Proposals<T>>::put(p);
|
||||
}
|
||||
|
||||
fn take_proposal_if_expiring_at(n: T::BlockNumber) -> Option<(T::Proposal, T::Hash)> {
|
||||
let proposals = Self::proposals();
|
||||
match proposals.first() {
|
||||
Some(&(expiry, hash)) if expiry == n => {
|
||||
// yes this is horrible, but fixing it will need substantial work in storage.
|
||||
Self::set_proposals(&proposals[1..].to_vec());
|
||||
let proposal = <ProposalOf<T>>::take(hash).expect("all queued proposal hashes must have associated proposals");
|
||||
Some((proposal, hash))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn end_block(now: T::BlockNumber) {
|
||||
while let Some((proposal, proposal_hash)) = Self::take_proposal_if_expiring_at(now) {
|
||||
let tally = Self::take_tally(&proposal_hash);
|
||||
if let Some(&democracy::PrivCall::cancel_referendum(ref_index)) = IsSubType::<democracy::Module<T>>::is_sub_type(&proposal) {
|
||||
if let (_, 0, 0) = tally {
|
||||
<democracy::Module<T>>::internal_cancel_referendum(ref_index);
|
||||
}
|
||||
} else {
|
||||
if tally.0 > tally.1 + tally.2 {
|
||||
Self::kill_veto_of(&proposal_hash);
|
||||
match tally {
|
||||
(_, 0, 0) => <democracy::Module<T>>::internal_start_referendum(proposal, democracy::VoteThreshold::SuperMajorityAgainst),
|
||||
_ => <democracy::Module<T>>::internal_start_referendum(proposal, democracy::VoteThreshold::SimpleMajority),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Executable for Council<T> {
|
||||
fn execute() {
|
||||
let n = <system::Module<T>>::block_number();
|
||||
Self::end_block(n);
|
||||
<Module<T>>::end_block(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ::tests::*;
|
||||
use runtime_support::Hashable;
|
||||
use democracy::VoteThreshold;
|
||||
|
||||
type CouncilVoting = super::Module<Test>;
|
||||
|
||||
#[test]
|
||||
fn basic_environment_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
assert_eq!(Staking::bonding_duration(), 0);
|
||||
assert_eq!(CouncilVoting::cooloff_period(), 2);
|
||||
assert_eq!(CouncilVoting::voting_period(), 1);
|
||||
assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 1), true);
|
||||
assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 10), false);
|
||||
assert_eq!(CouncilVoting::will_still_be_councillor_at(&4, 10), false);
|
||||
assert_eq!(CouncilVoting::is_councillor(&1), true);
|
||||
assert_eq!(CouncilVoting::is_councillor(&4), false);
|
||||
assert_eq!(CouncilVoting::proposals(), Vec::<(u64, H256)>::new());
|
||||
assert_eq!(CouncilVoting::proposal_voters(H256::default()), Vec::<u64>::new());
|
||||
assert_eq!(CouncilVoting::is_vetoed(&H256::default()), false);
|
||||
assert_eq!(CouncilVoting::vote_of((H256::default(), 1)), None);
|
||||
assert_eq!(CouncilVoting::tally(&H256::default()), (0, 0, 3));
|
||||
});
|
||||
}
|
||||
|
||||
fn bonding_duration_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::PrivCall::set_bonding_duration(value))
|
||||
}
|
||||
|
||||
fn cancel_referendum_proposal(id: u32) -> Proposal {
|
||||
Proposal::Democracy(democracy::PrivCall::cancel_referendum(id))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn referendum_cancellation_should_work_when_unanimous() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(cancellation));
|
||||
CouncilVoting::vote(&2, hash, true);
|
||||
CouncilVoting::vote(&3, hash, true);
|
||||
assert_eq!(CouncilVoting::proposals(), vec![(2, hash)]);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(2);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
assert_eq!(Democracy::active_referendums(), vec![]);
|
||||
assert_eq!(Staking::bonding_duration(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn referendum_cancellation_should_fail_when_not_unanimous() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(cancellation));
|
||||
CouncilVoting::vote(&2, hash, true);
|
||||
CouncilVoting::vote(&3, hash, false);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(2);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn referendum_cancellation_should_fail_when_abstentions() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(cancellation));
|
||||
CouncilVoting::vote(&2, hash, true);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(2);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::veto(&2, hash);
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums().len(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn double_veto_should_panic() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::veto(&2, hash);
|
||||
|
||||
System::set_block_number(3);
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::veto(&2, hash);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn retry_in_cooloff_should_panic() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::veto(&2, hash);
|
||||
|
||||
System::set_block_number(2);
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retry_after_cooloff_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::veto(&2, hash);
|
||||
|
||||
System::set_block_number(3);
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::vote(&2, hash, false);
|
||||
CouncilVoting::vote(&3, hash, true);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(4);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 7, bonding_duration_proposal(42), VoteThreshold::SimpleMajority)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alternative_double_veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::veto(&2, hash);
|
||||
|
||||
System::set_block_number(3);
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::veto(&3, hash);
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums().len(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_propose_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 1);
|
||||
assert_eq!(CouncilVoting::proposal_voters(&hash), vec![1]);
|
||||
assert_eq!(CouncilVoting::vote_of((hash, 1)), Some(true));
|
||||
assert_eq!(CouncilVoting::tally(&hash), (1, 0, 2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unvoted_proposal_should_expire_without_action() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (1, 0, 2));
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(2);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums().len(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unanimous_proposal_should_expire_with_biased_referendum() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::vote(&2, proposal.blake2_256().into(), true);
|
||||
CouncilVoting::vote(&3, proposal.blake2_256().into(), true);
|
||||
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (3, 0, 0));
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(2);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SuperMajorityAgainst)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn majority_proposal_should_expire_with_unbiased_referendum() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||
CouncilVoting::vote(&2, proposal.blake2_256().into(), true);
|
||||
CouncilVoting::vote(&3, proposal.blake2_256().into(), false);
|
||||
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (2, 1, 0));
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(2);
|
||||
CouncilVoting::end_block(System::block_number());
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SimpleMajority)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn propose_by_public_should_panic() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
CouncilVoting::propose(&4, Box::new(proposal));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "substrate-runtime-democracy"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-consensus = { path = "../consensus", default_features = false }
|
||||
substrate-runtime-session = { path = "../session", default_features = false }
|
||||
substrate-runtime-staking = { path = "../staking", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"safe-mix/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
@@ -0,0 +1,659 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Democratic system: Handles administration of general stakeholder voting.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_session as session;
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
extern crate substrate_runtime_system as system;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{Zero, Executable, RefInto, As};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType};
|
||||
|
||||
mod vote_threshold;
|
||||
pub use vote_threshold::{Approved, VoteThreshold};
|
||||
|
||||
/// A proposal index.
|
||||
pub type PropIndex = u32;
|
||||
/// A referendum index.
|
||||
pub type ReferendumIndex = u32;
|
||||
|
||||
pub trait Trait: staking::Trait + Sized {
|
||||
type Proposal: Parameter + Dispatchable + IsSubType<Module<Self>>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn propose(aux, proposal: Box<T::Proposal>, value: T::Balance) = 0;
|
||||
fn second(aux, proposal: PropIndex) = 1;
|
||||
fn vote(aux, ref_index: ReferendumIndex, approve_proposal: bool) = 2;
|
||||
}
|
||||
pub enum PrivCall {
|
||||
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) = 0;
|
||||
fn cancel_referendum(ref_index: ReferendumIndex) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait>;
|
||||
|
||||
// The number of (public) proposals that have been made so far.
|
||||
pub PublicPropCount get(public_prop_count): b"dem:ppc" => default PropIndex;
|
||||
// The public proposals. Unsorted.
|
||||
pub PublicProps get(public_props): b"dem:pub" => default Vec<(PropIndex, T::Proposal, T::AccountId)>;
|
||||
// Those who have locked a deposit.
|
||||
pub DepositOf get(deposit_of): b"dem:dep:" => map [ PropIndex => (T::Balance, Vec<T::AccountId>) ];
|
||||
// How often (in blocks) new public referenda are launched.
|
||||
pub LaunchPeriod get(launch_period): b"dem:lau" => required T::BlockNumber;
|
||||
// The minimum amount to be used as a deposit for a public referendum proposal.
|
||||
pub MinimumDeposit get(minimum_deposit): b"dem:min" => required T::Balance;
|
||||
|
||||
// How often (in blocks) to check for new votes.
|
||||
pub VotingPeriod get(voting_period): b"dem:per" => required T::BlockNumber;
|
||||
|
||||
// The next free referendum index, aka the number of referendums started so far.
|
||||
pub ReferendumCount get(referendum_count): b"dem:rco" => default ReferendumIndex;
|
||||
// The next referendum index that should be tallied.
|
||||
pub NextTally get(next_tally): b"dem:nxt" => default ReferendumIndex;
|
||||
// Information concerning any given referendum.
|
||||
pub ReferendumInfoOf get(referendum_info): b"dem:pro:" => map [ ReferendumIndex => (T::BlockNumber, T::Proposal, VoteThreshold) ];
|
||||
|
||||
// Get the voters for the current proposal.
|
||||
pub VotersFor get(voters_for): b"dem:vtr:" => default map [ ReferendumIndex => Vec<T::AccountId> ];
|
||||
|
||||
// Get the vote, if Some, of `who`.
|
||||
pub VoteOf get(vote_of): b"dem:vot:" => map [ (ReferendumIndex, T::AccountId) => bool ];
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
|
||||
// exposed immutables.
|
||||
|
||||
/// Get the amount locked in support of `proposal`; false if proposal isn't a valid proposal
|
||||
/// index.
|
||||
pub fn locked_for(proposal: PropIndex) -> Option<T::Balance> {
|
||||
Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len()))
|
||||
}
|
||||
|
||||
/// Return true if `ref_index` is an on-going referendum.
|
||||
pub fn is_active_referendum(ref_index: ReferendumIndex) -> bool {
|
||||
<ReferendumInfoOf<T>>::exists(ref_index)
|
||||
}
|
||||
|
||||
/// Get all referendums currently active.
|
||||
pub fn active_referendums() -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold)> {
|
||||
let next = Self::next_tally();
|
||||
let last = Self::referendum_count();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| Self::referendum_info(i).map(|(n, p, t)| (i, n, p, t)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get all referendums ready for tally at block `n`.
|
||||
pub fn maturing_referendums_at(n: T::BlockNumber) -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold)> {
|
||||
let next = Self::next_tally();
|
||||
let last = Self::referendum_count();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| Self::referendum_info(i).map(|(n, p, t)| (i, n, p, t)))
|
||||
.take_while(|&(_, block_number, _, _)| block_number == n)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) {
|
||||
Self::voters_for(ref_index).iter()
|
||||
.map(|a| (<staking::Module<T>>::balance(a), Self::vote_of((ref_index, a.clone())).expect("all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed")))
|
||||
.map(|(bal, vote)| if vote { (bal, Zero::zero()) } else { (Zero::zero(), bal) })
|
||||
.fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d))
|
||||
}
|
||||
|
||||
// dispatching.
|
||||
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) {
|
||||
assert!(value >= Self::minimum_deposit());
|
||||
assert!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), value));
|
||||
|
||||
let index = Self::public_prop_count();
|
||||
<PublicPropCount<T>>::put(index + 1);
|
||||
<DepositOf<T>>::insert(index, (value, vec![aux.ref_into().clone()]));
|
||||
|
||||
let mut props = Self::public_props();
|
||||
props.push((index, (*proposal).clone(), aux.ref_into().clone()));
|
||||
<PublicProps<T>>::put(props);
|
||||
}
|
||||
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn second(aux: &T::PublicAux, proposal: PropIndex) {
|
||||
let mut deposit = Self::deposit_of(proposal).expect("can only second an existing proposal");
|
||||
assert!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), deposit.0));
|
||||
|
||||
deposit.1.push(aux.ref_into().clone());
|
||||
<DepositOf<T>>::insert(proposal, deposit);
|
||||
}
|
||||
|
||||
/// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;
|
||||
/// false would be a vote to keep the status quo..
|
||||
fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) {
|
||||
if !Self::is_active_referendum(ref_index) {
|
||||
panic!("vote given for invalid referendum.")
|
||||
}
|
||||
if <staking::Module<T>>::balance(aux.ref_into()).is_zero() {
|
||||
panic!("transactor must have balance to signal approval.");
|
||||
}
|
||||
if !<VoteOf<T>>::exists(&(ref_index, aux.ref_into().clone())) {
|
||||
let mut voters = Self::voters_for(ref_index);
|
||||
voters.push(aux.ref_into().clone());
|
||||
<VotersFor<T>>::insert(ref_index, voters);
|
||||
}
|
||||
<VoteOf<T>>::insert(&(ref_index, aux.ref_into().clone()), approve_proposal);
|
||||
}
|
||||
|
||||
/// Start a referendum.
|
||||
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) {
|
||||
Self::inject_referendum(<system::Module<T>>::block_number() + Self::voting_period(), *proposal, vote_threshold);
|
||||
}
|
||||
|
||||
/// Remove a referendum.
|
||||
fn cancel_referendum(ref_index: ReferendumIndex) {
|
||||
Self::clear_referendum(ref_index);
|
||||
}
|
||||
|
||||
// exposed mutables.
|
||||
|
||||
/// Start a referendum. Can be called directly by the council.
|
||||
pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) {
|
||||
<Module<T>>::inject_referendum(<system::Module<T>>::block_number() + <Module<T>>::voting_period(), proposal, vote_threshold);
|
||||
}
|
||||
|
||||
/// Remove a referendum. Can be called directly by the council.
|
||||
pub fn internal_cancel_referendum(ref_index: ReferendumIndex) {
|
||||
<Module<T>>::clear_referendum(ref_index);
|
||||
}
|
||||
|
||||
// private.
|
||||
|
||||
/// Start a referendum
|
||||
fn inject_referendum(
|
||||
end: T::BlockNumber,
|
||||
proposal: T::Proposal,
|
||||
vote_threshold: VoteThreshold
|
||||
) -> ReferendumIndex {
|
||||
let ref_index = Self::referendum_count();
|
||||
if ref_index > 0 && Self::referendum_info(ref_index - 1).map(|i| i.0 > end).unwrap_or(false) {
|
||||
panic!("Cannot inject a referendum that ends earlier than preceeding referendum");
|
||||
}
|
||||
|
||||
<ReferendumCount<T>>::put(ref_index + 1);
|
||||
<ReferendumInfoOf<T>>::insert(ref_index, (end, proposal, vote_threshold));
|
||||
ref_index
|
||||
}
|
||||
|
||||
/// Remove all info on a referendum.
|
||||
fn clear_referendum(ref_index: ReferendumIndex) {
|
||||
<ReferendumInfoOf<T>>::remove(ref_index);
|
||||
<VotersFor<T>>::remove(ref_index);
|
||||
for v in Self::voters_for(ref_index) {
|
||||
<VoteOf<T>>::remove((ref_index, v));
|
||||
}
|
||||
}
|
||||
|
||||
/// Current era is ending; we should finish up any proposals.
|
||||
fn end_block(now: T::BlockNumber) {
|
||||
// pick out another public referendum if it's time.
|
||||
if (now % Self::launch_period()).is_zero() {
|
||||
let mut public_props = Self::public_props();
|
||||
if let Some((winner_index, _)) = public_props.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|x| Self::locked_for((x.1).0).expect("All current public proposals have an amount locked"))
|
||||
{
|
||||
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
||||
let (deposit, depositors): (T::Balance, Vec<T::AccountId>) =
|
||||
<DepositOf<T>>::take(prop_index).expect("depositors always exist for current proposals");
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
<staking::Module<T>>::refund(d, deposit);
|
||||
}
|
||||
<PublicProps<T>>::put(public_props);
|
||||
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove);
|
||||
}
|
||||
}
|
||||
|
||||
// tally up votes for any expiring referenda.
|
||||
for (index, _, proposal, vote_threshold) in Self::maturing_referendums_at(now) {
|
||||
let (approve, against) = Self::tally(index);
|
||||
let total_stake = <staking::Module<T>>::total_stake();
|
||||
Self::clear_referendum(index);
|
||||
if vote_threshold.approved(approve, against, total_stake) {
|
||||
proposal.dispatch();
|
||||
}
|
||||
<NextTally<T>>::put(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Executable for Module<T> {
|
||||
fn execute() {
|
||||
Self::end_block(<system::Module<T>>::block_number());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub struct GenesisConfig<T: Trait> {
|
||||
pub launch_period: T::BlockNumber,
|
||||
pub voting_period: T::BlockNumber,
|
||||
pub minimum_deposit: T::Balance,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> GenesisConfig<T> {
|
||||
pub fn new() -> Self {
|
||||
GenesisConfig {
|
||||
launch_period: T::BlockNumber::sa(1),
|
||||
voting_period: T::BlockNumber::sa(1),
|
||||
minimum_deposit: T::Balance::sa(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extended() -> Self {
|
||||
GenesisConfig {
|
||||
launch_period: T::BlockNumber::sa(1),
|
||||
voting_period: T::BlockNumber::sa(3),
|
||||
minimum_deposit: T::Balance::sa(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
launch_period: T::BlockNumber::sa(1000),
|
||||
voting_period: T::BlockNumber::sa(1000),
|
||||
minimum_deposit: T::Balance::sa(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
|
||||
{
|
||||
fn build_externalities(self) -> runtime_io::TestExternalities {
|
||||
use codec::Slicable;
|
||||
use runtime_io::twox_128;
|
||||
|
||||
map![
|
||||
twox_128(<LaunchPeriod<T>>::key()).to_vec() => self.launch_period.encode(),
|
||||
twox_128(<VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode(),
|
||||
twox_128(<MinimumDeposit<T>>::key()).to_vec() => self.minimum_deposit.encode()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::H256;
|
||||
use primitives::BuildExternalities;
|
||||
use primitives::traits::{HasPublicAux, Identity};
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_dispatch! {
|
||||
pub enum Proposal {
|
||||
Session = 0,
|
||||
Staking = 1,
|
||||
Democracy = 2,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Test;
|
||||
impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type SessionKey = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
type Balance = u64;
|
||||
type DetermineContractAddress = staking::DummyContractAddressFor;
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Proposal = Proposal;
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
|
||||
t.extend(consensus::GenesisConfig::<Test>{
|
||||
authorities: vec![],
|
||||
}.build_externalities());
|
||||
t.extend(session::GenesisConfig::<Test>{
|
||||
session_length: 1, //??? or 2?
|
||||
validators: vec![10, 20],
|
||||
}.build_externalities());
|
||||
t.extend(staking::GenesisConfig::<Test>{
|
||||
sessions_per_era: 1,
|
||||
current_era: 0,
|
||||
balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
|
||||
intentions: vec![],
|
||||
validator_count: 2,
|
||||
bonding_duration: 3,
|
||||
transaction_fee: 0,
|
||||
}.build_externalities());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
launch_period: 1,
|
||||
voting_period: 1,
|
||||
minimum_deposit: 1,
|
||||
}.build_externalities());
|
||||
t
|
||||
}
|
||||
|
||||
type System = system::Module<Test>;
|
||||
type Session = session::Module<Test>;
|
||||
type Staking = staking::Module<Test>;
|
||||
type Democracy = Module<Test>;
|
||||
|
||||
#[test]
|
||||
fn params_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(Democracy::launch_period(), 1);
|
||||
assert_eq!(Democracy::voting_period(), 1);
|
||||
assert_eq!(Democracy::minimum_deposit(), 1);
|
||||
assert_eq!(Democracy::referendum_count(), 0);
|
||||
assert_eq!(Staking::sessions_per_era(), 1);
|
||||
assert_eq!(Staking::total_stake(), 210);
|
||||
});
|
||||
}
|
||||
|
||||
fn propose_sessions_per_era(who: u64, value: u64, locked: u64) {
|
||||
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_sessions_per_era(value))), locked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_for_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
propose_sessions_per_era(1, 2, 2);
|
||||
propose_sessions_per_era(1, 4, 4);
|
||||
propose_sessions_per_era(1, 3, 3);
|
||||
assert_eq!(Democracy::locked_for(0), Some(2));
|
||||
assert_eq!(Democracy::locked_for(1), Some(4));
|
||||
assert_eq!(Democracy::locked_for(2), Some(3));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_proposal_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
propose_sessions_per_era(1, 2, 1);
|
||||
Democracy::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(2);
|
||||
let r = 0;
|
||||
Democracy::vote(&1, r, true);
|
||||
|
||||
assert_eq!(Democracy::referendum_count(), 1);
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
||||
assert_eq!(Democracy::tally(r), (10, 0));
|
||||
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deposit_for_proposals_should_be_taken() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
propose_sessions_per_era(1, 2, 5);
|
||||
Democracy::second(&2, 0);
|
||||
Democracy::second(&5, 0);
|
||||
Democracy::second(&5, 0);
|
||||
Democracy::second(&5, 0);
|
||||
assert_eq!(Staking::balance(&1), 5);
|
||||
assert_eq!(Staking::balance(&2), 15);
|
||||
assert_eq!(Staking::balance(&5), 35);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deposit_for_proposals_should_be_returned() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
propose_sessions_per_era(1, 2, 5);
|
||||
Democracy::second(&2, 0);
|
||||
Democracy::second(&5, 0);
|
||||
Democracy::second(&5, 0);
|
||||
Democracy::second(&5, 0);
|
||||
Democracy::end_block(System::block_number());
|
||||
assert_eq!(Staking::balance(&1), 10);
|
||||
assert_eq!(Staking::balance(&2), 20);
|
||||
assert_eq!(Staking::balance(&5), 50);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn proposal_with_deposit_below_minimum_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
propose_sessions_per_era(1, 2, 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn poor_proposer_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
propose_sessions_per_era(1, 2, 11);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn poor_seconder_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
propose_sessions_per_era(2, 2, 11);
|
||||
Democracy::second(&1, 0);
|
||||
});
|
||||
}
|
||||
|
||||
fn propose_bonding_duration(who: u64, value: u64, locked: u64) {
|
||||
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_bonding_duration(value))), locked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runners_up_should_come_after() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(0);
|
||||
propose_bonding_duration(1, 2, 2);
|
||||
propose_bonding_duration(1, 4, 4);
|
||||
propose_bonding_duration(1, 3, 3);
|
||||
Democracy::end_block(System::block_number());
|
||||
|
||||
System::set_block_number(1);
|
||||
Democracy::vote(&1, 0, true);
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
assert_eq!(Staking::bonding_duration(), 4);
|
||||
|
||||
System::set_block_number(2);
|
||||
Democracy::vote(&1, 1, true);
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
assert_eq!(Staking::bonding_duration(), 3);
|
||||
|
||||
System::set_block_number(3);
|
||||
Democracy::vote(&1, 2, true);
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
assert_eq!(Staking::bonding_duration(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
fn sessions_per_era_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::PrivCall::set_sessions_per_era(value))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_passing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
||||
Democracy::vote(&1, r, true);
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
||||
assert_eq!(Democracy::tally(r), (10, 0));
|
||||
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_referendum_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
||||
Democracy::vote(&1, r, true);
|
||||
Democracy::cancel_referendum(r);
|
||||
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_failing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
||||
Democracy::vote(&1, r, false);
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(false));
|
||||
assert_eq!(Democracy::tally(r), (0, 10));
|
||||
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn controversial_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
||||
Democracy::vote(&1, r, true);
|
||||
Democracy::vote(&2, r, false);
|
||||
Democracy::vote(&3, r, false);
|
||||
Democracy::vote(&4, r, true);
|
||||
Democracy::vote(&5, r, false);
|
||||
Democracy::vote(&6, r, true);
|
||||
|
||||
assert_eq!(Democracy::tally(r), (110, 100));
|
||||
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn controversial_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
||||
Democracy::vote(&5, r, false);
|
||||
Democracy::vote(&6, r, true);
|
||||
|
||||
assert_eq!(Democracy::tally(r), (60, 50));
|
||||
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
assert_eq!(Staking::total_stake(), 210);
|
||||
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
||||
Democracy::vote(&4, r, true);
|
||||
Democracy::vote(&5, r, false);
|
||||
Democracy::vote(&6, r, true);
|
||||
|
||||
assert_eq!(Democracy::tally(r), (100, 50));
|
||||
|
||||
Democracy::end_block(System::block_number());
|
||||
Staking::check_new_era();
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Voting thresholds.
|
||||
|
||||
use primitives::traits::IntegerSquareRoot;
|
||||
use codec::{Input, Slicable};
|
||||
use rstd::ops::{Add, Mul, Div};
|
||||
|
||||
/// A means of determining if a vote is past pass threshold.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
pub enum VoteThreshold {
|
||||
/// A supermajority of approvals is needed to pass this vote.
|
||||
SuperMajorityApprove,
|
||||
/// A supermajority of rejects is needed to fail this vote.
|
||||
SuperMajorityAgainst,
|
||||
/// A simple majority of approvals is needed to pass this vote.
|
||||
SimpleMajority,
|
||||
}
|
||||
|
||||
impl Slicable for VoteThreshold {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
input.read_byte().and_then(|v| match v {
|
||||
0 => Some(VoteThreshold::SuperMajorityApprove),
|
||||
1 => Some(VoteThreshold::SuperMajorityAgainst),
|
||||
2 => Some(VoteThreshold::SimpleMajority),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(&[match *self {
|
||||
VoteThreshold::SuperMajorityApprove => 0u8,
|
||||
VoteThreshold::SuperMajorityAgainst => 1u8,
|
||||
VoteThreshold::SimpleMajority => 2u8,
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Approved<Balance> {
|
||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
||||
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||
/// overall outcome is in favour of approval.
|
||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool;
|
||||
}
|
||||
|
||||
impl<Balance: IntegerSquareRoot + Ord + Add<Balance, Output = Balance> + Mul<Balance, Output = Balance> + Div<Balance, Output = Balance> + Copy> Approved<Balance> for VoteThreshold {
|
||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
||||
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||
/// overall outcome is in favour of approval.
|
||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool {
|
||||
let voters = approve + against;
|
||||
match *self {
|
||||
VoteThreshold::SuperMajorityApprove =>
|
||||
voters.integer_sqrt() * approve / electorate.integer_sqrt() > against,
|
||||
VoteThreshold::SuperMajorityAgainst =>
|
||||
approve > voters.integer_sqrt() * against / electorate.integer_sqrt(),
|
||||
VoteThreshold::SimpleMajority => approve > against,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "substrate-runtime-executive"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-primitives = { path = "../../primitives" }
|
||||
substrate-runtime-session = { path = "../session" }
|
||||
substrate-runtime-staking = { path = "../staking" }
|
||||
substrate-runtime-consensus = { path = "../consensus" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-support/std",
|
||||
"serde/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Executive: Handles all of the top-level stuff; essentially just executing blocks/extrinsics.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_system as system;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_session as session;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
|
||||
#[cfg(feature = "std")] extern crate serde;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::marker::PhantomData;
|
||||
use runtime_io::Hashing;
|
||||
use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, Executable, MakePayment};
|
||||
use codec::Slicable;
|
||||
|
||||
pub struct Executive<
|
||||
System,
|
||||
Block,
|
||||
Payment,
|
||||
Finalisation,
|
||||
>(PhantomData<(System, Block, Payment, Finalisation)>);
|
||||
|
||||
impl<
|
||||
System: system::Trait,
|
||||
Block: traits::Block<Header = System::Header>,
|
||||
Payment: MakePayment<System::AccountId>,
|
||||
Finalisation: Executable,
|
||||
> Executive<System, Block, Payment, Finalisation> where
|
||||
Block::Extrinsic: Checkable + Slicable,
|
||||
<Block::Extrinsic as Checkable>::Checked: Applyable<Index = System::Index, AccountId = System::AccountId>
|
||||
{
|
||||
/// Start the execution of a particular block.
|
||||
pub fn initialise_block(header: &System::Header) {
|
||||
<system::Module<System>>::initialise(header.number(), header.parent_hash(), header.extrinsics_root());
|
||||
}
|
||||
|
||||
fn initial_checks(block: &Block) {
|
||||
let header = block.header();
|
||||
|
||||
// check parent_hash is correct.
|
||||
let n = header.number().clone();
|
||||
assert!(
|
||||
n > System::BlockNumber::zero() && <system::Module<System>>::block_hash(n - System::BlockNumber::one()) == *header.parent_hash(),
|
||||
"Parent hash should be valid."
|
||||
);
|
||||
|
||||
// check transaction trie root represents the transactions.
|
||||
let txs = block.extrinsics().iter().map(Slicable::encode).collect::<Vec<_>>();
|
||||
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
|
||||
let txs_root = System::Hashing::enumerated_trie_root(&txs);
|
||||
header.extrinsics_root().check_equal(&txs_root);
|
||||
assert!(header.extrinsics_root() == &txs_root, "Transaction trie root must be valid.");
|
||||
}
|
||||
|
||||
/// Actually execute all transitioning for `block`.
|
||||
pub fn execute_block(block: Block) {
|
||||
Self::initialise_block(block.header());
|
||||
|
||||
// any initial checks
|
||||
Self::initial_checks(&block);
|
||||
|
||||
// execute transactions
|
||||
let (header, extrinsics) = block.deconstruct();
|
||||
extrinsics.into_iter().for_each(Self::apply_extrinsic);
|
||||
|
||||
// post-transactional book-keeping.
|
||||
Finalisation::execute();
|
||||
|
||||
// any final checks
|
||||
Self::final_checks(&header);
|
||||
|
||||
// any stuff that we do after taking the storage root.
|
||||
Self::post_finalise(&header);
|
||||
}
|
||||
|
||||
/// Finalise the block - it is up the caller to ensure that all header fields are valid
|
||||
/// except state-root.
|
||||
pub fn finalise_block() -> System::Header {
|
||||
Finalisation::execute();
|
||||
|
||||
let header = <system::Module<System>>::finalise();
|
||||
Self::post_finalise(&header);
|
||||
|
||||
header
|
||||
}
|
||||
|
||||
/// Apply outside of the block execution function.
|
||||
/// This doesn't attempt to validate anything regarding the block.
|
||||
pub fn apply_extrinsic(utx: Block::Extrinsic) {
|
||||
// Verify the signature is good.
|
||||
let tx = match utx.check() {
|
||||
Ok(tx) => tx,
|
||||
Err(_) => panic!("All transactions should be properly signed"),
|
||||
};
|
||||
|
||||
{
|
||||
// check index
|
||||
let expected_index = <system::Module<System>>::account_index(tx.sender());
|
||||
assert!(tx.index() == &expected_index, "All transactions should have the correct nonce");
|
||||
|
||||
// increment nonce in storage
|
||||
<system::Module<System>>::inc_account_index(tx.sender());
|
||||
}
|
||||
|
||||
// pay any fees.
|
||||
Payment::make_payment(tx.sender());
|
||||
|
||||
// decode parameters and dispatch
|
||||
tx.apply();
|
||||
}
|
||||
|
||||
fn final_checks(header: &System::Header) {
|
||||
// check digest
|
||||
assert!(header.digest() == &<system::Module<System>>::digest());
|
||||
|
||||
// remove temporaries.
|
||||
<system::Module<System>>::finalise();
|
||||
|
||||
// check storage root.
|
||||
let storage_root = System::Hashing::storage_root();
|
||||
header.state_root().check_equal(&storage_root);
|
||||
assert!(header.state_root() == &storage_root, "Storage root must match that calculated.");
|
||||
}
|
||||
|
||||
fn post_finalise(header: &System::Header) {
|
||||
// store the header hash in storage; we can't do it before otherwise there would be a
|
||||
// cyclic dependency.
|
||||
<system::Module<System>>::record_block_hash(header)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use staking::Call;
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::H256;
|
||||
use primitives::BuildExternalities;
|
||||
use primitives::traits::{HasPublicAux, Identity, Header as HeaderT};
|
||||
use primitives::testing::{Digest, Header, Block};
|
||||
|
||||
pub struct Test;
|
||||
impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type SessionKey = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = substrate_primitives::H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
type Balance = u64;
|
||||
type DetermineContractAddress = staking::DummyContractAddressFor;
|
||||
}
|
||||
|
||||
type TestXt = primitives::testing::TestXt<Call<Test>>;
|
||||
type Executive = super::Executive<Test, Block<TestXt>, staking::Module<Test>, (session::Module<Test>, staking::Module<Test>)>;
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_dispatch_works() {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
|
||||
t.extend(staking::GenesisConfig::<Test> {
|
||||
sessions_per_era: 0,
|
||||
current_era: 0,
|
||||
balances: vec![(1, 111)],
|
||||
intentions: vec![],
|
||||
validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
transaction_fee: 10,
|
||||
}.build_externalities());
|
||||
let xt = primitives::testing::TestXt((1, 0, Call::transfer(2, 69)));
|
||||
with_externalities(&mut t, || {
|
||||
Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default()));
|
||||
Executive::apply_extrinsic(xt);
|
||||
assert_eq!(<staking::Module<Test>>::balance(&1), 32);
|
||||
assert_eq!(<staking::Module<Test>>::balance(&2), 69);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
|
||||
t.extend(consensus::GenesisConfig::<Test>::default().build_externalities());
|
||||
t.extend(session::GenesisConfig::<Test>::default().build_externalities());
|
||||
t.extend(staking::GenesisConfig::<Test>::default().build_externalities());
|
||||
t
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_import_works() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Executive::execute_block(Block {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("9228e363883f4f5a01981985b5598d1a767e987eb3ccea017a0e14cac7acc79d").into(),
|
||||
extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
extrinsics: vec![],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn block_import_of_bad_state_root_fails() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Executive::execute_block(Block {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: [0u8; 32].into(),
|
||||
extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
extrinsics: vec![],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn block_import_of_bad_extrinsic_root_fails() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Executive::execute_block(Block {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("93dde1251278e65430baf291337ba219bacfa9ad583c52513b12cf1974109a97").into(),
|
||||
extrinsics_root: [0u8; 32].into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
extrinsics: vec![],
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "substrate-runtime-primitives"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
num-traits = { version = "0.2", default_features = false }
|
||||
integer-sqrt = "0.1.0"
|
||||
serde = { version = "1.0", optional = true }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"num-traits/std",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
]
|
||||
@@ -0,0 +1,398 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementations of Extrinsic/Header/Block.
|
||||
|
||||
#[cfg(feature = "std")] use serde::Serialize;
|
||||
use rstd::prelude::*;
|
||||
use codec::{Slicable, Input};
|
||||
use runtime_support::AuxDispatchable;
|
||||
use traits;
|
||||
use rstd::ops;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait MaybeSerializeDebug: Serialize + Debug {}
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Serialize + Debug> MaybeSerializeDebug for T {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait MaybeSerializeDebug {}
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> MaybeSerializeDebug for T {}
|
||||
|
||||
pub trait Member: MaybeSerializeDebug + Eq + PartialEq + Clone {}
|
||||
impl<T: MaybeSerializeDebug + Eq + PartialEq + Clone> Member for T {}
|
||||
|
||||
/// A vetted and verified extrinsic from the external world.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
pub struct Extrinsic<AccountId, Index, Call> where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
{
|
||||
/// Who signed it (note this is not a signature).
|
||||
pub signed: AccountId,
|
||||
/// The number of extrinsics have come before from the same signer.
|
||||
pub index: Index,
|
||||
/// The function that should be called.
|
||||
pub function: Call,
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call> Slicable for Extrinsic<AccountId, Index, Call> where
|
||||
AccountId: Member + Slicable,
|
||||
Index: Member + Slicable,
|
||||
Call: Member + Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Extrinsic {
|
||||
signed: Slicable::decode(input)?,
|
||||
index: Slicable::decode(input)?,
|
||||
function: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
self.signed.using_encoded(|s| v.extend(s));
|
||||
self.index.using_encoded(|s| v.extend(s));
|
||||
self.function.using_encoded(|s| v.extend(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
/// A extrinsics right from the external world. Unchecked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize))]
|
||||
pub struct UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
/// The actual extrinsic information.
|
||||
pub extrinsic: Extrinsic<AccountId, Index, Call>,
|
||||
/// The signature; should be an Ed25519 signature applied to the serialised `extrinsic` field.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member + Slicable,
|
||||
Index: Member + Slicable,
|
||||
Call: Member + Slicable,
|
||||
Signature: Member + Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of u32, which has the total number of bytes following (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = Slicable::decode(input)?;
|
||||
|
||||
Some(UncheckedExtrinsic {
|
||||
extrinsic: Slicable::decode(input)?,
|
||||
signature: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
// need to prefix with the total length as u32 to ensure it's binary comptible with
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.extrinsic.signed.using_encoded(|s| v.extend(s));
|
||||
self.extrinsic.index.using_encoded(|s| v.extend(s));
|
||||
self.extrinsic.function.using_encoded(|s| v.extend(s));
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
length.using_encoded(|s| v[0..4].copy_from_slice(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<AccountId, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedExtrinsic({:?})", self.extrinsic)
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> traits::Checkable for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member + traits::Verify<Signer = AccountId>,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable
|
||||
{
|
||||
type Checked = CheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
|
||||
fn check(self) -> Result<Self::Checked, Self> {
|
||||
if ::codec::Slicable::using_encoded(&self.extrinsic, |msg|
|
||||
self.signature.verify(msg, &self.extrinsic.signed)
|
||||
) {
|
||||
Ok(CheckedExtrinsic(self))
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-safe indicator that a extrinsic has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
(UncheckedExtrinsic<AccountId, Index, Call, Signature>)
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member;
|
||||
|
||||
impl<AccountId, Index, Call, Signature> CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
/// Get a reference to the checked signature.
|
||||
pub fn signature(&self) -> &Signature {
|
||||
&self.0.signature
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> ops::Deref
|
||||
for CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
type Target = Extrinsic<AccountId, Index, Call>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0.extrinsic
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member + AuxDispatchable<Aux = AccountId>,
|
||||
Signature: Member
|
||||
{
|
||||
type Index = Index;
|
||||
type AccountId = AccountId;
|
||||
|
||||
fn index(&self) -> &Self::Index {
|
||||
&self.0.extrinsic.index
|
||||
}
|
||||
|
||||
fn sender(&self) -> &Self::AccountId {
|
||||
&self.0.extrinsic.signed
|
||||
}
|
||||
|
||||
fn apply(self) {
|
||||
let xt = self.0.extrinsic;
|
||||
xt.function.dispatch(&xt.signed);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
pub struct Digest<Item: Member> {
|
||||
pub logs: Vec<Item>,
|
||||
}
|
||||
impl<Item> Slicable for Digest<Item> where
|
||||
Item: Member + Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Digest { logs: Slicable::decode(input)? })
|
||||
}
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.logs.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl<Item> traits::Digest for Digest<Item> where
|
||||
Item: Member + Slicable
|
||||
{
|
||||
type Item = Item;
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
self.logs.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a block header for a substrate chain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Header<Number, Hash, DigestItem> where
|
||||
Number: Member,
|
||||
Hash: Member,
|
||||
DigestItem: Member,
|
||||
{
|
||||
/// The parent hash.
|
||||
pub parent_hash: Hash,
|
||||
/// The block number.
|
||||
pub number: Number,
|
||||
/// The state trie merkle root
|
||||
pub state_root: Hash,
|
||||
/// The merkle root of the extrinsics.
|
||||
pub extrinsics_root: Hash,
|
||||
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
|
||||
pub digest: Digest<DigestItem>,
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> Slicable for Header<Number, Hash, DigestItem> where
|
||||
Number: Member + Slicable,
|
||||
Hash: Member + Slicable,
|
||||
DigestItem: Member + Slicable,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
parent_hash: Slicable::decode(input)?,
|
||||
number: Slicable::decode(input)?,
|
||||
state_root: Slicable::decode(input)?,
|
||||
extrinsics_root: Slicable::decode(input)?,
|
||||
digest: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
self.parent_hash.using_encoded(|s| v.extend(s));
|
||||
self.number.using_encoded(|s| v.extend(s));
|
||||
self.state_root.using_encoded(|s| v.extend(s));
|
||||
self.extrinsics_root.using_encoded(|s| v.extend(s));
|
||||
self.digest.using_encoded(|s| v.extend(s));
|
||||
v
|
||||
}
|
||||
}
|
||||
impl<Number, Hash, DigestItem> traits::Header for Header<Number, Hash, DigestItem> where
|
||||
Number: Member + Slicable,
|
||||
Hash: Member + Slicable,
|
||||
DigestItem: Member + Slicable,
|
||||
{
|
||||
type Number = Number;
|
||||
type Hash = Hash;
|
||||
type Digest = Digest<DigestItem>;
|
||||
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self {
|
||||
Header {
|
||||
number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Block<Number, Hash, DigestItem, AccountId, Index, Call, Signature> where
|
||||
Number: Member,
|
||||
Hash: Member,
|
||||
DigestItem: Member,
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
/// The block header.
|
||||
pub header: Header<Number, Hash, DigestItem>,
|
||||
/// The accompanying extrinsics.
|
||||
pub extrinsics: Vec<UncheckedExtrinsic<AccountId, Index, Call, Signature>>,
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem, AccountId, Index, Call, Signature> Slicable
|
||||
for Block<Number, Hash, DigestItem, AccountId, Index, Call, Signature>
|
||||
where
|
||||
Number: Member,
|
||||
Hash: Member,
|
||||
DigestItem: Member,
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member,
|
||||
Header<Number, Hash, DigestItem>: Slicable,
|
||||
UncheckedExtrinsic<AccountId, Index, Call, Signature>: Slicable,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Block {
|
||||
header: Slicable::decode(input)?,
|
||||
extrinsics: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
v.extend(self.header.encode());
|
||||
v.extend(self.extrinsics.encode());
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem, AccountId, Index, Call, Signature> traits::Block
|
||||
for Block<Number, Hash, DigestItem, AccountId, Index, Call, Signature>
|
||||
where
|
||||
Number: Member + Slicable,
|
||||
Hash: Member + Slicable,
|
||||
DigestItem: Member + Slicable,
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
type Extrinsic = UncheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
type Header = Header<Number, Hash, DigestItem>;
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.extrinsics[..]
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
|
||||
//! and depositing logs.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
extern crate num_traits;
|
||||
extern crate integer_sqrt;
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[cfg(feature = "std")] use std::collections::HashMap;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod testing;
|
||||
|
||||
pub mod traits;
|
||||
pub mod generic;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub type BuiltExternalities = HashMap<Vec<u8>, Vec<u8>>;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait BuildExternalities {
|
||||
fn build_externalities(self) -> BuiltExternalities;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! __impl_outer_config_types {
|
||||
($concrete:ident $config:ident $snake:ident $($rest:ident)*) => {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub type $config = $snake::GenesisConfig<$concrete>;
|
||||
__impl_outer_config_types! {$concrete $($rest)*}
|
||||
};
|
||||
($concrete:ident) => ()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Implement the output "meta" module configuration struct.
|
||||
macro_rules! impl_outer_config {
|
||||
( pub struct $main:ident for $concrete:ident { $( $config:ident => $snake:ident, )* } ) => {
|
||||
__impl_outer_config_types! { $concrete $( $config $snake )* }
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub struct $main {
|
||||
$(
|
||||
pub $snake: Option<$config>,
|
||||
)*
|
||||
}
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl $crate::BuildExternalities for $main {
|
||||
fn build_externalities(self) -> $crate::BuiltExternalities {
|
||||
let mut s = $crate::BuiltExternalities::new();
|
||||
$(
|
||||
if let Some(extra) = self.$snake {
|
||||
s.extend(extra.build_externalities());
|
||||
}
|
||||
)*
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Testing utilities.
|
||||
|
||||
use serde;
|
||||
use codec::{Slicable, Input};
|
||||
use runtime_support::AuxDispatchable;
|
||||
use substrate_primitives::H256;
|
||||
use traits::{self, Checkable, Applyable};
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
pub struct Digest {
|
||||
pub logs: Vec<u64>,
|
||||
}
|
||||
impl Slicable for Digest {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Vec::<u64>::decode(input).map(|logs| Digest { logs })
|
||||
}
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.logs.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl traits::Digest for Digest {
|
||||
type Item = u64;
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
self.logs.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Header {
|
||||
pub parent_hash: H256,
|
||||
pub number: u64,
|
||||
pub state_root: H256,
|
||||
pub extrinsics_root: H256,
|
||||
pub digest: Digest,
|
||||
}
|
||||
impl Slicable for Header {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
parent_hash: Slicable::decode(input)?,
|
||||
number: Slicable::decode(input)?,
|
||||
state_root: Slicable::decode(input)?,
|
||||
extrinsics_root: Slicable::decode(input)?,
|
||||
digest: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
self.parent_hash.using_encoded(|s| v.extend(s));
|
||||
self.number.using_encoded(|s| v.extend(s));
|
||||
self.state_root.using_encoded(|s| v.extend(s));
|
||||
self.extrinsics_root.using_encoded(|s| v.extend(s));
|
||||
self.digest.using_encoded(|s| v.extend(s));
|
||||
v
|
||||
}
|
||||
}
|
||||
impl traits::Header for Header {
|
||||
type Number = u64;
|
||||
type Hash = H256;
|
||||
type Digest = Digest;
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self {
|
||||
Header {
|
||||
number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
pub struct Block<Xt: Slicable + Sized + serde::Serialize> {
|
||||
pub header: Header,
|
||||
pub extrinsics: Vec<Xt>,
|
||||
}
|
||||
impl<Xt: Slicable + Sized + serde::Serialize> Slicable for Block<Xt> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Block {
|
||||
header: Slicable::decode(input)?,
|
||||
extrinsics: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
v.extend(self.header.encode());
|
||||
v.extend(self.extrinsics.encode());
|
||||
v
|
||||
}
|
||||
}
|
||||
impl<Xt: Slicable + Sized + serde::Serialize> traits::Block for Block<Xt> {
|
||||
type Extrinsic = Xt;
|
||||
type Header = Header;
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.extrinsics[..]
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
pub struct TestXt<Call: AuxDispatchable + Slicable + Sized + serde::Serialize>(pub (u64, u64, Call));
|
||||
impl<Call: AuxDispatchable + Slicable + Sized + serde::Serialize> Slicable for TestXt<Call> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(TestXt(Slicable::decode(input)?))
|
||||
}
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
self.0.encode()
|
||||
}
|
||||
}
|
||||
impl<Call: AuxDispatchable + Slicable + Sized + serde::Serialize> Checkable for TestXt<Call> {
|
||||
type Checked = Self;
|
||||
fn check(self) -> Result<Self, Self> { Ok(self) }
|
||||
}
|
||||
impl<Call: AuxDispatchable<Aux = u64> + Slicable + Sized + serde::Serialize> Applyable for TestXt<Call> {
|
||||
type AccountId = u64;
|
||||
type Index = u64;
|
||||
fn sender(&self) -> &u64 { &(self.0).0 }
|
||||
fn index(&self) -> &u64 { &(self.0).1 }
|
||||
fn apply(self) { (self.0).2.dispatch(&(self.0).0); }
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Primitives for the runtime modules.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd;
|
||||
#[cfg(not(feature = "std"))] use runtime_io;
|
||||
use substrate_primitives;
|
||||
use codec::{Input, Slicable};
|
||||
use substrate_primitives::hash::H512;
|
||||
pub use integer_sqrt::IntegerSquareRoot;
|
||||
pub use num_traits::{Zero, One, Bounded};
|
||||
use rstd::ops::{Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
|
||||
|
||||
/// Means of signature verification.
|
||||
pub trait Verify {
|
||||
/// Type of the signer.
|
||||
type Signer;
|
||||
/// Verify a signature.
|
||||
fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool;
|
||||
}
|
||||
|
||||
/// Ed25519 signature verify.
|
||||
#[derive(Eq, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
pub struct Ed25519Signature(H512);
|
||||
impl Verify for Ed25519Signature {
|
||||
type Signer = [u8; 32];
|
||||
fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool {
|
||||
::runtime_io::ed25519_verify(&(self.0).0, msg, &signer[..])
|
||||
}
|
||||
}
|
||||
impl Slicable for Ed25519Signature {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> { Some(Ed25519Signature(Slicable::decode(input)?,)) }
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { self.0.using_encoded(f) }
|
||||
}
|
||||
impl From<H512> for Ed25519Signature {
|
||||
fn from(h: H512) -> Ed25519Signature {
|
||||
Ed25519Signature(h)
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple payment making trait, operating on a single generic `AccountId` type.
|
||||
pub trait MakePayment<AccountId> {
|
||||
/// Make some sort of payment concerning `who`.
|
||||
fn make_payment(who: &AccountId);
|
||||
}
|
||||
|
||||
impl<T> MakePayment<T> for () {
|
||||
fn make_payment(_: &T) {}
|
||||
}
|
||||
|
||||
/// Extensible conversion trait. Generic over both source and destination types.
|
||||
pub trait Convert<A, B> {
|
||||
/// Make conversion.
|
||||
fn convert(a: A) -> B;
|
||||
}
|
||||
|
||||
/// Simple trait similar to `Into`, except that it can be used to convert numerics between each
|
||||
/// other.
|
||||
pub trait As<T> {
|
||||
/// Convert forward (ala `Into::into`).
|
||||
fn as_(self) -> T;
|
||||
/// Convert backward (ala `From::from`).
|
||||
fn sa(T) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_numerics {
|
||||
( $( $t:ty ),* ) => {
|
||||
$(
|
||||
impl_numerics!($t: u8, u16, u32, u64, usize, i8, i16, i32, i64, isize,);
|
||||
)*
|
||||
};
|
||||
( $f:ty : $t:ty, $( $rest:ty, )* ) => {
|
||||
impl As<$t> for $f {
|
||||
fn as_(self) -> $t { self as $t }
|
||||
fn sa(t: $t) -> Self { t as Self }
|
||||
}
|
||||
impl_numerics!($f: $( $rest, )*);
|
||||
};
|
||||
( $f:ty : ) => {}
|
||||
}
|
||||
|
||||
impl_numerics!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
|
||||
|
||||
pub struct Identity;
|
||||
impl<T> Convert<T, T> for Identity {
|
||||
fn convert(a: T) -> T { a }
|
||||
}
|
||||
|
||||
pub trait HasPublicAux {
|
||||
type PublicAux;
|
||||
}
|
||||
|
||||
pub trait RefInto<T> {
|
||||
fn ref_into(&self) -> &T;
|
||||
}
|
||||
impl<T> RefInto<T> for T {
|
||||
fn ref_into(&self) -> &T { &self }
|
||||
}
|
||||
|
||||
pub trait SimpleArithmetic:
|
||||
Zero + One + IntegerSquareRoot + As<usize> +
|
||||
Add<Self, Output = Self> + AddAssign<Self> +
|
||||
Sub<Self, Output = Self> + SubAssign<Self> +
|
||||
Mul<Self, Output = Self> + MulAssign<Self> +
|
||||
Div<Self, Output = Self> + DivAssign<Self> +
|
||||
Rem<Self, Output = Self> + RemAssign<Self> +
|
||||
PartialOrd<Self> + Ord
|
||||
{}
|
||||
impl<T:
|
||||
Zero + One + IntegerSquareRoot + As<usize> +
|
||||
Add<Self, Output = Self> + AddAssign<Self> +
|
||||
Sub<Self, Output = Self> + SubAssign<Self> +
|
||||
Mul<Self, Output = Self> + MulAssign<Self> +
|
||||
Div<Self, Output = Self> + DivAssign<Self> +
|
||||
Rem<Self, Output = Self> + RemAssign<Self> +
|
||||
PartialOrd<Self> + Ord
|
||||
> SimpleArithmetic for T {}
|
||||
|
||||
pub trait SimpleBitOps:
|
||||
Sized +
|
||||
rstd::ops::BitOr<Self, Output = Self> +
|
||||
rstd::ops::BitAnd<Self, Output = Self>
|
||||
{}
|
||||
impl<T:
|
||||
Sized +
|
||||
rstd::ops::BitOr<Self, Output = Self> +
|
||||
rstd::ops::BitAnd<Self, Output = Self>
|
||||
> SimpleBitOps for T {}
|
||||
|
||||
/// Something that can be executed.
|
||||
pub trait Executable {
|
||||
fn execute();
|
||||
}
|
||||
|
||||
impl Executable for () {
|
||||
fn execute() {}
|
||||
}
|
||||
impl<A: Executable, B: Executable> Executable for (A, B) {
|
||||
fn execute() {
|
||||
A::execute();
|
||||
B::execute();
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that acts like a `Digest` - it can have `Log`s `push`ed onto it and these `Log`s are
|
||||
/// each `Slicable`.
|
||||
pub trait Digest {
|
||||
type Item: Sized;
|
||||
fn push(&mut self, item: Self::Item);
|
||||
}
|
||||
|
||||
impl Digest for substrate_primitives::Digest {
|
||||
type Item = substrate_primitives::block::Log;
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
self.logs.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// Something which fulfills the abstract idea of a Substrate header. It has types for a `Number`,
|
||||
/// a `Hash` and a `Digest`. It provides access to an `extrinsics_root`, `state_root` and
|
||||
/// `parent_hash`, as well as a `digest` and a block `number`.
|
||||
///
|
||||
/// You can also create a `new` one from those fields.
|
||||
pub trait Header: Sized + Slicable {
|
||||
type Number: Sized;
|
||||
type Hash: Sized;
|
||||
type Digest: Sized;
|
||||
fn number(&self) -> &Self::Number;
|
||||
fn extrinsics_root(&self) -> &Self::Hash;
|
||||
fn state_root(&self) -> &Self::Hash;
|
||||
fn parent_hash(&self) -> &Self::Hash;
|
||||
fn digest(&self) -> &Self::Digest;
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl Header for substrate_primitives::Header {
|
||||
type Number = substrate_primitives::block::Number;
|
||||
type Hash = substrate_primitives::block::HeaderHash;
|
||||
type Digest = substrate_primitives::block::Digest;
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.transaction_root }
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self {
|
||||
substrate_primitives::Header {
|
||||
number: number,
|
||||
transaction_root: extrinsics_root,
|
||||
state_root: state_root,
|
||||
parent_hash: parent_hash,
|
||||
digest: digest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Something which fulfills the abstract idea of a Substrate block. It has types for an
|
||||
/// `Extrinsic` piece of information as well as a `Header`.
|
||||
///
|
||||
/// You can get an iterator over each of the `extrinsics` and retrieve the `header`.
|
||||
pub trait Block {
|
||||
type Extrinsic: Sized;
|
||||
type Header: Header;
|
||||
fn header(&self) -> &Self::Header;
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic];
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>);
|
||||
}
|
||||
|
||||
impl Block for substrate_primitives::Block {
|
||||
type Extrinsic = substrate_primitives::block::Transaction;
|
||||
type Header = substrate_primitives::Header;
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.transactions[..]
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.transactions)
|
||||
}
|
||||
}
|
||||
|
||||
/// A "checkable" piece of information, used by the standard Substrate Executive in order to
|
||||
/// check the validity of a piece of extrinsic information, usually by verifying the signature.
|
||||
pub trait Checkable: Sized {
|
||||
type Checked: Sized;
|
||||
fn check(self) -> Result<Self::Checked, Self>;
|
||||
}
|
||||
|
||||
/// An "executable" piece of information, used by the standard Substrate Executive in order to
|
||||
/// enact a piece of extrinsic information by marshalling and dispatching to a named functioon
|
||||
/// call.
|
||||
///
|
||||
/// Also provides information on to whom this information is attributable and an index that allows
|
||||
/// each piece of attributable information to be disambiguated.
|
||||
pub trait Applyable {
|
||||
type AccountId;
|
||||
type Index;
|
||||
fn index(&self) -> &Self::Index;
|
||||
fn sender(&self) -> &Self::AccountId;
|
||||
fn apply(self);
|
||||
}
|
||||
|
||||
/// Something that can be checked for equality and printed out to a debug channel if bad.
|
||||
pub trait CheckEqual {
|
||||
fn check_equal(&self, other: &Self);
|
||||
}
|
||||
|
||||
impl CheckEqual for substrate_primitives::H256 {
|
||||
#[cfg(feature = "std")]
|
||||
fn check_equal(&self, other: &Self) {
|
||||
use substrate_primitives::hexdisplay::HexDisplay;
|
||||
if &self.0 != &other.0 {
|
||||
println!("Hash: given={}, expected={}", HexDisplay::from(&self.0), HexDisplay::from(&other.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn check_equal(&self, other: &Self) {
|
||||
if self != other {
|
||||
runtime_io::print("Hash not equal");
|
||||
runtime_io::print(&self.0[..]);
|
||||
runtime_io::print(&other.0[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "substrate-runtime-session"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-consensus = { path = "../consensus", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
@@ -0,0 +1,345 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Session manager: is told the validators and allows them to manage their session keys for the
|
||||
//! consensus module.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate substrate_keyring as keyring;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[cfg_attr(feature = "std", macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_system as system;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{Zero, One, RefInto, Executable, Convert};
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
|
||||
pub trait Trait: consensus::Trait + system::Trait {
|
||||
type PublicAux: RefInto<Self::AccountId>;
|
||||
type ConvertAccountIdToSessionKey: Convert<Self::AccountId, Self::SessionKey>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set_key(aux, key: T::SessionKey) = 0;
|
||||
}
|
||||
pub enum PrivCall {
|
||||
fn set_length(new: T::BlockNumber) = 0;
|
||||
fn force_new_session() = 1;
|
||||
}
|
||||
}
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait>;
|
||||
|
||||
// The current set of validators.
|
||||
pub Validators get(validators): b"ses:val" => required Vec<T::AccountId>;
|
||||
// Current length of the session.
|
||||
pub SessionLength get(length): b"ses:len" => required T::BlockNumber;
|
||||
// Current index of the session.
|
||||
pub CurrentIndex get(current_index): b"ses:ind" => required T::BlockNumber;
|
||||
|
||||
// Block at which the session length last changed.
|
||||
LastLengthChange: b"ses:llc" => T::BlockNumber;
|
||||
// The next key for a given validator.
|
||||
NextKeyFor: b"ses:nxt:" => map [ T::AccountId => T::SessionKey ];
|
||||
// The next session length.
|
||||
NextSessionLength: b"ses:nln" => T::BlockNumber;
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// The number of validators currently.
|
||||
pub fn validator_count() -> u32 {
|
||||
<Validators<T>>::get().len() as u32 // TODO: can probably optimised
|
||||
}
|
||||
|
||||
/// The last length change, if there was one, zero if not.
|
||||
pub fn last_length_change() -> T::BlockNumber {
|
||||
<LastLengthChange<T>>::get().unwrap_or_else(T::BlockNumber::zero)
|
||||
}
|
||||
|
||||
/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
|
||||
/// session.
|
||||
fn set_key(aux: &T::PublicAux, key: T::SessionKey) {
|
||||
// set new value for next session
|
||||
<NextKeyFor<T>>::insert(aux.ref_into(), key)
|
||||
}
|
||||
|
||||
/// Set a new era length. Won't kick in until the next era change (at current length).
|
||||
fn set_length(new: T::BlockNumber) {
|
||||
<NextSessionLength<T>>::put(new);
|
||||
}
|
||||
|
||||
/// Forces a new session.
|
||||
fn force_new_session() {
|
||||
Self::rotate_session();
|
||||
}
|
||||
|
||||
// INTERNAL API (available to other runtime modules)
|
||||
|
||||
/// Set the current set of validators.
|
||||
///
|
||||
/// Called by `staking::next_era()` only. `next_session` should be called after this in order to
|
||||
/// update the session keys to the next validator set.
|
||||
pub fn set_validators(new: &[T::AccountId]) {
|
||||
<Validators<T>>::put(&new.to_vec()); // TODO: optimise.
|
||||
<consensus::Module<T>>::set_authorities(
|
||||
&new.iter().cloned().map(T::ConvertAccountIdToSessionKey::convert).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
/// Hook to be called after transaction processing.
|
||||
pub fn check_rotate_session() {
|
||||
// do this last, after the staking system has had chance to switch out the authorities for the
|
||||
// new set.
|
||||
// check block number and call next_session if necessary.
|
||||
let block_number = <system::Module<T>>::block_number();
|
||||
if ((block_number - Self::last_length_change()) % Self::length()).is_zero() {
|
||||
Self::rotate_session();
|
||||
}
|
||||
}
|
||||
|
||||
/// Move onto next session: register the new authority set.
|
||||
pub fn rotate_session() {
|
||||
// Increment current session index.
|
||||
<CurrentIndex<T>>::put(<CurrentIndex<T>>::get() + One::one());
|
||||
|
||||
// Enact era length change.
|
||||
if let Some(next_len) = <NextSessionLength<T>>::take() {
|
||||
let block_number = <system::Module<T>>::block_number();
|
||||
<SessionLength<T>>::put(next_len);
|
||||
<LastLengthChange<T>>::put(block_number);
|
||||
}
|
||||
|
||||
// Update any changes in session keys.
|
||||
Self::validators().iter().enumerate().for_each(|(i, v)| {
|
||||
if let Some(n) = <NextKeyFor<T>>::take(v) {
|
||||
<consensus::Module<T>>::set_authority(i as u32, &n);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Executable for Module<T> {
|
||||
fn execute() {
|
||||
Self::check_rotate_session();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub struct GenesisConfig<T: Trait> {
|
||||
pub session_length: T::BlockNumber,
|
||||
pub validators: Vec<T::AccountId>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> GenesisConfig<T> where T::AccountId: From<keyring::Keyring> {
|
||||
pub fn simple() -> Self where T::AccountId: From<[u8; 32]> {
|
||||
use primitives::traits::As;
|
||||
use keyring::Keyring::*;
|
||||
let three = [3u8; 32];
|
||||
GenesisConfig {
|
||||
session_length: T::BlockNumber::sa(2),
|
||||
validators: vec![T::AccountId::from(One), T::AccountId::from(Two), T::AccountId::from(three)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extended() -> Self {
|
||||
use primitives::traits::As;
|
||||
use keyring::Keyring::*;
|
||||
GenesisConfig {
|
||||
session_length: T::BlockNumber::sa(1),
|
||||
validators: vec![T::AccountId::from(Alice), T::AccountId::from(Bob), T::AccountId::from(Charlie)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
use primitives::traits::As;
|
||||
GenesisConfig {
|
||||
session_length: T::BlockNumber::sa(1000),
|
||||
validators: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
|
||||
{
|
||||
fn build_externalities(self) -> runtime_io::TestExternalities {
|
||||
use runtime_io::twox_128;
|
||||
use codec::Slicable;
|
||||
use primitives::traits::As;
|
||||
map![
|
||||
twox_128(<SessionLength<T>>::key()).to_vec() => self.session_length.encode(),
|
||||
twox_128(<CurrentIndex<T>>::key()).to_vec() => T::BlockNumber::sa(0).encode(),
|
||||
twox_128(<Validators<T>>::key()).to_vec() => self.validators.encode()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::H256;
|
||||
use primitives::BuildExternalities;
|
||||
use primitives::traits::{HasPublicAux, Identity};
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
pub struct Test;
|
||||
impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type SessionKey = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
}
|
||||
impl Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
}
|
||||
|
||||
type System = system::Module<Test>;
|
||||
type Consensus = consensus::Module<Test>;
|
||||
type Session = Module<Test>;
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
|
||||
t.extend(consensus::GenesisConfig::<Test>{
|
||||
authorities: vec![1, 2, 3],
|
||||
}.build_externalities());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
session_length: 2,
|
||||
validators: vec![1, 2, 3],
|
||||
}.build_externalities());
|
||||
t
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_setup_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(Consensus::authorities(), vec![1, 2, 3]);
|
||||
assert_eq!(Session::length(), 2);
|
||||
assert_eq!(Session::validators(), vec![1, 2, 3]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_length_change_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
// Block 1: Change to length 3; no visible change.
|
||||
System::set_block_number(1);
|
||||
Session::set_length(3);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::length(), 2);
|
||||
assert_eq!(Session::current_index(), 0);
|
||||
|
||||
// Block 2: Length now changed to 3. Index incremented.
|
||||
System::set_block_number(2);
|
||||
Session::set_length(3);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::length(), 3);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
|
||||
// Block 3: Length now changed to 3. Index incremented.
|
||||
System::set_block_number(3);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::length(), 3);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
|
||||
// Block 4: Change to length 2; no visible change.
|
||||
System::set_block_number(4);
|
||||
Session::set_length(2);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::length(), 3);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
|
||||
// Block 5: Length now changed to 2. Index incremented.
|
||||
System::set_block_number(5);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::length(), 2);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
|
||||
// Block 6: No change.
|
||||
System::set_block_number(6);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::length(), 2);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
|
||||
// Block 7: Next index.
|
||||
System::set_block_number(7);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::length(), 2);
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_change_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
// Block 1: No change
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Consensus::authorities(), vec![1, 2, 3]);
|
||||
|
||||
// Block 2: Session rollover, but no change.
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Consensus::authorities(), vec![1, 2, 3]);
|
||||
|
||||
// Block 3: Set new key for validator 2; no visible change.
|
||||
System::set_block_number(3);
|
||||
Session::set_key(&2, 5);
|
||||
assert_eq!(Consensus::authorities(), vec![1, 2, 3]);
|
||||
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Consensus::authorities(), vec![1, 2, 3]);
|
||||
|
||||
// Block 4: Session rollover, authority 2 changes.
|
||||
System::set_block_number(4);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Consensus::authorities(), vec![1, 5, 3]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "substrate-runtime-staking"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-consensus = { path = "../consensus", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
substrate-runtime-session = { path = "../session", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "substrate-runtime-system"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"safe-mix/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
]
|
||||
@@ -0,0 +1,178 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! System manager: Handles lowest level stuff like depositing logs, basic set up and take down of
|
||||
//! temporary storage entries, access to old block hashes.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(any(feature = "std", test), macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate safe_mix;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use runtime_io::Hashing;
|
||||
use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use safe_mix::TripletMix;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use rstd::marker::PhantomData;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use codec::Slicable;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
|
||||
pub trait Trait {
|
||||
type Index: Parameter + Default + SimpleArithmetic + Copy;
|
||||
type BlockNumber: Parameter + SimpleArithmetic + Default + Bounded + Copy;
|
||||
type Hash: Parameter + SimpleBitOps + Default + Copy + CheckEqual;
|
||||
type Hashing: Hashing<Output = Self::Hash>;
|
||||
type Digest: Parameter + Default + traits::Digest;
|
||||
type AccountId: Parameter + Ord + Default;
|
||||
type Header: traits::Header<Number = Self::BlockNumber, Hash = Self::Hash, Digest = Self::Digest>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait>;
|
||||
|
||||
pub AccountIndex get(account_index): b"sys:non" => default map [ T::AccountId => T::Index ];
|
||||
pub BlockHash get(block_hash): b"sys:old" => required map [ T::BlockNumber => T::Hash ];
|
||||
|
||||
RandomSeed get(random_seed): b"sys:rnd" => required T::Hash;
|
||||
// The current block number being processed. Set by `execute_block`.
|
||||
Number get(block_number): b"sys:num" => required T::BlockNumber;
|
||||
ParentHash get(parent_hash): b"sys:pha" => required T::Hash;
|
||||
ExtrinsicsRoot get(extrinsics_root): b"sys:txr" => required T::Hash;
|
||||
Digest get(digest): b"sys:dig" => default T::Digest;
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Start the execution of a particular block.
|
||||
pub fn initialise(number: &T::BlockNumber, parent_hash: &T::Hash, txs_root: &T::Hash) {
|
||||
// populate environment.
|
||||
<Number<T>>::put(number);
|
||||
<ParentHash<T>>::put(parent_hash);
|
||||
<ExtrinsicsRoot<T>>::put(txs_root);
|
||||
<RandomSeed<T>>::put(Self::calculate_random());
|
||||
}
|
||||
|
||||
/// Remove temporary "environment" entries in storage.
|
||||
pub fn finalise() -> T::Header {
|
||||
<RandomSeed<T>>::kill();
|
||||
|
||||
let number = <Number<T>>::take();
|
||||
let parent_hash = <ParentHash<T>>::take();
|
||||
let digest = <Digest<T>>::take();
|
||||
let extrinsics_root = <ExtrinsicsRoot<T>>::take();
|
||||
let storage_root = T::Hashing::storage_root();
|
||||
<T::Header as traits::Header>::new(number, extrinsics_root, storage_root, parent_hash, digest)
|
||||
}
|
||||
|
||||
/// Deposits a log and ensures it matches the blocks log data.
|
||||
pub fn deposit_log(item: <T::Digest as traits::Digest>::Item) {
|
||||
let mut l = <Digest<T>>::get();
|
||||
traits::Digest::push(&mut l, item);
|
||||
<Digest<T>>::put(l);
|
||||
}
|
||||
|
||||
/// Records a particular block number and hash combination.
|
||||
pub fn record_block_hash<H: traits::Header<Number = T::BlockNumber>>(header: &H) {
|
||||
// store the header hash in storage; we can't do it before otherwise there would be a
|
||||
// cyclic dependency.
|
||||
<BlockHash<T>>::insert(header.number(), &T::Hashing::hash_of(header));
|
||||
}
|
||||
|
||||
/// Initializes the state following the determination of the genesis block.
|
||||
pub fn initialise_genesis_state<H: traits::Header<Number = T::BlockNumber>>(header: &H) {
|
||||
Self::record_block_hash(header);
|
||||
}
|
||||
|
||||
/// Calculate the current block's random seed.
|
||||
fn calculate_random() -> T::Hash {
|
||||
(0..81)
|
||||
.scan(
|
||||
{ let mut n = Self::block_number().clone(); n -= T::BlockNumber::one(); n },
|
||||
|c, _| { if *c > T::BlockNumber::zero() { *c -= T::BlockNumber::one() }; Some(c.clone())
|
||||
})
|
||||
.map(Self::block_hash)
|
||||
.triplet_mix()
|
||||
}
|
||||
|
||||
/// Get the basic externalities for this module, useful for tests.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub fn externalities() -> TestExternalities {
|
||||
map![
|
||||
twox_128(&<BlockHash<T>>::key_for(T::BlockNumber::zero())).to_vec() => [69u8; 32].encode(), // TODO: replace with Hash::default().encode
|
||||
twox_128(<Number<T>>::key()).to_vec() => T::BlockNumber::one().encode(),
|
||||
twox_128(<ParentHash<T>>::key()).to_vec() => [69u8; 32].encode(), // TODO: replace with Hash::default().encode
|
||||
twox_128(<RandomSeed<T>>::key()).to_vec() => T::Hash::default().encode()
|
||||
]
|
||||
}
|
||||
|
||||
/// Set the block number to something in particular. Can be used as an alternative to
|
||||
/// `initialise` for tests that don't need to bother with the other environment entries.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub fn set_block_number(n: T::BlockNumber) {
|
||||
<Number<T>>::put(n);
|
||||
}
|
||||
|
||||
/// Increment a particular account's nonce by 1.
|
||||
pub fn inc_account_index(who: &T::AccountId) {
|
||||
<AccountIndex<T>>::insert(who, Self::account_index(who) + T::Index::one());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub struct GenesisConfig<T: Trait>(PhantomData<T>);
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
|
||||
{
|
||||
fn build_externalities(self) -> runtime_io::TestExternalities {
|
||||
use runtime_io::twox_128;
|
||||
use codec::Slicable;
|
||||
|
||||
map![
|
||||
twox_128(&<BlockHash<T>>::key_for(T::BlockNumber::zero())).to_vec() => [69u8; 32].encode(),
|
||||
twox_128(<Number<T>>::key()).to_vec() => 1u64.encode(),
|
||||
twox_128(<ParentHash<T>>::key()).to_vec() => [69u8; 32].encode(),
|
||||
twox_128(<RandomSeed<T>>::key()).to_vec() => [0u8; 32].encode()
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "substrate-runtime-timestamp"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"serde/std",
|
||||
"substrate-codec/std",
|
||||
]
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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 Demo 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 Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Timestamp manager: just handles the current timestamp.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate substrate_codec as codec;
|
||||
|
||||
use runtime_support::{StorageValue, Parameter};
|
||||
use runtime_primitives::traits::HasPublicAux;
|
||||
|
||||
pub trait Trait: HasPublicAux {
|
||||
type Value: Parameter + Default;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set(aux, now: T::Value) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
pub trait Store for Module<T: Trait>;
|
||||
pub Now get(now): b"tim:val" => required T::Value;
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
pub fn get() -> T::Value {
|
||||
<Self as Store>::Now::get()
|
||||
}
|
||||
|
||||
/// Set the current time.
|
||||
fn set(_aux: &T::PublicAux, now: T::Value) {
|
||||
<Self as Store>::Now::put(now);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::Joiner;
|
||||
use runtime_support::storage::StorageValue;
|
||||
|
||||
struct TraitImpl;
|
||||
impl HasPublicAux for TraitImpl {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl Trait for TraitImpl {
|
||||
type Value = u64;
|
||||
}
|
||||
type Timestamp = Module<TraitImpl>;
|
||||
|
||||
#[test]
|
||||
fn timestamp_works() {
|
||||
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(<Timestamp as Store>::Now::key()).to_vec() => vec![].and(&42u64)
|
||||
];
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(<Timestamp as Store>::Now::get(), 42);
|
||||
Timestamp::aux_dispatch(Call::set(69), &0);
|
||||
assert_eq!(Timestamp::now(), 69);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ impl Slicable for Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for Transaction {}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@@ -60,7 +60,7 @@ impl Slicable for UncheckedTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for UncheckedTransaction {}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
+2
-1
@@ -621,8 +621,9 @@ name = "substrate-runtime-support"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"environmental 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
|
||||
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Reference in New Issue
Block a user