diff --git a/Cargo.lock b/Cargo.lock index 763bbbf3db65a..99556b9af5550 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1233,6 +1233,7 @@ dependencies = [ "substrate-primitives 0.1.0", "substrate-runtime-executive 0.1.0", "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", "substrate-state-machine 0.1.0", ] @@ -1430,12 +1431,14 @@ dependencies = [ "ed25519 0.1.0", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-api 0.1.0", "polkadot-primitives 0.1.0", "polkadot-runtime 0.1.0", "substrate-client 0.1.0", "substrate-codec 0.1.0", "substrate-extrinsic-pool 0.1.0", + "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", "substrate-runtime-primitives 0.1.0", ] @@ -1559,7 +1562,7 @@ dependencies = [ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1922,6 +1925,9 @@ dependencies = [ [[package]] name = "substrate-codec" version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "substrate-executor" @@ -2141,6 +2147,7 @@ version = "0.1.0" dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (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", @@ -2267,6 +2274,7 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 0.1.0", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (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", diff --git a/demo/cli/src/lib.rs b/demo/cli/src/lib.rs index e80caa1e95d0e..9a08eeffcc631 100644 --- a/demo/cli/src/lib.rs +++ b/demo/cli/src/lib.rs @@ -122,6 +122,11 @@ pub fn run(args: I) -> error::Result<()> where intentions: vec![], transaction_base_fee: 100, transaction_byte_fee: 1, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, + existential_deposit: 500, balances: vec![(god_key.clone().into(), 1u64 << 63)].into_iter().collect(), validator_count: 12, sessions_per_era: 24, // 24 hours per era. diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index 009e5f2de0676..f8c74f3c048c0 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -46,11 +46,12 @@ mod tests { use runtime_support::{Hashable, StorageValue, StorageMap}; use state_machine::{CodeExecutor, TestExternalities}; use primitives::twox_128; - use demo_primitives::{Hash, BlockNumber}; + use demo_primitives::{Hash, BlockNumber, AccountId}; use runtime_primitives::traits::Header as HeaderT; + use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult, MaybeUnsigned}; use {staking, system}; use demo_runtime::{Header, Block, UncheckedExtrinsic, Extrinsic, Call, Concrete, Staking, - BuildExternalities, GenesisConfig, SessionConfig, StakingConfig}; + BuildExternalities, GenesisConfig, SessionConfig, StakingConfig, BareExtrinsic}; use ed25519::{Public, Pair}; const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm"); @@ -63,24 +64,28 @@ mod tests { ) } - fn alice() -> Hash { - Keyring::Alice.to_raw_public().into() + fn alice() -> AccountId { + AccountId::from(Keyring::Alice.to_raw_public()) } - fn bob() -> Hash { - Keyring::Bob.to_raw_public().into() + fn bob() -> AccountId { + AccountId::from(Keyring::Bob.to_raw_public()) } fn xt() -> UncheckedExtrinsic { - let extrinsic = Extrinsic { + let extrinsic = BareExtrinsic { signed: alice(), index: 0, - function: Call::Staking(staking::Call::transfer::(bob(), 69)), + function: Call::Staking(staking::Call::transfer::(bob().into(), 69)), }; - let signature = Keyring::from_raw_public(extrinsic.signed.0).unwrap() - .sign(&extrinsic.encode()).into(); - - UncheckedExtrinsic { extrinsic, signature } + let signature = MaybeUnsigned(Keyring::from_raw_public(extrinsic.signed.0.clone()).unwrap() + .sign(&extrinsic.encode()).into()); + let extrinsic = Extrinsic { + signed: extrinsic.signed.into(), + index: extrinsic.index, + function: extrinsic.function, + }; + UncheckedExtrinsic::new(extrinsic, signature) } fn from_block_number(n: u64) -> Header { @@ -93,28 +98,36 @@ mod tests { twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![70u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; 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, "apply_extrinsic", &vec![].and(&xt())); - assert!(r.is_err()); + let v = Executor::new().call(&mut t, BLOATY_CODE, "apply_extrinsic", &vec![].and(&xt())).unwrap(); + let r = ApplyResult::decode(&mut &v[..]).unwrap(); + assert_eq!(r, Err(ApplyError::CantPay)); } #[test] - fn panic_execution_with_native_equivalent_code_gives_error() { + fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t: TestExternalities = map![ twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![70u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; 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, "apply_extrinsic", &vec![].and(&xt())); - assert!(r.is_err()); + let v = Executor::new().call(&mut t, COMPACT_CODE, "apply_extrinsic", &vec![].and(&xt())).unwrap(); + let r = ApplyResult::decode(&mut &v[..]).unwrap(); + assert_eq!(r, Err(ApplyError::CantPay)); } #[test] @@ -123,6 +136,9 @@ mod tests { twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -132,8 +148,8 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::balance(&alice()), 42); - assert_eq!(Staking::balance(&bob()), 69); + assert_eq!(Staking::voting_balance(&alice()), 42); + assert_eq!(Staking::voting_balance(&bob()), 69); }); } @@ -143,6 +159,9 @@ mod tests { twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -152,8 +171,8 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::balance(&alice()), 42); - assert_eq!(Staking::balance(&bob()), 69); + assert_eq!(Staking::voting_balance(&alice()), 42); + assert_eq!(Staking::voting_balance(&bob()), 69); }); } @@ -176,20 +195,29 @@ mod tests { bonding_duration: 0, transaction_base_fee: 1, transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, }), democracy: Some(Default::default()), council: Some(Default::default()), }.build_externalities() } - fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec) -> (Vec, Hash) { + fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec) -> (Vec, Hash) { use triehash::ordered_trie_root; let extrinsics = extrinsics.into_iter().map(|extrinsic| { - let signature = Pair::from(Keyring::from_public(Public::from_raw(extrinsic.signed.0)).unwrap()) - .sign(&extrinsic.encode()).into(); - - UncheckedExtrinsic { extrinsic, signature } + let signature = MaybeUnsigned(Pair::from(Keyring::from_public(Public::from_raw(extrinsic.signed.0.clone())).unwrap()) + .sign(&extrinsic.encode()).into()); + let extrinsic = Extrinsic { + signed: extrinsic.signed.into(), + index: extrinsic.index, + function: extrinsic.function, + }; + UncheckedExtrinsic::new(extrinsic, signature) }).collect::>(); let extrinsics_root = ordered_trie_root(extrinsics.iter().map(Slicable::encode)).0.into(); @@ -210,11 +238,11 @@ mod tests { construct_block( 1, [69u8; 32].into(), - hex!("76b0393b4958d3cb98bb51d9f4edb316af48485142b8721e94c3b52c75ec3243").into(), - vec![Extrinsic { + hex!("4f7a61bceecddc19d49fbee53f82402c2a8727c1b2aeb5e5070a59f0777a203b").into(), + vec![BareExtrinsic { signed: alice(), index: 0, - function: Call::Staking(staking::Call::transfer(bob(), 69)), + function: Call::Staking(staking::Call::transfer(bob().into(), 69)), }] ) } @@ -223,17 +251,17 @@ mod tests { construct_block( 2, block1().1, - hex!("8ae9828a5988459d35fb428086170dead660176ee0766e89bc1a4b48153d4e88").into(), + hex!("67c588603dd727601263cf8d6138a2003ffc0df793c5ea34e7defc945da24bf0").into(), vec![ - Extrinsic { + BareExtrinsic { signed: bob(), index: 0, - function: Call::Staking(staking::Call::transfer(alice(), 5)), + function: Call::Staking(staking::Call::transfer(alice().into(), 5)), }, - Extrinsic { + BareExtrinsic { signed: alice(), index: 1, - function: Call::Staking(staking::Call::transfer(bob(), 15)), + function: Call::Staking(staking::Call::transfer(bob().into(), 15)), } ] ) @@ -246,15 +274,15 @@ mod tests { Executor::new().call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::balance(&alice()), 41); - assert_eq!(Staking::balance(&bob()), 69); + assert_eq!(Staking::voting_balance(&alice()), 41); + assert_eq!(Staking::voting_balance(&bob()), 69); }); Executor::new().call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::balance(&alice()), 30); - assert_eq!(Staking::balance(&bob()), 78); + assert_eq!(Staking::voting_balance(&alice()), 30); + assert_eq!(Staking::voting_balance(&bob()), 78); }); } @@ -265,15 +293,15 @@ mod tests { WasmExecutor.call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::balance(&alice()), 41); - assert_eq!(Staking::balance(&bob()), 69); + assert_eq!(Staking::voting_balance(&alice()), 41); + assert_eq!(Staking::voting_balance(&bob()), 69); }); WasmExecutor.call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::balance(&alice()), 30); - assert_eq!(Staking::balance(&bob()), 78); + assert_eq!(Staking::voting_balance(&alice()), 30); + assert_eq!(Staking::voting_balance(&bob()), 78); }); } @@ -283,14 +311,18 @@ mod tests { twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![70u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::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(&from_block_number(1u64))); assert!(r.is_ok()); - let r = WasmExecutor.call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())); - assert!(r.is_err()); + let r = WasmExecutor.call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap(); + let r = ApplyResult::decode(&mut &r[..]).unwrap(); + assert_eq!(r, Err(ApplyError::CantPay)); } #[test] @@ -299,18 +331,22 @@ mod tests { twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::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(&from_block_number(1u64))); assert!(r.is_ok()); - let r = WasmExecutor.call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())); - assert!(r.is_ok()); + let r = WasmExecutor.call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap(); + let r = ApplyResult::decode(&mut &r[..]).unwrap(); + assert_eq!(r, Ok(ApplyOutcome::Success)); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::balance(&alice()), 42); - assert_eq!(Staking::balance(&bob()), 69); + assert_eq!(Staking::voting_balance(&alice()), 42); + assert_eq!(Staking::voting_balance(&bob()), 69); }); } } diff --git a/demo/primitives/src/lib.rs b/demo/primitives/src/lib.rs index d9b4f66e58e4b..a435edd3696e2 100644 --- a/demo/primitives/src/lib.rs +++ b/demo/primitives/src/lib.rs @@ -35,6 +35,10 @@ pub type BlockNumber = u64; /// certainly continue to be the same as the substrate's `AuthorityId`. pub type AccountId = ::primitives::H256; +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + /// Balance of an account. pub type Balance = u64; @@ -49,4 +53,4 @@ pub type Index = u64; pub type Hash = primitives::H256; /// Alias to 512-bit hash when used in the context of a signature on the relay chain. -pub type Signature = runtime_primitives::Ed25519Signature; +pub type Signature = runtime_primitives::MaybeUnsigned; diff --git a/demo/runtime/src/lib.rs b/demo/runtime/src/lib.rs index 83503885a6737..90fcce51bf39f 100644 --- a/demo/runtime/src/lib.rs +++ b/demo/runtime/src/lib.rs @@ -46,13 +46,16 @@ extern crate substrate_runtime_timestamp as timestamp; extern crate demo_primitives; use rstd::prelude::*; -use demo_primitives::{AccountId, Balance, BlockNumber, Hash, Index, SessionKey, Signature}; +use demo_primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, SessionKey, Signature}; use runtime_primitives::generic; use runtime_primitives::traits::{Convert, HasPublicAux, BlakeTwo256}; #[cfg(any(feature = "std", test))] pub use runtime_primitives::BuildExternalities; +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] /// Concrete runtime type used to parameterize the various modules. pub struct Concrete; @@ -108,6 +111,7 @@ pub type Session = session::Module; impl staking::Trait for Concrete { type Balance = Balance; type DetermineContractAddress = BlakeTwo256; + type AccountIndex = AccountIndex; } /// Staking module for this concrete runtime. @@ -152,16 +156,20 @@ impl_outer_dispatch! { } } +/// The address format for describing accounts. +pub type Address = staking::Address; /// Block header type as expected by this runtime. pub type Header = generic::Header>; /// Block type as expected by this runtime. pub type Block = generic::Block; /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -/// Extrinsic type as expected by this runtime. -pub type Extrinsic = generic::Extrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type as expected by this runtime. This is not the type that is signed. +pub type Extrinsic = generic::Extrinsic; +/// Extrinsic type that is signed. +pub type BareExtrinsic = generic::Extrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; impl_outer_config! { diff --git a/demo/runtime/wasm/Cargo.lock b/demo/runtime/wasm/Cargo.lock index e5f973ab5307d..8532d96db9946 100644 --- a/demo/runtime/wasm/Cargo.lock +++ b/demo/runtime/wasm/Cargo.lock @@ -543,6 +543,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "substrate-codec" version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "substrate-keyring" @@ -647,6 +650,7 @@ version = "0.1.0" dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (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", @@ -767,6 +771,7 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 0.1.0", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (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", diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm index fbbd57cbdeca2..e08fcfe565c51 100644 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm differ diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm index 595bb32153d9d..3d4eb2e456ee8 100755 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm differ diff --git a/polkadot/api/Cargo.toml b/polkadot/api/Cargo.toml index 860536ac95913..5a883cfd2cdff 100644 --- a/polkadot/api/Cargo.toml +++ b/polkadot/api/Cargo.toml @@ -11,6 +11,7 @@ polkadot-primitives = { path = "../primitives" } substrate-codec = { path = "../../substrate/codec" } substrate-runtime-io = { path = "../../substrate/runtime-io" } substrate-runtime-executive = { path = "../../substrate/runtime/executive" } +substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } substrate-client = { path = "../../substrate/client" } substrate-primitives = { path = "../../substrate/primitives" } substrate-executor = { path = "../../substrate/executor" } diff --git a/polkadot/api/src/full.rs b/polkadot/api/src/full.rs index 101f59f5a70a1..a4203eac751ee 100644 --- a/polkadot/api/src/full.rs +++ b/polkadot/api/src/full.rs @@ -23,6 +23,8 @@ use polkadot_executor::Executor as LocalDispatch; use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; use state_machine; +use runtime::Address; +use runtime_primitives::traits::AuxLookup; use primitives::{AccountId, Block, Header, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; @@ -135,7 +137,11 @@ impl> PolkadotApi for Client Result { - with_runtime!(self, at, || ::runtime::System::account_index(account)) + with_runtime!(self, at, || ::runtime::System::account_nonce(account)) + } + + fn lookup(&self, at: &Self::CheckedBlockId, address: Address) -> Result> { + with_runtime!(self, at, || <::runtime::Staking as AuxLookup>::lookup(address).ok()) } fn active_parachains(&self, at: &CheckedId) -> Result> { diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index c92f568f7b59c..81e3b024201e3 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -26,6 +26,7 @@ extern crate substrate_client as client; extern crate substrate_executor as substrate_executor; extern crate substrate_runtime_executive; extern crate substrate_primitives; +extern crate substrate_runtime_primitives as runtime_primitives; extern crate substrate_state_machine as state_machine; #[macro_use] @@ -37,7 +38,9 @@ extern crate substrate_keyring as keyring; pub mod full; pub mod light; -use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; +use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, + UncheckedExtrinsic}; +use runtime::Address; use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; error_chain! { @@ -119,9 +122,12 @@ pub trait PolkadotApi { /// Get the timestamp registered at a block. fn timestamp(&self, at: &Self::CheckedBlockId) -> Result; - /// Get the index of an account at a block. + /// Get the nonce (né index) of an account at a block. fn index(&self, at: &Self::CheckedBlockId, account: AccountId) -> Result; + /// Get the account id of an address at a block. + fn lookup(&self, at: &Self::CheckedBlockId, address: Address) -> Result>; + /// Get the active parachains at a block. fn active_parachains(&self, at: &Self::CheckedBlockId) -> Result>; diff --git a/polkadot/api/src/light.rs b/polkadot/api/src/light.rs index 560c18669922a..6a4f1f0322438 100644 --- a/polkadot/api/src/light.rs +++ b/polkadot/api/src/light.rs @@ -22,6 +22,7 @@ use client::{Client, CallExecutor}; use codec::Slicable; use state_machine; use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; +use runtime::Address; use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; use full::CheckedId; use {PolkadotApi, BlockBuilder, RemotePolkadotApi, CheckedBlockId, Result, ErrorKind}; @@ -84,6 +85,10 @@ impl, E: CallExecutor> PolkadotApi for RemotePolkadotAp Err(ErrorKind::UnknownRuntime.into()) } + fn lookup(&self, _at: &CheckedId, _address: Address) -> Result> { + Err(ErrorKind::UnknownRuntime.into()) + } + fn active_parachains(&self, _at: &Self::CheckedBlockId) -> Result> { Err(ErrorKind::UnknownRuntime.into()) } diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 2e23b256b28e4..6146e6d87da90 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -72,6 +72,7 @@ use runtime_support::Hashable; use polkadot_api::PolkadotApi; use polkadot_primitives::{Hash, Block, BlockId, BlockNumber, Header, Timestamp}; use polkadot_primitives::parachain::{Id as ParaId, Chain, DutyRoster, BlockData, Extrinsic as ParachainExtrinsic, CandidateReceipt}; +use polkadot_runtime::BareExtrinsic; use primitives::AuthorityId; use transaction_pool::{Ready, TransactionPool}; use tokio_core::reactor::{Handle, Timeout, Interval}; @@ -502,9 +503,9 @@ impl bft::Proposer for Proposer let mut next_index = { let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client); let cur_index = self.transaction_pool.cull_and_get_pending(readiness_evaluator, |pending| pending - .filter(|tx| tx.as_ref().as_ref().signed == local_id) + .filter(|tx| tx.sender().map(|s| s == local_id).unwrap_or(false)) .last() - .map(|tx| Ok(tx.as_ref().as_ref().index)) + .map(|tx| Ok(tx.index())) .unwrap_or_else(|| self.client.index(&self.parent_id, local_id)) ); @@ -531,7 +532,7 @@ impl bft::Proposer for Proposer => MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)), } }; - let extrinsic = Extrinsic { + let extrinsic = BareExtrinsic { signed: local_id, index: next_index, function: Call::Consensus(ConsensusCall::report_misbehavior(report)), @@ -540,7 +541,13 @@ impl bft::Proposer for Proposer next_index += 1; let signature = MaybeUnsigned(self.local_key.sign(&extrinsic.encode()).into()); - let uxt = UncheckedExtrinsic { extrinsic, signature }; + + let extrinsic = Extrinsic { + signed: extrinsic.signed.into(), + index: extrinsic.index, + function: extrinsic.function, + }; + let uxt = UncheckedExtrinsic::new(extrinsic, signature); self.transaction_pool.import_unchecked_extrinsic(uxt) .expect("locally signed extrinsic is valid; qed"); diff --git a/polkadot/parachain/test-chains/basic_add/src/lib.rs b/polkadot/parachain/test-chains/basic_add/src/lib.rs index 1ef898f1dc3f5..9d0a8a0d74749 100644 --- a/polkadot/parachain/test-chains/basic_add/src/lib.rs +++ b/polkadot/parachain/test-chains/basic_add/src/lib.rs @@ -17,7 +17,7 @@ //! Basic parachain that adds a number as part of its state. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc, core_intrinsics, lang_items, panic_implementation))] +#![cfg_attr(not(feature = "std"), feature(alloc, core_intrinsics, global_allocator, lang_items, panic_implementation, core_panic_info))] #[cfg(not(feature = "std"))] extern crate alloc; diff --git a/polkadot/parachain/test-chains/basic_add/src/wasm.rs b/polkadot/parachain/test-chains/basic_add/src/wasm.rs index 3af2dd39a9722..edf53dc46f68a 100644 --- a/polkadot/parachain/test-chains/basic_add/src/wasm.rs +++ b/polkadot/parachain/test-chains/basic_add/src/wasm.rs @@ -16,15 +16,14 @@ //! Defines WASM module logic. -use core::intrinsics; - +use core::{intrinsics, panic}; use parachain::{self, ValidationResult}; use parachain::codec::Slicable; use super::{HeadData, BlockData}; #[panic_implementation] #[no_mangle] -pub fn rust_begin_panic(_info: &::core::panic::PanicInfo) -> ! { +pub fn panic(_info: &panic::PanicInfo) -> ! { unsafe { intrinsics::abort() } diff --git a/polkadot/parachain/tests/res/basic_add.wasm b/polkadot/parachain/tests/res/basic_add.wasm index 709c111d278c7..c32910f0adf97 100755 Binary files a/polkadot/parachain/tests/res/basic_add.wasm and b/polkadot/parachain/tests/res/basic_add.wasm differ diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 4deded2969293..f7861a8337fbe 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -67,6 +67,10 @@ pub type BlockNumber = u64; /// Alias to Ed25519 pubkey that identifies an account on the relay chain. pub type AccountId = primitives::hash::H256; +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = 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; diff --git a/polkadot/runtime/src/checked_block.rs b/polkadot/runtime/src/checked_block.rs new file mode 100644 index 0000000000000..4ea7bd0e88163 --- /dev/null +++ b/polkadot/runtime/src/checked_block.rs @@ -0,0 +1,108 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Typesafe block interaction. + +use super::{Call, Block, TIMESTAMP_SET_POSITION, PARACHAINS_SET_POSITION}; +use timestamp::Call as TimestampCall; +use parachains::Call as ParachainsCall; +use primitives::parachain::CandidateReceipt; + +/// Provides a type-safe wrapper around a structurally valid block. +pub struct CheckedBlock { + inner: Block, + file_line: Option<(&'static str, u32)>, +} + +impl CheckedBlock { + /// Create a new checked block. Fails if the block is not structurally valid. + pub fn new(block: Block) -> Result { + let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { + !xt.is_signed() && match xt.extrinsic.function { + Call::Timestamp(TimestampCall::set(_)) => true, + _ => false, + } + }); + + if !has_timestamp { return Err(block) } + + let has_heads = block.extrinsics.get(PARACHAINS_SET_POSITION as usize).map_or(false, |xt| { + !xt.is_signed() && match xt.extrinsic.function { + Call::Parachains(ParachainsCall::set_heads(_)) => true, + _ => false, + } + }); + + if !has_heads { return Err(block) } + Ok(CheckedBlock { + inner: block, + file_line: None, + }) + } + + // Creates a new checked block, asserting that it is valid. + #[doc(hidden)] + pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { + CheckedBlock { + inner: block, + file_line: Some((file, line)), + } + } + + /// Extract the timestamp from the block. + pub fn timestamp(&self) -> ::primitives::Timestamp { + let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Timestamp(TimestampCall::set(x)) => Some(x), + _ => None + }); + + match x { + Some(x) => x, + None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), + } + } + + /// Extract the parachain heads from the block. + pub fn parachain_heads(&self) -> &[CandidateReceipt] { + let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]), + _ => None + }); + + match x { + Some(x) => x, + None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), + } + } + + /// Convert into inner block. + pub fn into_inner(self) -> Block { self.inner } +} + +impl ::std::ops::Deref for CheckedBlock { + type Target = Block; + + fn deref(&self) -> &Block { &self.inner } +} + +/// Assert that a block is structurally valid. May lead to panic in the future +/// in case it isn't. +#[macro_export] +macro_rules! assert_polkadot_block { + ($block: expr) => { + $crate::CheckedBlock::new_unchecked($block, file!(), line!()) + } +} diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index 16d080cb9ee4e..9d9b3f9a599cc 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -58,11 +58,17 @@ extern crate substrate_runtime_staking as staking; extern crate substrate_runtime_system as system; extern crate substrate_runtime_timestamp as timestamp; +#[cfg(feature = "std")] +mod checked_block; mod parachains; +mod utils; + +#[cfg(feature = "std")] +pub use checked_block::CheckedBlock; +pub use utils::{inherent_extrinsics, check_extrinsic}; +pub use staking::address::Address as RawAddress; -use rstd::prelude::*; -use primitives::{AccountId, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; -use primitives::parachain::CandidateReceipt; +use primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; use runtime_primitives::{generic, traits::{HasPublicAux, BlakeTwo256, Convert}}; #[cfg(feature = "std")] @@ -78,107 +84,23 @@ pub const TIMESTAMP_SET_POSITION: u32 = 0; /// The position of the parachains set extrinsic. pub const PARACHAINS_SET_POSITION: u32 = 1; +/// The address format for describing accounts. +pub type Address = staking::Address; /// Block Id type for this block. pub type BlockId = generic::BlockId; /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -/// Extrinsic type as expected by this runtime. -pub type Extrinsic = generic::Extrinsic; - +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type as expected by this runtime. This is not the type that is signed. +pub type Extrinsic = generic::Extrinsic; +/// Extrinsic type that is signed. +pub type BareExtrinsic = generic::Extrinsic; /// Block type as expected by this runtime. pub type Block = generic::Block; -/// Provides a type-safe wrapper around a structurally valid block. -#[cfg(feature = "std")] -pub struct CheckedBlock { - inner: Block, - file_line: Option<(&'static str, u32)>, -} - -#[cfg(feature = "std")] -impl CheckedBlock { - /// Create a new checked block. Fails if the block is not structurally valid. - pub fn new(block: Block) -> Result { - let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { - !xt.is_signed() && match xt.extrinsic.function { - Call::Timestamp(TimestampCall::set(_)) => true, - _ => false, - } - }); - - if !has_timestamp { return Err(block) } - - let has_heads = block.extrinsics.get(PARACHAINS_SET_POSITION as usize).map_or(false, |xt| { - !xt.is_signed() && match xt.extrinsic.function { - Call::Parachains(ParachainsCall::set_heads(_)) => true, - _ => false, - } - }); - - if !has_heads { return Err(block) } - Ok(CheckedBlock { - inner: block, - file_line: None, - }) - } - - // Creates a new checked block, asserting that it is valid. - #[doc(hidden)] - pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { - CheckedBlock { - inner: block, - file_line: Some((file, line)), - } - } - - /// Extract the timestamp from the block. - pub fn timestamp(&self) -> ::primitives::Timestamp { - let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { - Call::Timestamp(TimestampCall::set(x)) => Some(x), - _ => None - }); - - match x { - Some(x) => x, - None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), - } - } - - /// Extract the parachain heads from the block. - pub fn parachain_heads(&self) -> &[CandidateReceipt] { - let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { - Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]), - _ => None - }); - - match x { - Some(x) => x, - None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), - } - } - - /// Convert into inner block. - pub fn into_inner(self) -> Block { self.inner } -} - -#[cfg(feature = "std")] -impl ::std::ops::Deref for CheckedBlock { - type Target = Block; - - fn deref(&self) -> &Block { &self.inner } -} - -/// Assert that a block is structurally valid. May lead to panic in the future -/// in case it isn't. -#[cfg(feature = "std")] -#[macro_export] -macro_rules! assert_polkadot_block { - ($block: expr) => { - $crate::CheckedBlock::new_unchecked($block, file!(), line!()) - } -} - /// Concrete runtime type used to parameterize the various modules. +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] pub struct Concrete; impl HasPublicAux for Concrete { @@ -228,6 +150,7 @@ pub type Session = session::Module; impl staking::Trait for Concrete { type Balance = Balance; type DetermineContractAddress = BlakeTwo256; + type AccountIndex = AccountIndex; } /// Staking module for this concrete runtime. pub type Staking = staking::Module; @@ -280,7 +203,7 @@ impl_outer_dispatch! { } /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; impl_outer_config! { @@ -295,35 +218,6 @@ impl_outer_config! { } } -/// Produces the list of inherent extrinsics. -pub fn inherent_extrinsics(timestamp: ::primitives::Timestamp, parachain_heads: Vec) -> Vec { - vec![ - UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: Default::default(), - function: Call::Timestamp(TimestampCall::set(timestamp)), - index: 0, - }, - signature: Default::default(), - }, - UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: Default::default(), - function: Call::Parachains(ParachainsCall::set_heads(parachain_heads)), - index: 0, - }, - signature: Default::default(), - }, - ] -} - -/// Checks an unchecked extrinsic for validity. -pub fn check_extrinsic(xt: UncheckedExtrinsic) -> bool { - use runtime_primitives::traits::Checkable; - - xt.check().is_ok() -} - pub mod api { impl_stubs!( authorities => |()| super::Consensus::authorities(), @@ -378,14 +272,14 @@ mod tests { let mut block = Block { header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), extrinsics: vec![ - UncheckedExtrinsic { - extrinsic: Extrinsic { + UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Timestamp(timestamp::Call::set(100_000_000)), signed: Default::default(), index: Default::default(), }, - signature: Default::default(), - } + Default::default(), + ) ], }; @@ -394,14 +288,14 @@ mod tests { assert_eq!(block, decoded); - block.extrinsics.push(UncheckedExtrinsic { - extrinsic: Extrinsic { + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Staking(staking::Call::stake()), signed: Default::default(), index: 10101, }, - signature: Default::default(), - }); + Default::default(), + )); let raw = block.encode(); let decoded = Block::decode(&mut &raw[..]).unwrap(); @@ -414,25 +308,25 @@ mod tests { let mut block = Block { header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), extrinsics: vec![ - UncheckedExtrinsic { - extrinsic: Extrinsic { + UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Timestamp(timestamp::Call::set(100_000_000)), signed: Default::default(), index: Default::default(), }, - signature: Default::default(), - } + Default::default(), + ) ], }; - block.extrinsics.push(UncheckedExtrinsic { - extrinsic: Extrinsic { + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Staking(staking::Call::stake()), signed: Default::default(), index: 10101, }, - signature: Default::default(), - }); + Default::default() + )); let raw = block.encode(); let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap(); @@ -444,22 +338,24 @@ mod tests { #[test] fn serialize_unchecked() { - let tx = UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: [1; 32].into(), + let tx = UncheckedExtrinsic::new( + Extrinsic { + signed: AccountId::from([1; 32]).into(), index: 999, function: Call::Timestamp(TimestampCall::set(135135)), }, - signature: runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into(), - }; - // 71000000 - // 0101010101010101010101010101010101010101010101010101010101010101 - // e703000000000000 - // 00 + runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into() + ); + + // 6f000000 + // ff0101010101010101010101010101010101010101010101010101010101010101 + // e7030000 + // 0300 // df0f0200 // 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 let v = Slicable::encode(&tx); + assert_eq!(&v[..], &hex!["6f000000ff0101010101010101010101010101010101010101010101010101010101010101e70300000300df0f02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"][..]); println!("{}", HexDisplay::from(&v)); assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx); } @@ -467,7 +363,7 @@ mod tests { #[test] fn serialize_checked() { let xt = Extrinsic { - signed: hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"].into(), + signed: AccountId::from(hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"]).into(), index: 0, function: Call::CouncilVoting(council::voting::Call::propose(Box::new( PrivCall::Consensus(consensus::PrivCall::set_code( @@ -476,11 +372,6 @@ mod tests { ))), }; let v = Slicable::encode(&xt); - - let data = hex!["e00000000d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e0000000007000000000000006369D39D892B7B87A6769F90E14C618C2B84EBB293E2CC46640136E112C078C75619AC2E0815F2511568736623C055156C8FC427CE2AEE4AE2838F86EFE80208"]; - let uxt: UncheckedExtrinsic = Slicable::decode(&mut &data[..]).unwrap(); - assert_eq!(uxt.extrinsic, xt); - assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt); } } diff --git a/polkadot/runtime/src/parachains.rs b/polkadot/runtime/src/parachains.rs index 5bd62afcf246c..9bf9a2d181ada 100644 --- a/polkadot/runtime/src/parachains.rs +++ b/polkadot/runtime/src/parachains.rs @@ -234,6 +234,7 @@ mod tests { use runtime_primitives::testing::{Digest, Header}; use consensus; + #[derive(Clone, Eq, PartialEq)] pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; diff --git a/polkadot/runtime/src/utils.rs b/polkadot/runtime/src/utils.rs new file mode 100644 index 0000000000000..4c16e215bab7e --- /dev/null +++ b/polkadot/runtime/src/utils.rs @@ -0,0 +1,51 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Utils for block interaction. + +use rstd::prelude::*; +use super::{Call, UncheckedExtrinsic, Extrinsic, Staking}; +use runtime_primitives::traits::{Checkable, AuxLookup}; +use primitives::parachain::CandidateReceipt; +use timestamp::Call as TimestampCall; +use parachains::Call as ParachainsCall; + +/// Produces the list of inherent extrinsics. +pub fn inherent_extrinsics(timestamp: ::primitives::Timestamp, parachain_heads: Vec) -> Vec { + vec![ + UncheckedExtrinsic::new( + Extrinsic { + signed: Default::default(), + function: Call::Timestamp(TimestampCall::set(timestamp)), + index: 0, + }, + Default::default() + ), + UncheckedExtrinsic::new( + Extrinsic { + signed: Default::default(), + function: Call::Parachains(ParachainsCall::set_heads(parachain_heads)), + index: 0, + }, + Default::default() + ) + ] +} + +/// Checks an unchecked extrinsic for validity. +pub fn check_extrinsic(xt: UncheckedExtrinsic) -> bool { + xt.check(Staking::lookup).is_ok() +} diff --git a/polkadot/runtime/wasm/Cargo.lock b/polkadot/runtime/wasm/Cargo.lock index 40e3acb37a1ea..aac3375c7a315 100644 --- a/polkadot/runtime/wasm/Cargo.lock +++ b/polkadot/runtime/wasm/Cargo.lock @@ -543,6 +543,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "substrate-codec" version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "substrate-keyring" @@ -647,6 +650,7 @@ version = "0.1.0" dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (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", @@ -767,6 +771,7 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 0.1.0", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (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", diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index 9ea43b2f9a1d5..4e37cbbab1864 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm index dc22849a14312..2d84f17d87db0 100755 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm differ diff --git a/polkadot/service/src/lib.rs b/polkadot/service/src/lib.rs index 1e86d52112b96..a22e62d342496 100644 --- a/polkadot/service/src/lib.rs +++ b/polkadot/service/src/lib.rs @@ -177,6 +177,11 @@ fn poc_2_testnet_config() -> ChainConfig { intentions: initial_authorities.iter().cloned().map(Into::into).collect(), transaction_base_fee: 100, transaction_byte_fee: 1, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(), validator_count: 12, sessions_per_era: 24, // 24 hours per era. @@ -236,6 +241,11 @@ fn testnet_config(initial_authorities: Vec) -> ChainConfig { intentions: initial_authorities.iter().cloned().map(Into::into).collect(), transaction_base_fee: 1, transaction_byte_fee: 0, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(), validator_count: 2, sessions_per_era: 5, diff --git a/polkadot/transaction-pool/Cargo.toml b/polkadot/transaction-pool/Cargo.toml index 140bea88e6f3d..89fa3e88c03b2 100644 --- a/polkadot/transaction-pool/Cargo.toml +++ b/polkadot/transaction-pool/Cargo.toml @@ -6,11 +6,13 @@ authors = ["Parity Technologies "] [dependencies] log = "0.3.0" error-chain = "0.11" +parking_lot = "0.4" polkadot-api = { path = "../api" } polkadot-primitives = { path = "../primitives" } polkadot-runtime = { path = "../runtime" } substrate-client = { path = "../../substrate/client" } substrate-codec = { path = "../../substrate/codec" } +substrate-keyring = { path = "../../substrate/keyring" } substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" } substrate-primitives = { path = "../../substrate/primitives" } substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } diff --git a/polkadot/transaction-pool/src/error.rs b/polkadot/transaction-pool/src/error.rs index 18b79e59fee42..78f9d3e771041 100644 --- a/polkadot/transaction-pool/src/error.rs +++ b/polkadot/transaction-pool/src/error.rs @@ -16,7 +16,7 @@ use extrinsic_pool::{self, txpool}; use primitives::Hash; -use runtime::UncheckedExtrinsic; +use runtime::{Address, UncheckedExtrinsic}; error_chain! { links { @@ -34,9 +34,9 @@ error_chain! { display("Inehrent transactions cannot be queued."), } /// Attempted to queue a transaction with bad signature. - BadSignature(xt: UncheckedExtrinsic) { + BadSignature(e: &'static str) { description("Transaction had bad signature."), - display("Transaction had bad signature."), + display("Transaction had bad signature: {}", e), } /// Attempted to queue a transaction that is already in the pool. AlreadyImported(hash: Hash) { @@ -48,6 +48,16 @@ error_chain! { description("Error importing transaction"), display("Error importing transaction: {}", err.description()), } + /// Runtime failure. + UnrecognisedAddress(who: Address) { + description("Unrecognised address in extrinsic"), + display("Unrecognised address in extrinsic: {}", who), + } + /// Extrinsic is not yet checked. + NotReady { + description("Indexed address is unverified"), + display("Indexed address is unverified"), + } } } diff --git a/polkadot/transaction-pool/src/lib.rs b/polkadot/transaction-pool/src/lib.rs index 2c2224b5d3da7..843af20fb16f2 100644 --- a/polkadot/transaction-pool/src/lib.rs +++ b/polkadot/transaction-pool/src/lib.rs @@ -22,6 +22,10 @@ extern crate substrate_runtime_primitives; extern crate polkadot_runtime as runtime; extern crate polkadot_primitives as primitives; extern crate polkadot_api; +extern crate parking_lot; + +#[cfg(test)] +extern crate substrate_keyring; #[macro_use] extern crate error_chain; @@ -33,18 +37,20 @@ mod error; use std::{ cmp::Ordering, - collections::HashMap, + collections::{hash_map::Entry, HashMap}, ops::Deref, sync::Arc, + result }; +use parking_lot::Mutex; use codec::Slicable; use extrinsic_pool::{Pool, txpool::{self, Readiness, scoring::{Change, Choice}}}; use extrinsic_pool::api::ExtrinsicPool; use polkadot_api::PolkadotApi; -use primitives::{AccountId, Hash, UncheckedExtrinsic as FutureProofUncheckedExtrinsic}; -use runtime::UncheckedExtrinsic; -use substrate_runtime_primitives::traits::{Bounded, Checkable, BlakeTwo256, Hashing}; +use primitives::{AccountId, AccountIndex, Hash, Index, UncheckedExtrinsic as FutureProofUncheckedExtrinsic}; +use runtime::{Address, RawAddress, UncheckedExtrinsic}; +use substrate_runtime_primitives::traits::{Bounded, Checkable, Hashing, BlakeTwo256}; pub use extrinsic_pool::txpool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps}; pub use error::{Error, ErrorKind, Result}; @@ -53,37 +59,68 @@ pub use error::{Error, ErrorKind, Result}; pub type CheckedExtrinsic = ::Checked; /// A verified transaction which should be includable and non-inherent. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct VerifiedTransaction { - inner: CheckedExtrinsic, + original: UncheckedExtrinsic, + // `create()` will leave this as `Some` only if the `Address` is an `AccountId`, otherwise a + // call to `polish` is needed. + inner: Mutex>, hash: Hash, encoded_size: usize, } +impl Clone for VerifiedTransaction { + fn clone(&self) -> Self { + VerifiedTransaction { + original: self.original.clone(), + inner: Mutex::new(self.inner.lock().clone()), + hash: self.hash.clone(), + encoded_size: self.encoded_size.clone(), + } + } +} + impl VerifiedTransaction { /// Attempt to verify a transaction. - fn create(xt: UncheckedExtrinsic) -> Result { - if !xt.is_signed() { - bail!(ErrorKind::IsInherent(xt)) + fn create(original: UncheckedExtrinsic) -> Result { + if !original.is_signed() { + bail!(ErrorKind::IsInherent(original)) } + const UNAVAILABLE_MESSAGE: &'static str = "chain state not available"; + let (encoded_size, hash) = original.using_encoded(|e| (e.len(), BlakeTwo256::hash(e))); + let lookup = |a| match a { + RawAddress::Id(i) => Ok(i), + _ => Err(UNAVAILABLE_MESSAGE), + }; + let inner = Mutex::new(match original.clone().check(lookup) { + Ok(xt) => Some(xt), + Err(e) if e == UNAVAILABLE_MESSAGE => None, + Err(e) => bail!(ErrorKind::BadSignature(e)), + }); + Ok(VerifiedTransaction { original, inner, hash, encoded_size }) + } - let message = Slicable::encode(&xt); - match xt.check() { - Ok(xt) => { - let hash = BlakeTwo256::hash(&message); - Ok(VerifiedTransaction { - inner: xt, - hash: hash.into(), - encoded_size: message.len(), - }) - } - Err(xt) => Err(ErrorKind::BadSignature(xt).into()), - } + /// If this transaction isn't really verified, verify it and morph it into a really verified + /// transaction. + pub fn polish(&self, lookup: F) -> Result<()> where + F: FnOnce(Address) -> result::Result + Send + Sync + { + let inner: result::Result = self.original + .clone() + .check(lookup) + .map_err(|e| ErrorKind::BadSignature(e).into()); + *self.inner.lock() = Some(inner?); + Ok(()) + } + + /// Is this transaction *really* verified? + pub fn is_really_verified(&self) -> bool { + self.inner.lock().is_some() } /// Access the underlying transaction. pub fn as_transaction(&self) -> &UncheckedExtrinsic { - self.as_ref().as_unchecked() + &self.original } /// Convert to primitive unchecked extrinsic. @@ -93,8 +130,8 @@ impl VerifiedTransaction { } /// Consume the verified transaciton, yielding the unchecked counterpart. - pub fn into_inner(self) -> CheckedExtrinsic { - self.inner + pub fn into_inner(self) -> Result { + self.inner.lock().clone().ok_or_else(|| ErrorKind::NotReady.into()) } /// Get the 256-bit hash of this transaction. @@ -103,8 +140,13 @@ impl VerifiedTransaction { } /// Get the account ID of the sender of this transaction. - pub fn sender(&self) -> &AccountId { - &self.inner.signed + pub fn sender(&self) -> Result { + self.inner.lock().as_ref().map(|i| i.signed.clone()).ok_or_else(|| ErrorKind::NotReady.into()) + } + + /// Get the account ID of the sender of this transaction. + pub fn index(&self) -> Index { + self.original.extrinsic.index } /// Get encoded size of the transaction. @@ -113,22 +155,16 @@ impl VerifiedTransaction { } } -impl AsRef for VerifiedTransaction { - fn as_ref(&self) -> &CheckedExtrinsic { - &self.inner - } -} - impl txpool::VerifiedTransaction for VerifiedTransaction { type Hash = Hash; - type Sender = AccountId; + type Sender = Address; fn hash(&self) -> &Self::Hash { &self.hash } fn sender(&self) -> &Self::Sender { - &self.inner.signed + self.original.sender() } fn mem_usage(&self) -> usize { @@ -145,7 +181,7 @@ impl txpool::Scoring for Scoring { type Event = (); fn compare(&self, old: &VerifiedTransaction, other: &VerifiedTransaction) -> Ordering { - old.inner.index.cmp(&other.inner.index) + old.index().cmp(&other.index()) } fn choose(&self, _old: &VerifiedTransaction, _new: &VerifiedTransaction) -> Choice { @@ -173,17 +209,8 @@ impl txpool::Scoring for Scoring { pub struct Ready<'a, T: 'a + PolkadotApi> { at_block: T::CheckedBlockId, api: &'a T, - known_indices: HashMap, -} - -impl<'a, T: 'a + PolkadotApi> Clone for Ready<'a, T> { - fn clone(&self) -> Self { - Ready { - at_block: self.at_block.clone(), - api: self.api, - known_indices: self.known_indices.clone(), - } - } + known_nonces: HashMap, + known_indexes: HashMap, } impl<'a, T: 'a + PolkadotApi> Ready<'a, T> { @@ -193,34 +220,85 @@ impl<'a, T: 'a + PolkadotApi> Ready<'a, T> { Ready { at_block: at, api, - known_indices: HashMap::new(), + known_nonces: HashMap::new(), + known_indexes: HashMap::new(), + } + } +} + +impl<'a, T: 'a + PolkadotApi> Clone for Ready<'a, T> { + fn clone(&self) -> Self { + Ready { + at_block: self.at_block.clone(), + api: self.api, + known_nonces: self.known_nonces.clone(), + known_indexes: self.known_indexes.clone(), } } } -impl<'a, T: 'a + PolkadotApi> txpool::Ready for Ready<'a, T> { +impl<'a, T: 'a + PolkadotApi> txpool::Ready for Ready<'a, T> +{ fn is_ready(&mut self, xt: &VerifiedTransaction) -> Readiness { - let sender = xt.inner.signed; + if !xt.is_really_verified() { + let id = match xt.original.extrinsic.signed.clone() { + RawAddress::Id(id) => id.clone(), // should never happen, since we're not verified. + RawAddress::Index(i) => match self.known_indexes.entry(i) { + Entry::Occupied(e) => e.get().clone(), + Entry::Vacant(e) => { + let (api, at_block) = (&self.api, &self.at_block); + if let Some(id) = api.lookup(at_block, RawAddress::Index(i)) + .ok() + .and_then(|o| o) + { + e.insert(id.clone()); + id + } else { + // Invalid index. + // return stale in order to get the pool to throw it away. + return Readiness::Stale + } + } + }, + }; + if VerifiedTransaction::polish(xt, move |_| Ok(id)).is_err() { + // Invalid signature. + // return stale in order to get the pool to throw it away. + return Readiness::Stale + } + } + + // guaranteed to be properly verified at this point. + + let sender = xt.sender().expect("only way to get here is `is_really_verified` or successful `polish`; either guarantees `is_really_verified`; `sender` is `Ok` if `is_really_verified`; qed"); trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.hash, Hash::from(sender)); + let is_index_sender = match xt.original.extrinsic.signed { RawAddress::Index(_) => false, _ => true }; + // TODO: find a way to handle index error properly -- will need changes to // transaction-pool trait. let (api, at_block) = (&self.api, &self.at_block); - let next_index = self.known_indices.entry(sender) - .or_insert_with(|| api.index(at_block, sender).ok().unwrap_or_else(Bounded::max_value)); - - trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.inner.index); - - let result = match xt.inner.index.cmp(&next_index) { - Ordering::Greater => Readiness::Future, - Ordering::Equal => Readiness::Ready, - Ordering::Less => Readiness::Stale, - }; - - // remember to increment `next_index` - *next_index = next_index.saturating_add(1); - - result + let get_nonce = || api.index(at_block, sender).ok().unwrap_or_else(Bounded::max_value); + let (next_nonce, was_index_sender) = self.known_nonces.entry(sender).or_insert_with(|| (get_nonce(), is_index_sender)); + + trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_nonce, xt.original.extrinsic.index); + + if *was_index_sender == is_index_sender || get_nonce() == *next_nonce { + match xt.original.extrinsic.index.cmp(&next_nonce) { + Ordering::Greater => Readiness::Future, + Ordering::Less => Readiness::Stale, + Ordering::Equal => { + // remember to increment `next_nonce` + // TODO: this won't work perfectly since accounts can now be killed, returning the nonce + // to zero. + *next_nonce = next_nonce.saturating_add(1); + Readiness::Ready + } + } + } else { + // ignore for now. + Readiness::Future + } } } @@ -251,8 +329,9 @@ impl TransactionPool { } } + // TODO: remove. This is pointless - just use `submit()` directly. pub fn import_unchecked_extrinsic(&self, uxt: UncheckedExtrinsic) -> Result> { - Ok(self.inner.import(VerifiedTransaction::create(uxt)?)?) + self.inner.submit(vec![uxt]).map(|mut v| v.swap_remove(0)) } } @@ -279,3 +358,246 @@ impl ExtrinsicPool for TransactionPool { .collect() } } + +#[cfg(test)] +mod tests { + use super::{TransactionPool, Ready}; + use substrate_keyring::Keyring::{self, *}; + use codec::Slicable; + use polkadot_api::{PolkadotApi, BlockBuilder, CheckedBlockId, Result}; + use primitives::{AccountId, AccountIndex, Block, BlockId, Hash, Index, SessionKey, Timestamp, + UncheckedExtrinsic as FutureProofUncheckedExtrinsic}; + use runtime::{RawAddress, Call, TimestampCall, BareExtrinsic, Extrinsic, UncheckedExtrinsic}; + use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; + use substrate_runtime_primitives::{MaybeUnsigned, generic}; + + struct TestBlockBuilder; + impl BlockBuilder for TestBlockBuilder { + fn push_extrinsic(&mut self, _extrinsic: FutureProofUncheckedExtrinsic) -> Result<()> { unimplemented!() } + fn bake(self) -> Result { unimplemented!() } + } + + #[derive(Clone)] + struct TestCheckedBlockId(pub BlockId); + impl CheckedBlockId for TestCheckedBlockId { + fn block_id(&self) -> &BlockId { &self.0 } + } + + fn number_of(at: &TestCheckedBlockId) -> u32 { + match at.0 { + generic::BlockId::Number(n) => n as u32, + _ => 0, + } + } + + #[derive(Clone)] + struct TestPolkadotApi; + impl PolkadotApi for TestPolkadotApi { + type CheckedBlockId = TestCheckedBlockId; + type BlockBuilder = TestBlockBuilder; + + fn check_id(&self, id: BlockId) -> Result { Ok(TestCheckedBlockId(id)) } + fn session_keys(&self, _at: &TestCheckedBlockId) -> Result> { unimplemented!() } + fn validators(&self, _at: &TestCheckedBlockId) -> Result> { unimplemented!() } + fn random_seed(&self, _at: &TestCheckedBlockId) -> Result { unimplemented!() } + fn duty_roster(&self, _at: &TestCheckedBlockId) -> Result { unimplemented!() } + fn timestamp(&self, _at: &TestCheckedBlockId) -> Result { unimplemented!() } + fn evaluate_block(&self, _at: &TestCheckedBlockId, _block: Block) -> Result { unimplemented!() } + fn active_parachains(&self, _at: &TestCheckedBlockId) -> Result> { unimplemented!() } + fn parachain_code(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result>> { unimplemented!() } + fn parachain_head(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result>> { unimplemented!() } + fn build_block(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result { unimplemented!() } + fn inherent_extrinsics(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result>> { unimplemented!() } + + fn index(&self, _at: &TestCheckedBlockId, _account: AccountId) -> Result { + Ok((_account[0] as u32) + number_of(_at)) + } + fn lookup(&self, _at: &TestCheckedBlockId, _address: RawAddress) -> Result> { + match _address { + RawAddress::Id(i) => Ok(Some(i)), + RawAddress::Index(i) => Ok(match (i < 8, i + (number_of(_at) as u64) % 8) { + (false, _) => None, + (_, 0) => Some(Alice.to_raw_public().into()), + (_, 1) => Some(Bob.to_raw_public().into()), + (_, 2) => Some(Charlie.to_raw_public().into()), + (_, 3) => Some(Dave.to_raw_public().into()), + (_, 4) => Some(Eve.to_raw_public().into()), + (_, 5) => Some(Ferdie.to_raw_public().into()), + (_, 6) => Some(One.to_raw_public().into()), + (_, 7) => Some(Two.to_raw_public().into()), + _ => None, + }), + } + } + } + + fn uxt(who: Keyring, nonce: Index, use_id: bool) -> UncheckedExtrinsic { + let sxt = BareExtrinsic { + signed: who.to_raw_public().into(), + index: nonce, + function: Call::Timestamp(TimestampCall::set(0)), + }; + let sig = sxt.using_encoded(|e| who.sign(e)); + UncheckedExtrinsic::new(Extrinsic { + signed: if use_id { RawAddress::Id(sxt.signed) } else { RawAddress::Index( + match who { + Alice => 0, + Bob => 1, + Charlie => 2, + Dave => 3, + Eve => 4, + Ferdie => 5, + One => 6, + Two => 7, + } + )}, + index: sxt.index, + function: sxt.function, + }, MaybeUnsigned(sig.into())).using_encoded(|e| UncheckedExtrinsic::decode(&mut &e[..])).unwrap() + } + + #[test] + fn id_submission_should_work() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 209, true)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209)]); + } + + #[test] + fn index_submission_should_work() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 209, false)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209)]); + } + + #[test] + fn multiple_id_submission_should_work() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 209, true)]).unwrap(); + pool.submit(vec![uxt(Alice, 210, true)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]); + } + + #[test] + fn multiple_index_submission_should_work() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 209, false)]).unwrap(); + pool.submit(vec![uxt(Alice, 210, false)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]); + } + + #[test] + fn id_based_early_nonce_should_be_culled() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 208, true)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![]); + } + + #[test] + fn index_based_early_nonce_should_be_culled() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 208, false)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![]); + } + + #[test] + fn id_based_late_nonce_should_be_queued() { + let pool = TransactionPool::new(Default::default()); + let ready = || Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + + pool.submit(vec![uxt(Alice, 210, true)]).unwrap(); + let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![]); + + pool.submit(vec![uxt(Alice, 209, true)]).unwrap(); + let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]); + } + + #[test] + fn index_based_late_nonce_should_be_queued() { + let pool = TransactionPool::new(Default::default()); + let ready = || Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + + pool.submit(vec![uxt(Alice, 210, false)]).unwrap(); + let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![]); + + pool.submit(vec![uxt(Alice, 209, false)]).unwrap(); + let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]); + } + + #[test] + fn index_then_id_submission_should_make_progress() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 209, false)]).unwrap(); + pool.submit(vec![uxt(Alice, 210, true)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![ + (Some(Alice.to_raw_public().into()), 209) + ]); + } + + #[test] + fn id_then_index_submission_should_make_progress() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 209, true)]).unwrap(); + pool.submit(vec![uxt(Alice, 210, false)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![ + (Some(Alice.to_raw_public().into()), 209) + ]); + } + + #[test] + fn index_change_should_result_in_second_tx_culled_or_future() { + let pool = TransactionPool::new(Default::default()); + pool.submit(vec![uxt(Alice, 209, false)]).unwrap(); + pool.submit(vec![uxt(Alice, 210, false)]).unwrap(); + + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![ + (Some(Alice.to_raw_public().into()), 209), + (Some(Alice.to_raw_public().into()), 210) + ]); + + // first xt is mined, but that has a side-effect of switching index 0 from Alice to Bob. + // second xt now invalid signature, so it fails. + + // there is no way of reporting this back to the queue right now (TODO). this should cause + // the queue to flush all information regarding the sender index/account. + + // after this, a re-evaluation of the second's readiness should result in it being thrown + // out (or maybe placed in future queue). +/* + // TODO: uncomment once the new queue design is in. + let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(1)).unwrap(), &TestPolkadotApi); + let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect()); + assert_eq!(pending, vec![]); +*/ + } +} diff --git a/substrate/codec/Cargo.toml b/substrate/codec/Cargo.toml index a800afa926baa..a228686e90b1d 100644 --- a/substrate/codec/Cargo.toml +++ b/substrate/codec/Cargo.toml @@ -5,6 +5,7 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] +arrayvec = { version = "0.4", default_features = false } [features] default = ["std"] diff --git a/substrate/codec/src/lib.rs b/substrate/codec/src/lib.rs index b4c360fb28958..b0ed6dc328dca 100644 --- a/substrate/codec/src/lib.rs +++ b/substrate/codec/src/lib.rs @@ -26,6 +26,8 @@ extern crate alloc; #[cfg(feature = "std")] extern crate core; +extern crate arrayvec; + #[cfg(feature = "std")] pub mod alloc { pub use std::boxed; diff --git a/substrate/codec/src/slicable.rs b/substrate/codec/src/slicable.rs index 0ea2c0e6306ee..8d66b896faf6d 100644 --- a/substrate/codec/src/slicable.rs +++ b/substrate/codec/src/slicable.rs @@ -20,6 +20,7 @@ use alloc::vec::Vec; use alloc::boxed::Box; use core::{mem, slice}; use super::joiner::Joiner; +use arrayvec::ArrayVec; /// Trait that allows reading of data into a slice. pub trait Input { @@ -87,25 +88,75 @@ impl Slicable for Result { } } -impl Slicable for Option { +/// Shim type because we can't do a specialised implementation for `Option` directly. +pub struct OptionBool(pub Option); + +impl Slicable for OptionBool { fn decode(input: &mut I) -> Option { match input.read_byte()? { - 0 => Some(Some(false)), - 1 => Some(Some(true)), - 2 => Some(None), + 0 => Some(OptionBool(None)), + 1 => Some(OptionBool(Some(true))), + 2 => Some(OptionBool(Some(false))), _ => None, } } fn using_encoded R>(&self, f: F) -> R { f(&[match *self { - Some(false) => 0u8, - Some(true) => 1u8, - None => 2u8, + OptionBool(None) => 0u8, + OptionBool(Some(true)) => 1u8, + OptionBool(Some(false)) => 2u8, }]) } } +impl Slicable for Option { + fn decode(input: &mut I) -> Option { + match input.read_byte()? { + 0 => Some(None), + 1 => Some(Some(T::decode(input)?)), + _ => None, + } + } + + fn encode(&self) -> Vec { + let mut v = Vec::new(); + match *self { + Some(ref t) => { + v.push(1); + t.using_encoded(|s| v.extend(s)); + } + None => v.push(0), + } + v + } +} + +macro_rules! impl_array { + ( $( $n:expr )* ) => { $( + impl Slicable for [T; $n] { + fn decode(input: &mut I) -> Option { + let mut r = ArrayVec::new(); + for _ in 0..$n { + r.push(T::decode(input)?); + } + r.into_inner().ok() + } + + fn encode(&self) -> Vec { + use core::iter::Extend; + let mut r = Vec::new(); + for item in self.iter() { + item.using_encoded(|e| r.extend(e)); + } + r + } + } + )* } +} + +impl_array!(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 + 40 48 56 64 72 96 128 160 192 224 256); impl Slicable for Box { fn decode(input: &mut I) -> Option { @@ -145,12 +196,8 @@ impl Slicable for Vec { u32::decode(input).and_then(move |len| { let mut r = Vec::with_capacity(len as usize); for _ in 0..len { - r.push(match T::decode(input) { - Some(x) => x, - None => return None, - }); + r.push(T::decode(input)?); } - Some(r) }) } @@ -163,14 +210,12 @@ impl Slicable for Vec { let mut r: Vec = Vec::new().and(&(len as u32)); for item in self { - r.extend(item.encode()); + item.using_encoded(|e| r.extend(e)) } r } } - - impl Slicable for () { fn decode(_: &mut I) -> Option<()> { Some(()) diff --git a/substrate/environmental/src/lib.rs b/substrate/environmental/src/lib.rs index 8c415fe63af65..86e1100ecccad 100644 --- a/substrate/environmental/src/lib.rs +++ b/substrate/environmental/src/lib.rs @@ -150,18 +150,18 @@ pub fn with R>( /// trait Increment { fn increment(&mut self); } /// /// impl Increment for i32 { -/// fn increment(&mut self) { *self += 1 } +/// fn increment(&mut self) { *self += 1 } /// } /// /// environmental!(val: Increment + 'static); /// /// fn main() { -/// let mut local = 0i32; -/// val::using(&mut local, || { -/// val::with(|v| for _ in 0..5 { v.increment() }); -/// }); +/// let mut local = 0i32; +/// val::using(&mut local, || { +/// val::with(|v| for _ in 0..5 { v.increment() }); +/// }); /// -/// assert_eq!(local, 5); +/// assert_eq!(local, 5); /// } /// ``` #[macro_export] diff --git a/substrate/executor/wasm/Cargo.lock b/substrate/executor/wasm/Cargo.lock index f9291ee9c6526..96f0ce1616f42 100644 --- a/substrate/executor/wasm/Cargo.lock +++ b/substrate/executor/wasm/Cargo.lock @@ -1,3 +1,11 @@ +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "byteorder" version = "1.2.1" @@ -13,6 +21,11 @@ name = "fixed-hash" version = "0.1.3" source = "git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm#8dc457899afdaf968ff7f16140b03d1e37b01d71" +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pwasm-alloc" version = "0.1.0" @@ -67,6 +80,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "substrate-codec" version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "substrate-primitives" @@ -122,9 +138,11 @@ dependencies = [ ] [metadata] +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum fixed-hash 0.1.3 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)" = "" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum rustc-hex 2.0.0 (git+https://github.com/rphmeier/rustc-hex.git)" = "" "checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" diff --git a/substrate/executor/wasm/src/lib.rs b/substrate/executor/wasm/src/lib.rs index e7c28e37eaec4..8d99329239693 100644 --- a/substrate/executor/wasm/src/lib.rs +++ b/substrate/executor/wasm/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(lang_items)] +#![feature(panic_implementation)] #![cfg_attr(feature = "strict", deny(warnings))] #![feature(alloc)] diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index 8b02a415c4c2d..d62a456ea44ea 100644 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm index 95220fd9db80a..b470be1870577 100755 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/network/src/test/sync.rs b/substrate/network/src/test/sync.rs index e5cc1ebfad082..71e57e4b292f5 100644 --- a/substrate/network/src/test/sync.rs +++ b/substrate/network/src/test/sync.rs @@ -84,4 +84,3 @@ fn sync_after_fork_works() { assert!(net.peer(1).client.backend().blockchain().canon_equals_to(&peer1_chain)); assert!(net.peer(2).client.backend().blockchain().canon_equals_to(&peer1_chain)); } - diff --git a/substrate/runtime-io/src/lib.rs b/substrate/runtime-io/src/lib.rs index 6526568920792..b3373fb46dc09 100644 --- a/substrate/runtime-io/src/lib.rs +++ b/substrate/runtime-io/src/lib.rs @@ -18,9 +18,9 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(lang_items))] +#![cfg_attr(not(feature = "std"), feature(panic_implementation))] #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #![cfg_attr(not(feature = "std"), feature(alloc))] -#![cfg_attr(not(feature = "std"), feature(panic_implementation))] #![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")] #![cfg_attr(not(feature = "std"), doc = "Substrate's runtime standard library as compiled without Rust's standard library.")] diff --git a/substrate/runtime-io/without_std.rs b/substrate/runtime-io/without_std.rs index ffe98bb5be057..9e145aa853a1a 100644 --- a/substrate/runtime-io/without_std.rs +++ b/substrate/runtime-io/without_std.rs @@ -23,18 +23,18 @@ pub extern crate substrate_runtime_std as rstd; #[doc(hidden)] pub extern crate substrate_codec as codec; -use rstd::intrinsics; +use core::intrinsics; use rstd::vec::Vec; pub use rstd::{mem, slice}; #[panic_implementation] #[no_mangle] -pub fn rust_begin_panic(info: &::core::panic::PanicInfo) -> ! { +pub fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { - if let Some(loc) = info.location() { - ext_print_utf8(loc.file().as_ptr() as *const u8, loc.file().len() as u32); - ext_print_num(loc.line() as u64); - ext_print_num(loc.column() as u64); + if let Some(location) = _info.location() { + ext_print_utf8(location.file().as_ptr() as *const u8, location.file().len() as u32); + ext_print_num(location.line() as u64); + ext_print_num(location.column() as u64); } intrinsics::abort() } diff --git a/substrate/runtime-sandbox/src/lib.rs b/substrate/runtime-sandbox/src/lib.rs index f275404faec3b..6c71c085bc3f5 100755 --- a/substrate/runtime-sandbox/src/lib.rs +++ b/substrate/runtime-sandbox/src/lib.rs @@ -34,7 +34,7 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(lang_items))] +#![cfg_attr(not(feature = "std"), feature(panic_implementation))] #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #![cfg_attr(not(feature = "std"), feature(alloc))] diff --git a/substrate/runtime-std/src/lib.rs b/substrate/runtime-std/src/lib.rs index 22bdebe96b366..64267b23d0547 100644 --- a/substrate/runtime-std/src/lib.rs +++ b/substrate/runtime-std/src/lib.rs @@ -18,7 +18,7 @@ //! or core/alloc to be used with any code that depends on the runtime. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(lang_items))] +#![cfg_attr(not(feature = "std"), feature(panic_implementation))] #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #![cfg_attr(not(feature = "std"), feature(alloc))] #![cfg_attr(not(feature = "std"), feature(use_extern_macros))] diff --git a/substrate/runtime-support/src/dispatch.rs b/substrate/runtime-support/src/dispatch.rs index 0425b6f46a368..d9c33bdc4c704 100644 --- a/substrate/runtime-support/src/dispatch.rs +++ b/substrate/runtime-support/src/dispatch.rs @@ -84,6 +84,9 @@ macro_rules! decl_module { pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>; $($rest:tt)* ) => { + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] // TODO: switching based on std feature is because of an issue in // serde-derive for when we attempt to derive `Deserialize` on these types, // in a situation where we've imported `substrate_runtime_support` as another name. @@ -91,6 +94,9 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>); + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] #[cfg(not(feature = "std"))] $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>); @@ -105,10 +111,16 @@ macro_rules! decl_module { struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>; $($rest:tt)* ) => { + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] #[cfg(feature = "std")] $(#[$attr])* struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>); + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] #[cfg(not(feature = "std"))] $(#[$attr])* struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>); diff --git a/substrate/runtime-support/src/lib.rs b/substrate/runtime-support/src/lib.rs index 8577a31bf7ae5..b07d17c41dcf8 100644 --- a/substrate/runtime-support/src/lib.rs +++ b/substrate/runtime-support/src/lib.rs @@ -77,6 +77,9 @@ macro_rules! assert_err { #[cfg(feature = "std")] macro_rules! assert_ok { ( $x:expr ) => { - assert!($x.is_ok()); + assert_eq!($x, Ok(())); + }; + ( $x:expr, $y:expr ) => { + assert_eq!($x, Ok($y)); } } diff --git a/substrate/runtime/consensus/src/lib.rs b/substrate/runtime/consensus/src/lib.rs index 6a8fa64a6b804..ec62a7d8cb88b 100644 --- a/substrate/runtime/consensus/src/lib.rs +++ b/substrate/runtime/consensus/src/lib.rs @@ -60,7 +60,7 @@ pub type KeyValue = (Vec, Vec); pub trait Trait: system::Trait { type PublicAux: RefInto; - type SessionKey: Parameter + Default; + type SessionKey: Parameter + Default; } decl_module! { diff --git a/substrate/runtime/contract/src/lib.rs b/substrate/runtime/contract/src/lib.rs index 6b8f63b974cc6..bc51673fd44db 100644 --- a/substrate/runtime/contract/src/lib.rs +++ b/substrate/runtime/contract/src/lib.rs @@ -624,171 +624,6 @@ mod tests { }]); } - /// Returns code that uses `ext_create` runtime call. - /// - /// Takes bytecode of the contract that needs to be deployed. - fn code_create(child_bytecode: &[u8]) -> String { - /// Convert a byte slice to a string with hex values. - /// - /// Each value is preceeded with a `\` character. - fn escaped_bytestring(bytes: &[u8]) -> String { - use std::fmt::Write; - let mut result = String::new(); - for b in bytes { - write!(result, "\\{:02x}", b).unwrap(); - } - result - } - - format!( -r#" -(module - ;; ext_create(code_ptr: u32, code_len: u32, value_ptr: u32, value_len: u32) - (import "env" "ext_create" (func $ext_create (param i32 i32 i32 i32))) - - (import "env" "memory" (memory 1 1)) - - (func (export "call") - (call $ext_create - (i32.const 12) ;; Pointer to `code` - (i32.const {code_len}) ;; Length of `code` - (i32.const 4) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer - ) - ) - ;; Amount of value to transfer. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 4) "\03\00\00\00\00\00\00\00") - - ;; Embedded wasm code. - (data (i32.const 12) "{escaped_bytecode}") -) -"#, - escaped_bytecode = escaped_bytestring(&child_bytecode), - code_len = child_bytecode.len(), - ) - } - - #[test] - fn contract_create() { - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - let code_create = wabt::wat2wasm(&code_create(&code_transfer)).unwrap(); - - let mut mock_ext = MockExt::default(); - execute(&code_create, &mut mock_ext, 50_000).unwrap(); - - assert_eq!(&mock_ext.creates, &[ - CreateEntry { - code: code_transfer, - endownment: 3, - } - ]); - } - - /// This code a value from the storage, increment it's first byte - /// and then stores it back in the storage. - const CODE_ADDER: &str = -r#" -(module - ;; ext_set_storage(location_ptr: i32, value_non_null: bool, value_ptr: i32) - (import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32))) - ;; ext_get_storage(location_ptr: i32, value_ptr: i32) - (import "env" "ext_get_storage" (func $ext_get_storage (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - (func (export "call") - (call $ext_get_storage - (i32.const 4) ;; Point to a location of the storage. - (i32.const 36) ;; The result will be written at this address. - ) - (i32.store - (i32.const 36) - (i32.add - (i32.load - (i32.const 36) - ) - (i32.const 1) - ) - ) - - (call $ext_set_storage - (i32.const 4) ;; Pointer to a location of the storage. - (i32.const 1) ;; Value is not null. - (i32.const 36) ;; Pointer to a data we want to put in the storage. - ) - ) - - ;; Location of storage to load/store the data. 32 bytes. - (data (i32.const 4) "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01") -) -"#; - - #[test] - fn contract_adder() { - let code_adder = wabt::wat2wasm(CODE_ADDER).unwrap(); - - let mut mock_ext = MockExt::default(); - - // Execute the test twice. - execute(&code_adder, &mut mock_ext, 50_000).unwrap(); - execute(&code_adder, &mut mock_ext, 50_000).unwrap(); - - let storage_addr = [0x01u8; 32]; - assert_eq!( - &mock_ext.storage.get(&storage_addr[..]).unwrap()[..], - &[ - 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ][..], - ); - } - - // This code should make 100_000 iterations. - const CODE_LOOP: &str = -r#" -(module - (func (export "call") - (local $i i32) - - loop $l - ;; $i = $i + 1 - (set_local $i - (i32.add - (get_local $i) - (i32.const 1) - ) - ) - - ;; if $i < 100_000u32: goto $l - (br_if $l - (i32.lt_u - (get_local $i) - (i32.const 100000) - ) - ) - end - ) -) -"#; - - #[test] - fn contract_out_of_gas() { - let code_loop = wabt::wat2wasm(CODE_LOOP).unwrap(); - - let mut mock_ext = MockExt::default(); - - assert_matches!( - execute(&code_loop, &mut mock_ext, 900_000), - Err(_) - ); - assert_matches!( - execute(&code_loop, &mut mock_ext, 937_000), - Ok(_) - ); - } - const CODE_MEM: &str = r#" (module diff --git a/substrate/runtime/council/src/lib.rs b/substrate/runtime/council/src/lib.rs index 9a0dcf38d9b92..d0509c7045ec1 100644 --- a/substrate/runtime/council/src/lib.rs +++ b/substrate/runtime/council/src/lib.rs @@ -40,9 +40,10 @@ extern crate substrate_runtime_staking as staking; extern crate substrate_runtime_system as system; use rstd::prelude::*; -use primitives::traits::{Zero, One, RefInto, As}; +use primitives::traits::{Zero, One, RefInto, As, AuxLookup}; use substrate_runtime_support::{StorageValue, StorageMap}; use substrate_runtime_support::dispatch::Result; +use staking::address::Address; pub mod voting; @@ -110,16 +111,16 @@ decl_module! { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Call where aux: T::PublicAux { fn set_approvals(aux, votes: Vec, index: VoteIndex) -> Result = 0; - fn reap_inactive_voter(aux, signed_index: u32, who: T::AccountId, who_index: u32, assumed_vote_index: VoteIndex) -> Result = 1; + fn reap_inactive_voter(aux, signed_index: u32, who: Address, who_index: u32, assumed_vote_index: VoteIndex) -> Result = 1; fn retract_voter(aux, index: u32) -> Result = 2; fn submit_candidacy(aux, slot: u32) -> Result = 3; - fn present_winner(aux, candidate: T::AccountId, total: T::Balance, index: VoteIndex) -> Result = 4; + fn present_winner(aux, candidate: Address, total: T::Balance, index: VoteIndex) -> Result = 4; } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum PrivCall { fn set_desired_seats(count: u32) -> Result = 0; - fn remove_member(who: T::AccountId) -> Result = 1; + fn remove_member(who: Address) -> Result = 1; fn set_presentation_duration(count: T::BlockNumber) -> Result = 2; fn set_term_duration(count: T::BlockNumber) -> Result = 3; } @@ -239,7 +240,7 @@ impl Module { if !>::exists(aux.ref_into()) { // not yet a voter - deduct bond. // NOTE: this must be the last potential bailer, since it changes state. - >::reserve_balance(aux.ref_into(), Self::voting_bond())?; + >::reserve(aux.ref_into(), Self::voting_bond())?; >::put({ let mut v = Self::voters(); @@ -260,10 +261,11 @@ impl Module { fn reap_inactive_voter( aux: &T::PublicAux, signed_index: u32, - who: T::AccountId, + who: Address, who_index: u32, assumed_vote_index: VoteIndex ) -> Result { + let who = >::lookup(who)?; ensure!(!Self::presentation_active(), "cannot reap during presentation period"); ensure!(Self::voter_last_active(aux.ref_into()).is_some(), "reaper must be a voter"); let last_active = Self::voter_last_active(&who).ok_or("target for inactivity cleanup must be active")?; @@ -291,10 +293,13 @@ impl Module { voters ); if valid { - >::transfer_reserved_balance(&who, aux.ref_into(), Self::voting_bond()) + // This only fails if `who` doesn't exist, which it clearly must do since its the aux. + // Still, it's no more harmful to propagate any error at this point. + >::transfer_reserved(&who, aux.ref_into(), Self::voting_bond())?; } else { - >::slash_reserved(aux.ref_into(), Self::voting_bond()) + >::slash_reserved(aux.ref_into(), Self::voting_bond()); } + Ok(()) } /// Remove a voter. All votes are cancelled and the voter deposit is returned. @@ -307,7 +312,7 @@ impl Module { ensure!(&voters[index] == aux.ref_into(), "retraction index mismatch"); Self::remove_voter(aux.ref_into(), index, voters); - >::unreserve_balance(aux.ref_into(), Self::voting_bond()); + >::unreserve(aux.ref_into(), Self::voting_bond()); Ok(()) } @@ -325,7 +330,7 @@ impl Module { "invalid candidate slot" ); // NOTE: This must be last as it has side-effects. - >::deduct_unbonded(aux.ref_into(), Self::candidacy_bond()) + >::reserve(aux.ref_into(), Self::candidacy_bond()) .map_err(|_| "candidate has not enough funds")?; let mut candidates = candidates; @@ -345,10 +350,11 @@ impl Module { /// `signed` should have at least fn present_winner( aux: &T::PublicAux, - candidate: T::AccountId, + candidate: Address, total: T::Balance, index: VoteIndex ) -> Result { + let candidate = >::lookup(candidate)?; ensure!(index == Self::vote_index(), "index not current"); let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?; let stakes = Self::snapshoted_stakes(); @@ -381,12 +387,13 @@ impl Module { leaderboard[0] = (total, candidate); leaderboard.sort_by_key(|&(t, _)| t); >::put(leaderboard); + Ok(()) } else { // we can rest assured it will be Ok since we checked `can_slash` earlier; still // better safe than sorry. let _ = >::slash(aux.ref_into(), bad_presentation_punishment); + Err(if dupe { "duplicate presentation" } else { "incorrect total" }) } - Ok(()) } /// Set the desired member count; if lower than the current count, then seats will not be up @@ -400,7 +407,8 @@ impl Module { /// Remove a particular member. A tally will happen instantly (if not already in a presentation /// period) to fill the seat if removal means that the desired members are not met. /// This is effective immediately. - fn remove_member(who: T::AccountId) -> Result { + fn remove_member(who: Address) -> Result { + let who = >::lookup(who)?; let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() .into_iter() .filter(|i| i.0 != who) @@ -460,7 +468,7 @@ impl Module { >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); let voters = Self::voters(); - let votes = voters.iter().map(>::balance).collect::>(); + let votes = voters.iter().map(>::voting_balance).collect::>(); >::put(votes); // initialise leaderboard. @@ -487,7 +495,7 @@ impl Module { .take_while(|&&(b, _)| !b.is_zero()) .take(coming as usize) { - >::refund(w, candidacy_bond); + >::unreserve(w, candidacy_bond); } // set the new council. @@ -617,6 +625,8 @@ mod tests { } } + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; @@ -640,6 +650,7 @@ mod tests { impl staking::Trait for Test { type Balance = u64; type DetermineContractAddress = staking::DummyContractAddressFor; + type AccountIndex = u64; } impl democracy::Trait for Test { type Proposal = Proposal; @@ -665,6 +676,11 @@ mod tests { bonding_duration: 0, transaction_base_fee: 0, transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, }.build_externalities()); t.extend(democracy::GenesisConfig::{ launch_period: 1, @@ -980,13 +996,16 @@ mod tests { assert_ok!(Council::submit_candidacy(&5, 1)); assert_ok!(Council::set_approvals(&2, vec![true, false], 0)); assert_ok!(Council::set_approvals(&5, vec![false, true], 0)); + assert_eq!(Council::voters(), vec![2, 5]); + assert_eq!(Council::approvals_of(2), vec![true, false]); + assert_eq!(Council::approvals_of(5), vec![false, true]); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); assert!(Council::presentation_active()); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (0, 0), (11, 2), (41, 5)])); + assert_eq!(Council::present_winner(&4, 2.into(), 20, 0), Ok(())); + assert_eq!(Council::present_winner(&4, 5.into(), 50, 0), Ok(())); + assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); assert_ok!(Council::end_block(System::block_number())); @@ -1014,13 +1033,13 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 0)); + assert_eq!(Council::present_winner(&4, 5.into(), 50, 0), Err("duplicate presentation")); assert_ok!(Council::end_block(System::block_number())); assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); - assert_eq!(Staking::balance(&4), 38); + assert_eq!(Staking::voting_balance(&4), 38); }); } @@ -1033,7 +1052,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); @@ -1042,19 +1061,19 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(&4, 5, 41, 1)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 1)); assert_ok!(Council::end_block(System::block_number())); assert_ok!(Council::reap_inactive_voter(&5, Council::voters().iter().position(|&i| i == 5).unwrap() as u32, - 2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, 2 )); assert_eq!(Council::voters(), vec![5]); assert_eq!(Council::approvals_of(2).len(), 0); - assert_eq!(Staking::balance(&2), 17); - assert_eq!(Staking::balance(&5), 53); + assert_eq!(Staking::voting_balance(&2), 17); + assert_eq!(Staking::voting_balance(&5), 53); }); } @@ -1062,21 +1081,21 @@ mod tests { fn presenting_for_double_election_should_not_work() { with_externalities(&mut new_test_ext(false), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(&2, 0)); + assert_eq!(Council::submit_candidacy(&2, 0), Ok(())); assert_ok!(Council::set_approvals(&2, vec![true], 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::submit_candidacy(&2, 0)); + assert_eq!(Council::submit_candidacy(&2, 0), Ok(())); assert_ok!(Council::set_approvals(&2, vec![true], 1)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_noop!(Council::present_winner(&4, 2, 11, 1), "candidate must not form a duplicated member if elected"); + assert_noop!(Council::present_winner(&4, 2.into(), 20, 1), "candidate must not form a duplicated member if elected"); }); } @@ -1089,7 +1108,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); @@ -1098,7 +1117,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(&4, 5, 41, 1)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 1)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(11); @@ -1106,14 +1125,14 @@ mod tests { assert_ok!(Council::reap_inactive_voter(&5, Council::voters().iter().position(|&i| i == 5).unwrap() as u32, - 2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, 2 )); assert_eq!(Council::voters(), vec![5]); assert_eq!(Council::approvals_of(2).len(), 0); - assert_eq!(Staking::balance(&2), 17); - assert_eq!(Staking::balance(&5), 53); + assert_eq!(Staking::voting_balance(&2), 17); + assert_eq!(Staking::voting_balance(&5), 53); }); } @@ -1126,7 +1145,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 8, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); @@ -1135,12 +1154,12 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(&4, 5, 38, 1)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 1)); assert_ok!(Council::end_block(System::block_number())); assert_noop!(Council::reap_inactive_voter(&2, 42, - 2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, 2 ), "bad reporter index"); }); @@ -1155,7 +1174,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 8, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); @@ -1164,12 +1183,12 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(&4, 5, 38, 1)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 1)); assert_ok!(Council::end_block(System::block_number())); assert_noop!(Council::reap_inactive_voter(&2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32, - 2, 42, + 2.into(), 42, 2 ), "bad target index"); }); @@ -1190,10 +1209,10 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); - assert_ok!(Council::present_winner(&4, 3, 21, 0)); - assert_ok!(Council::present_winner(&4, 4, 31, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); + assert_ok!(Council::present_winner(&4, 3.into(), 30, 0)); + assert_ok!(Council::present_winner(&4, 4.into(), 40, 0)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); @@ -1201,19 +1220,19 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(&4, 2, 11, 1)); - assert_ok!(Council::present_winner(&4, 3, 21, 1)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 1)); + assert_ok!(Council::present_winner(&4, 3.into(), 30, 1)); assert_ok!(Council::end_block(System::block_number())); assert_ok!(Council::reap_inactive_voter(&4, Council::voters().iter().position(|&i| i == 4).unwrap() as u32, - 2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, 2 )); assert_eq!(Council::voters(), vec![2, 3, 5]); assert_eq!(Council::approvals_of(4).len(), 0); - assert_eq!(Staking::balance(&4), 37); + assert_eq!(Staking::voting_balance(&4), 37); }); } @@ -1226,7 +1245,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); @@ -1235,12 +1254,12 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(&4, 5, 41, 1)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 1)); assert_ok!(Council::end_block(System::block_number())); assert_noop!(Council::reap_inactive_voter(&4, 0, - 2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, 2 ), "reaper must be a voter"); }); @@ -1263,11 +1282,11 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 1, 60, 0)); - assert_ok!(Council::present_winner(&4, 3, 21, 0)); - assert_ok!(Council::present_winner(&4, 4, 31, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); - assert_noop!(Council::present_winner(&4, 2, 11, 0), "candidate not worthy of leaderboard"); + assert_ok!(Council::present_winner(&4, 1.into(), 60, 0)); + assert_ok!(Council::present_winner(&4, 3.into(), 30, 0)); + assert_ok!(Council::present_winner(&4, 4.into(), 40, 0)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 0)); + assert_noop!(Council::present_winner(&4, 2.into(), 20, 0), "candidate not worthy of leaderboard"); }); } @@ -1288,16 +1307,16 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 11, 0)); - assert_ok!(Council::present_winner(&4, 1, 60, 0)); - assert_ok!(Council::present_winner(&4, 3, 21, 0)); - assert_ok!(Council::present_winner(&4, 4, 31, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); + assert_ok!(Council::present_winner(&4, 2.into(), 20, 0)); + assert_ok!(Council::present_winner(&4, 1.into(), 60, 0)); + assert_ok!(Council::present_winner(&4, 3.into(), 30, 0)); + assert_ok!(Council::present_winner(&4, 4.into(), 40, 0)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 0)); assert_eq!(Council::leaderboard(), Some(vec![ - (21, 3), - (31, 4), - (41, 5), + (30, 3), + (40, 4), + (50, 5), (60, 1) ])); }); @@ -1308,7 +1327,7 @@ mod tests { with_externalities(&mut new_test_ext(false), || { System::set_block_number(4); assert!(!Council::presentation_active()); - assert_noop!(Council::present_winner(&5, 5, 1, 0), "cannot present outside of presentation period"); + assert_noop!(Council::present_winner(&5, 5.into(), 1, 0), "cannot present outside of presentation period"); }); } @@ -1323,7 +1342,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_noop!(Council::present_winner(&4, 2, 11, 1), "index not current"); + assert_noop!(Council::present_winner(&4, 2.into(), 20, 1), "index not current"); }); } @@ -1340,8 +1359,9 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_eq!(Staking::balance(&1), 1); - assert_noop!(Council::present_winner(&1, 1, 30, 0), "presenter must have sufficient slashable funds"); + assert_eq!(Staking::free_balance(&1), 1); + assert_eq!(Staking::reserved_balance(&1), 9); + assert_noop!(Council::present_winner(&1, 1.into(), 20, 0), "presenter must have sufficient slashable funds"); }); } @@ -1350,7 +1370,7 @@ mod tests { with_externalities(&mut new_test_ext(false), || { System::set_block_number(4); assert!(!Council::presentation_active()); - assert_eq!(Staking::balance(&4), 40); + assert_eq!(Staking::voting_balance(&4), 40); assert_ok!(Council::submit_candidacy(&2, 0)); assert_ok!(Council::submit_candidacy(&5, 1)); @@ -1359,9 +1379,9 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 2, 80, 0)); + assert_err!(Council::present_winner(&4, 2.into(), 80, 0), "incorrect total"); - assert_eq!(Staking::balance(&4), 38); + assert_eq!(Staking::voting_balance(&4), 38); }); } @@ -1386,20 +1406,20 @@ mod tests { System::set_block_number(6); assert!(Council::presentation_active()); - assert_ok!(Council::present_winner(&4, 1, 60, 0)); + assert_ok!(Council::present_winner(&4, 1.into(), 60, 0)); assert_eq!(Council::leaderboard(), Some(vec![ (0, 0), (0, 0), (0, 0), (60, 1) ])); - assert_ok!(Council::present_winner(&4, 3, 21, 0)); - assert_ok!(Council::present_winner(&4, 4, 31, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); + assert_ok!(Council::present_winner(&4, 3.into(), 30, 0)); + assert_ok!(Council::present_winner(&4, 4.into(), 40, 0)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 0)); assert_eq!(Council::leaderboard(), Some(vec![ - (21, 3), - (31, 4), - (41, 5), + (30, 3), + (40, 4), + (50, 5), (60, 1) ])); @@ -1441,10 +1461,10 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(&4, 1, 60, 0)); - assert_ok!(Council::present_winner(&4, 3, 21, 0)); - assert_ok!(Council::present_winner(&4, 4, 31, 0)); - assert_ok!(Council::present_winner(&4, 5, 41, 0)); + assert_ok!(Council::present_winner(&4, 1.into(), 60, 0)); + assert_ok!(Council::present_winner(&4, 3.into(), 30, 0)); + assert_ok!(Council::present_winner(&4, 4.into(), 40, 0)); + assert_ok!(Council::present_winner(&4, 5.into(), 50, 0)); assert_ok!(Council::end_block(System::block_number())); System::set_block_number(8); @@ -1453,8 +1473,8 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(&4, 3, 81, 1)); - assert_ok!(Council::present_winner(&4, 4, 31, 1)); + assert_ok!(Council::present_winner(&4, 3.into(), 90, 1)); + assert_ok!(Council::present_winner(&4, 4.into(), 40, 1)); assert_ok!(Council::end_block(System::block_number())); assert!(!Council::presentation_active()); diff --git a/substrate/runtime/council/src/voting.rs b/substrate/runtime/council/src/voting.rs index 0b9dec8976c39..8470d1b420404 100644 --- a/substrate/runtime/council/src/voting.rs +++ b/substrate/runtime/council/src/voting.rs @@ -256,7 +256,7 @@ mod tests { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); let proposal = bonding_duration_proposal(42); - assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove)); + assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); let cancellation = cancel_referendum_proposal(0); @@ -279,7 +279,7 @@ mod tests { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); let proposal = bonding_duration_proposal(42); - assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove)); + assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); let cancellation = cancel_referendum_proposal(0); let hash = cancellation.blake2_256().into(); @@ -299,7 +299,7 @@ mod tests { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); let proposal = bonding_duration_proposal(42); - assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove)); + assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); let cancellation = cancel_referendum_proposal(0); let hash = cancellation.blake2_256().into(); diff --git a/substrate/runtime/democracy/src/lib.rs b/substrate/runtime/democracy/src/lib.rs index d6b6a4640f110..70565e5850961 100644 --- a/substrate/runtime/democracy/src/lib.rs +++ b/substrate/runtime/democracy/src/lib.rs @@ -145,7 +145,7 @@ impl Module { /// 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| (>::balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/)) + .map(|a| (>::voting_balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: 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)) } @@ -155,7 +155,7 @@ impl Module { /// Propose a sensitive action to be taken. fn propose(aux: &T::PublicAux, proposal: Box, value: T::Balance) -> Result { ensure!(value >= Self::minimum_deposit(), "value too low"); - >::deduct_unbonded(aux.ref_into(), value) + >::reserve(aux.ref_into(), value) .map_err(|_| "proposer's balance too low")?; let index = Self::public_prop_count(); @@ -172,7 +172,7 @@ impl Module { fn second(aux: &T::PublicAux, proposal: PropIndex) -> Result { let mut deposit = Self::deposit_of(proposal) .ok_or("can only second an existing proposal")?; - >::deduct_unbonded(aux.ref_into(), deposit.0) + >::reserve(aux.ref_into(), deposit.0) .map_err(|_| "seconder's balance too low")?; deposit.1.push(aux.ref_into().clone()); >::insert(proposal, deposit); @@ -183,7 +183,7 @@ impl Module { /// false would be a vote to keep the status quo.. fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result { ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum."); - ensure!(!>::balance(aux.ref_into()).is_zero(), + ensure!(!>::voting_balance(aux.ref_into()).is_zero(), "transactor must have balance to signal approval."); if !>::exists(&(ref_index, aux.ref_into().clone())) { let mut voters = Self::voters_for(ref_index); @@ -261,7 +261,7 @@ impl Module { if let Some((deposit, depositors)) = >::take(prop_index) {//: (T::Balance, Vec) = // refund depositors for d in &depositors { - >::refund(d, deposit); + >::unreserve(d, deposit); } >::put(public_props); Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove)?; @@ -366,6 +366,8 @@ mod tests { } } + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; @@ -389,6 +391,7 @@ mod tests { impl staking::Trait for Test { type Balance = u64; type DetermineContractAddress = staking::DummyContractAddressFor; + type AccountIndex = u64; } impl Trait for Test { type Proposal = Proposal; @@ -413,6 +416,11 @@ mod tests { bonding_duration: 3, transaction_base_fee: 0, transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, }.build_externalities()); t.extend(GenesisConfig::{ launch_period: 1, @@ -488,9 +496,9 @@ mod tests { assert_ok!(Democracy::second(&5, 0)); assert_ok!(Democracy::second(&5, 0)); assert_ok!(Democracy::second(&5, 0)); - assert_eq!(Staking::balance(&1), 5); - assert_eq!(Staking::balance(&2), 15); - assert_eq!(Staking::balance(&5), 35); + assert_eq!(Staking::free_balance(&1), 5); + assert_eq!(Staking::free_balance(&2), 15); + assert_eq!(Staking::free_balance(&5), 35); }); } @@ -504,9 +512,9 @@ mod tests { assert_ok!(Democracy::second(&5, 0)); assert_ok!(Democracy::second(&5, 0)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - assert_eq!(Staking::balance(&1), 10); - assert_eq!(Staking::balance(&2), 20); - assert_eq!(Staking::balance(&5), 50); + assert_eq!(Staking::free_balance(&1), 10); + assert_eq!(Staking::free_balance(&2), 20); + assert_eq!(Staking::free_balance(&5), 50); }); } diff --git a/substrate/runtime/executive/Cargo.toml b/substrate/runtime/executive/Cargo.toml index 5b1280c2490fa..64e2f89d06eed 100644 --- a/substrate/runtime/executive/Cargo.toml +++ b/substrate/runtime/executive/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Parity Technologies "] [dependencies] hex-literal = "0.1.0" serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } 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 } @@ -25,6 +26,7 @@ std = [ "substrate-runtime-std/std", "substrate-runtime-support/std", "serde/std", + "serde_derive", "substrate-codec/std", "substrate-runtime-primitives/std", "substrate-runtime-io/std", diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs index f9079c3739f0b..4196260b29dde 100644 --- a/substrate/runtime/executive/src/lib.rs +++ b/substrate/runtime/executive/src/lib.rs @@ -20,6 +20,9 @@ #[cfg(feature = "std")] extern crate serde; +#[cfg(test)] +#[macro_use] +extern crate serde_derive; extern crate substrate_runtime_std as rstd; extern crate substrate_runtime_support as runtime_support; @@ -46,27 +49,45 @@ extern crate substrate_runtime_staking as staking; use rstd::prelude::*; use rstd::marker::PhantomData; +use rstd::result; use runtime_support::StorageValue; use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, Executable, - MakePayment, Hashing}; + MakePayment, Hashing, AuxLookup}; use codec::Slicable; use system::extrinsics_root; +use primitives::{ApplyOutcome, ApplyError}; + +mod internal { + pub enum ApplyError { + BadSignature(&'static str), + Stale, + Future, + CantPay, + } + + pub enum ApplyOutcome { + Success, + Fail(&'static str), + } +} pub struct Executive< System, Block, + Lookup, Payment, Finalisation, ->(PhantomData<(System, Block, Payment, Finalisation)>); +>(PhantomData<(System, Block, Lookup, Payment, Finalisation)>); impl< System: system::Trait, - Block: traits::Block
, + Block: traits::Block, + Lookup: AuxLookup::Address, Target=System::AccountId>, Payment: MakePayment, Finalisation: Executable, -> Executive where - Block::Extrinsic: Checkable + Slicable, - ::Checked: Applyable +> Executive where + Block::Extrinsic: Checkable + Slicable, + ::Checked: Applyable { /// Start the execution of a particular block. pub fn initialise_block(header: &System::Header) { @@ -120,45 +141,59 @@ impl< /// Apply extrinsic outside of the block execution function. /// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt /// hashes. - pub fn apply_extrinsic(uxt: Block::Extrinsic) { + pub fn apply_extrinsic(uxt: Block::Extrinsic) -> result::Result { let encoded = uxt.encode(); let encoded_len = encoded.len(); >::note_extrinsic(encoded); - Self::apply_extrinsic_no_note_with_len(uxt, encoded_len); + match Self::apply_extrinsic_no_note_with_len(uxt, encoded_len) { + Ok(internal::ApplyOutcome::Success) => Ok(ApplyOutcome::Success), + Ok(internal::ApplyOutcome::Fail(_)) => Ok(ApplyOutcome::Fail), + Err(internal::ApplyError::CantPay) => Err(ApplyError::CantPay), + Err(internal::ApplyError::BadSignature(_)) => Err(ApplyError::BadSignature), + Err(internal::ApplyError::Stale) => Err(ApplyError::Stale), + Err(internal::ApplyError::Future) => Err(ApplyError::Future), + } } /// Apply an extrinsic inside the block execution function. fn apply_extrinsic_no_note(uxt: Block::Extrinsic) { let l = uxt.encode().len(); - Self::apply_extrinsic_no_note_with_len(uxt, l); + match Self::apply_extrinsic_no_note_with_len(uxt, l) { + Ok(internal::ApplyOutcome::Success) => (), + Ok(internal::ApplyOutcome::Fail(e)) => runtime_io::print(e), + Err(internal::ApplyError::CantPay) => panic!("All extrinsics should have sender able to pay their fees"), + Err(internal::ApplyError::BadSignature(_)) => panic!("All extrinsics should be properly signed"), + Err(internal::ApplyError::Stale) | Err(internal::ApplyError::Future) => panic!("All extrinsics should have the correct nonce"), + } } /// Actually apply an extrinsic given its `encoded_len`; this doesn't note its hash. - fn apply_extrinsic_no_note_with_len(uxt: Block::Extrinsic, encoded_len: usize) { + fn apply_extrinsic_no_note_with_len(uxt: Block::Extrinsic, encoded_len: usize) -> result::Result { // Verify the signature is good. - let xt = match uxt.check() { - Ok(xt) => xt, - Err(_) => panic!("All extrinsics should be properly signed"), - }; + let xt = uxt.check(Lookup::lookup).map_err(internal::ApplyError::BadSignature)?; if xt.sender() != &Default::default() { // check index - let expected_index = >::account_index(xt.sender()); - assert!(xt.index() == &expected_index, "All extrinsics should have the correct nonce"); + let expected_index = >::account_nonce(xt.sender()); + if xt.index() != &expected_index { return Err( + if xt.index() < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future } + ) } // pay any fees. - assert!(Payment::make_payment(xt.sender(), encoded_len), "All extrinsics should have sender able to pay their fees"); + Payment::make_payment(xt.sender(), encoded_len).map_err(|_| internal::ApplyError::CantPay)?; // AUDIT: Under no circumstances may this function panic from here onwards. // increment nonce in storage - >::inc_account_index(xt.sender()); + >::inc_account_nonce(xt.sender()); } // decode parameters and dispatch - xt.apply(); + let r = xt.apply(); >::put(>::get() + 1u32); + + r.map(|_| internal::ApplyOutcome::Success).or_else(|e| Ok(internal::ApplyOutcome::Fail(e))) } fn final_checks(header: &System::Header) { @@ -182,9 +217,20 @@ mod tests { use runtime_io::with_externalities; use substrate_primitives::H256; use primitives::BuildExternalities; - use primitives::traits::{HasPublicAux, Identity, Header as HeaderT, BlakeTwo256}; + use primitives::traits::{HasPublicAux, Identity, Header as HeaderT, BlakeTwo256, AuxLookup}; use primitives::testing::{Digest, Header, Block}; + struct NullLookup; + impl AuxLookup for NullLookup { + type Source = u64; + type Target = u64; + fn lookup(s: Self::Source) -> Result { + Ok(s) + } + } + + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; @@ -208,10 +254,11 @@ mod tests { impl staking::Trait for Test { type Balance = u64; type DetermineContractAddress = staking::DummyContractAddressFor; + type AccountIndex = u64; } type TestXt = primitives::testing::TestXt>; - type Executive = super::Executive, staking::Module, (session::Module, staking::Module)>; + type Executive = super::Executive, NullLookup, staking::Module, (session::Module, staking::Module)>; #[test] fn staking_balance_transfer_dispatch_works() { @@ -225,13 +272,18 @@ mod tests { bonding_duration: 0, transaction_base_fee: 10, transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, }.build_externalities()); - let xt = primitives::testing::TestXt((1, 0, Call::transfer(2, 69))); + let xt = primitives::testing::TestXt((1, 0, Call::transfer(2.into(), 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!(>::balance(&1), 32); - assert_eq!(>::balance(&2), 69); + Executive::apply_extrinsic(xt).unwrap(); + assert_eq!(>::voting_balance(&1), 32); + assert_eq!(>::voting_balance(&2), 69); }); } @@ -250,7 +302,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("1d43ef0fcabb78d925093fe22e50cc9ca5d182d189a3407c778e5fca714177dd").into(), + state_root: hex!("4fd406d2d62a841f7e2f956b52ce9ed98111c9eb6b3a9051aa4667b470030832").into(), extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), digest: Digest { logs: vec![], }, }, @@ -284,7 +336,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("1d43ef0fcabb78d925093fe22e50cc9ca5d182d189a3407c778e5fca714177dd").into(), + state_root: hex!("4fd406d2d62a841f7e2f956b52ce9ed98111c9eb6b3a9051aa4667b470030832").into(), extrinsics_root: [0u8; 32].into(), digest: Digest { logs: vec![], }, }, diff --git a/substrate/runtime/primitives/src/generic.rs b/substrate/runtime/primitives/src/generic.rs index f2b33ae94ec3f..ba11ab310a360 100644 --- a/substrate/runtime/primitives/src/generic.rs +++ b/substrate/runtime/primitives/src/generic.rs @@ -29,22 +29,22 @@ use traits::{self, Member, SimpleArithmetic, SimpleBitOps, MaybeDisplay, Block a Header as HeaderT, Hashing as HashingT}; use rstd::ops; -/// A vetted and verified extrinsic from the external world. +/// Definition of something that the external world might want to say. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -pub struct Extrinsic { +pub struct Extrinsic { /// Who signed it (note this is not a signature). - pub signed: AccountId, + pub signed: Address, /// The number of extrinsics have come before from the same signer. pub index: Index, /// The function that should be called. pub function: Call, } -impl Slicable for Extrinsic where - AccountId: Member + Slicable + MaybeDisplay, - Index: Member + Slicable + MaybeDisplay + SimpleArithmetic, - Call: Member + Slicable +impl Slicable for Extrinsic where + Address: Member + Slicable + MaybeDisplay, + Index: Member + Slicable + MaybeDisplay + SimpleArithmetic, + Call: Member + Slicable { fn decode(input: &mut I) -> Option { Some(Extrinsic { @@ -65,53 +65,83 @@ impl Slicable for Extrinsic wher } } -/// A extrinsics right from the external world. Unchecked. +/// A extrinsic right from the external world. Unchecked. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct UncheckedExtrinsic { +pub struct UncheckedExtrinsic { /// The actual extrinsic information. - pub extrinsic: Extrinsic, - /// The signature; should be an Ed25519 signature applied to the serialised `extrinsic` field. + pub extrinsic: Extrinsic, + /// The signature. pub signature: Signature, } -impl traits::Checkable for UncheckedExtrinsic where - AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member, - Signature: Member + traits::Verify, - Extrinsic: Slicable, -{ - type Checked = CheckedExtrinsic; - - fn check(self) -> Result { - if ::verify_encoded_lazy(&self.signature, &self.extrinsic, &self.extrinsic.signed) { - Ok(CheckedExtrinsic(self)) - } else { - Err(self) +impl UncheckedExtrinsic { + /// New instance. + pub fn new(extrinsic: Extrinsic, signature: Signature) -> Self { + UncheckedExtrinsic { + extrinsic, + signature, } } } -impl UncheckedExtrinsic> where - AccountId: Member + Default + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member, - Signature: Member + Default + traits::Verify, - Extrinsic: Slicable, +impl UncheckedExtrinsic> where + Signature: traits::Verify + Default + Eq, + AccountId: Default + Eq, { - /// Is this extrinsic signed? + /// `true` if this extrinsic is signed. pub fn is_signed(&self) -> bool { - self.signature.is_signed(&self.extrinsic.signed) + self.signature.is_signed() } } -impl Slicable for UncheckedExtrinsic where - AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member, - Signature: Member + Slicable, +impl traits::Checkable + for UncheckedExtrinsic> +where + Address: Member + Default + MaybeDisplay, + Index: Member + MaybeDisplay + SimpleArithmetic, + Call: Member, + Signature: traits::Verify + Eq + Default, + AccountId: Member + Default + MaybeDisplay, + ::MaybeUnsigned: Member, Extrinsic: Slicable, +{ + type Address = Address; + type AccountId = AccountId; + type Checked = CheckedExtrinsic; + + fn sender(&self) -> &Address { + &self.extrinsic.signed + } + + fn check(self, lookup: ThisLookup) -> Result where + ThisLookup: FnOnce(Address) -> Result + Send + Sync, + { + if !self.is_signed() { + Ok(CheckedExtrinsic(Extrinsic { + signed: Default::default(), + index: self.extrinsic.index, + function: self.extrinsic.function, + })) + } else { + let extrinsic: Extrinsic + = Extrinsic { + signed: lookup(self.extrinsic.signed)?, + index: self.extrinsic.index, + function: self.extrinsic.function, + }; + if ::verify_encoded_lazy(&self.signature, &extrinsic, &extrinsic.signed) { + Ok(CheckedExtrinsic(extrinsic)) + } else { + Err("bad signature in extrinsic") + } + } + } +} + +impl Slicable for UncheckedExtrinsic where + Signature: Slicable, + Extrinsic: Slicable, { fn decode(input: &mut I) -> Option { // This is a little more complicated than usual since the binary format must be compatible @@ -120,10 +150,10 @@ impl Slicable for UncheckedExtrinsic Vec { @@ -133,9 +163,6 @@ impl Slicable for UncheckedExtrinsic. 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.extrinsic.using_encoded(|s| v.extend(s)); self.signature.using_encoded(|s| v.extend(s)); @@ -147,11 +174,12 @@ impl Slicable for UncheckedExtrinsic fmt::Debug for UncheckedExtrinsic where - AccountId: fmt::Debug, - Index: fmt::Debug, - Call: fmt::Debug, +impl fmt::Debug for UncheckedExtrinsic where + Address: fmt::Debug, + Index: fmt::Debug, + Call: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UncheckedExtrinsic({:?})", self.extrinsic) @@ -161,69 +189,44 @@ impl fmt::Debug for UncheckedExtrinsic - (UncheckedExtrinsic); - -impl CheckedExtrinsic -where - AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member, - Signature: Member -{ - /// Get a reference to the checked signature. - pub fn signature(&self) -> &Signature { - &self.0.signature - } - - /// Get a reference to the checked signature. - pub fn as_unchecked(&self) -> &UncheckedExtrinsic { - &self.0 - } +pub struct CheckedExtrinsic + (Extrinsic); - /// Get a reference to the checked signature. - pub fn into_unchecked(self) -> UncheckedExtrinsic { - self.0 - } -} - -impl ops::Deref - for CheckedExtrinsic +impl ops::Deref + for CheckedExtrinsic where - AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member, - Signature: Member + AccountId: Member + MaybeDisplay, + Index: Member + MaybeDisplay + SimpleArithmetic, + Call: Member, { type Target = Extrinsic; fn deref(&self) -> &Self::Target { - &self.0.extrinsic + &self.0 } } -impl traits::Applyable - for CheckedExtrinsic +impl traits::Applyable + for CheckedExtrinsic where - AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member + AuxDispatchable, - Signature: Member + AccountId: Member + MaybeDisplay, + Index: Member + MaybeDisplay + SimpleArithmetic, + Call: Member + AuxDispatchable, { type Index = Index; type AccountId = AccountId; fn index(&self) -> &Self::Index { - &self.0.extrinsic.index + &self.0.index } fn sender(&self) -> &Self::AccountId { - &self.0.extrinsic.signed + &self.0.signed } - fn apply(self) { - let xt = self.0.extrinsic; - xt.function.dispatch(&xt.signed); + fn apply(self) -> Result<(), &'static str> { + let xt = self.0; + xt.function.dispatch(&xt.signed) } } @@ -234,7 +237,7 @@ pub struct Digest { } impl Slicable for Digest where - Item: Member + Default + Slicable + Item: Member + Default + Slicable { fn decode(input: &mut I) -> Option { Some(Digest { logs: Slicable::decode(input)? }) @@ -244,7 +247,7 @@ impl Slicable for Digest where } } impl traits::Digest for Digest where - Item: Member + Default + Slicable + Item: Member + Default + Slicable { type Item = Item; fn push(&mut self, item: Self::Item) { @@ -338,7 +341,7 @@ impl Slicable for Header traits::Header for Header where - Number: Member + ::rstd::hash::Hash + Copy + Slicable + MaybeDisplay + SimpleArithmetic + Slicable, + Number: Member + ::rstd::hash::Hash + Copy + Slicable + MaybeDisplay + SimpleArithmetic + Slicable, Hashing: HashingT, DigestItem: Member + Default + Slicable, Hashing::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Slicable, @@ -377,7 +380,7 @@ impl traits::Header for Header Header where - Number: Member + ::rstd::hash::Hash + Copy + Slicable + MaybeDisplay + SimpleArithmetic + Slicable, + Number: Member + ::rstd::hash::Hash + Copy + Slicable + MaybeDisplay + SimpleArithmetic + Slicable, Hashing: HashingT, DigestItem: Member + Default + Slicable, Hashing::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Slicable, @@ -453,7 +456,7 @@ impl Slicable for Block traits::Block for Block where Header: HeaderT, - Extrinsic: Member + Slicable, + Extrinsic: Member + Slicable, { type Extrinsic = Extrinsic; type Header = Header; @@ -495,22 +498,22 @@ mod tests { digest: Digest { logs: vec![vec![1, 2, 3], vec![4, 5, 6]] }, }, extrinsics: vec![ - UncheckedExtrinsic { - signature: H512::from([0u8; 64]).into(), - extrinsic: Extrinsic { + UncheckedExtrinsic::new( + Extrinsic { signed: [255u8; 32].into(), index: 0, function: 100, - } - }, - UncheckedExtrinsic { - signature: H512::from([255u8; 64]).into(), - extrinsic: Extrinsic { + }, + H512::from([0u8; 64]).into() + ), + UncheckedExtrinsic::new( + Extrinsic { signed: [128u8; 32].into(), index: 100, function: 99, - } - }, + }, + H512::from([255u8; 64]).into() + ) ] }; @@ -520,7 +523,6 @@ mod tests { assert_eq!(block, decoded); } - { let encoded = block.encode(); let decoded = Block::decode(&mut &encoded[..]).unwrap(); diff --git a/substrate/runtime/primitives/src/lib.rs b/substrate/runtime/primitives/src/lib.rs index e62f508f9dc1b..3c344b75f9813 100644 --- a/substrate/runtime/primitives/src/lib.rs +++ b/substrate/runtime/primitives/src/lib.rs @@ -83,6 +83,61 @@ impl From for Ed25519Signature { } } +#[derive(Eq, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[repr(u8)] +/// Outcome of a valid extrinsic application. Capable of being sliced. +pub enum ApplyOutcome { + /// Successful application (extrinsic reported no issue). + Success = 0, + /// Failed application (extrinsic was probably a no-op other than fees). + Fail = 1, +} +impl codec::Slicable for ApplyOutcome { + fn decode(input: &mut I) -> Option { + match input.read_byte()? { + x if x == ApplyOutcome::Success as u8 => Some(ApplyOutcome::Success), + x if x == ApplyOutcome::Fail as u8 => Some(ApplyOutcome::Fail), + _ => None, + } + } + fn using_encoded R>(&self, f: F) -> R { + f(&[*self as u8]) + } +} + +#[derive(Eq, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[repr(u8)] +/// Reason why an extrinsic couldn't be applied (i.e. invalid extrinsic). +pub enum ApplyError { + /// Bad signature. + BadSignature = 0, + /// Nonce too low. + Stale = 1, + /// Nonce too high. + Future = 2, + /// Sending account had too low a balance. + CantPay = 3, +} +impl codec::Slicable for ApplyError { + fn decode(input: &mut I) -> Option { + match input.read_byte()? { + x if x == ApplyError::BadSignature as u8 => Some(ApplyError::BadSignature), + x if x == ApplyError::Stale as u8 => Some(ApplyError::Stale), + x if x == ApplyError::Future as u8 => Some(ApplyError::Future), + x if x == ApplyError::CantPay as u8 => Some(ApplyError::CantPay), + _ => None, + } + } + fn using_encoded R>(&self, f: F) -> R { + f(&[*self as u8]) + } +} + +/// Result from attempt to apply an extrinsic. +pub type ApplyResult = Result; + /// Potentially "unsigned" signature verification. #[derive(Eq, PartialEq, Clone, Default)] #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] @@ -92,8 +147,12 @@ impl MaybeUnsigned where T: Default + Eq, ::Signer: Default + Eq, { - fn is_signed(&self, signer: &::Signer) -> bool { - self.0 != T::default() || signer != &::Signer::default() + fn is_signed(&self) -> bool { + self.0 != T::default() + } + + fn is_addressed(&self, signer: &::Signer) -> bool { + signer != &Default::default() } } @@ -103,8 +162,8 @@ impl Verify for MaybeUnsigned where { type Signer = T::Signer; fn verify>(&self, msg: L, signer: &Self::Signer) -> bool { - if !self.is_signed(signer) { - true + if !self.is_signed() { + !self.is_addressed(signer) } else { self.0.verify(msg, signer) } diff --git a/substrate/runtime/primitives/src/testing.rs b/substrate/runtime/primitives/src/testing.rs index 27e675b04fe80..a3d58d12e73cb 100644 --- a/substrate/runtime/primitives/src/testing.rs +++ b/substrate/runtime/primitives/src/testing.rs @@ -159,12 +159,15 @@ impl Checkable for TestXt { type Checked = Self; - fn check(self) -> Result { Ok(self) } + type Address = u64; + type AccountId = u64; + fn sender(&self) -> &u64 { &(self.0).0 } + fn check Result + Send + Sync>(self, _lookup: ThisLookup) -> Result { Ok(self) } } impl + Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Applyable for TestXt { 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); } + fn apply(self) -> Result<(), &'static str> { (self.0).2.dispatch(&(self.0).0) } } diff --git a/substrate/runtime/primitives/src/traits.rs b/substrate/runtime/primitives/src/traits.rs index 43639df61e796..c204b5ebda049 100644 --- a/substrate/runtime/primitives/src/traits.rs +++ b/substrate/runtime/primitives/src/traits.rs @@ -17,7 +17,7 @@ //! Primitives for the runtime modules. use rstd::prelude::*; -use rstd; +use rstd::{self, result}; use runtime_io; #[cfg(feature = "std")] use std::fmt::{Debug, Display}; #[cfg(feature = "std")] use serde::{Serialize, de::DeserializeOwned}; @@ -40,15 +40,25 @@ pub trait Verify { fn verify>(&self, msg: L, signer: &Self::Signer) -> bool; } +/// Means of changing one type into another in a manner dependent on the source type. +pub trait AuxLookup { + /// Type to lookup from. + type Source; + /// Type to lookup into. + type Target; + /// Attempt a lookup. + fn lookup(s: Self::Source) -> result::Result; +} + /// Simple payment making trait, operating on a single generic `AccountId` type. pub trait MakePayment { /// Make some sort of payment concerning `who` for an extrinsic (transaction) of encoded length /// `encoded_len` bytes. Return true iff the payment was successful. - fn make_payment(who: &AccountId, encoded_len: usize) -> bool; + fn make_payment(who: &AccountId, encoded_len: usize) -> Result<(), &'static str>; } impl MakePayment for () { - fn make_payment(_: &T, _: usize) -> bool { true } + fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } } /// Extensible conversion trait. Generic over both source and destination types. @@ -259,6 +269,16 @@ impl CheckEqual for substrate_primitives::H256 { } } +#[cfg(feature = "std")] +pub trait MaybeSerializeDebugButNotDeserialize: Serialize + Debug {} +#[cfg(feature = "std")] +impl MaybeSerializeDebugButNotDeserialize for T {} + +#[cfg(not(feature = "std"))] +pub trait MaybeSerializeDebugButNotDeserialize {} +#[cfg(not(feature = "std"))] +impl MaybeSerializeDebugButNotDeserialize for T {} + #[cfg(feature = "std")] pub trait MaybeSerializeDebug: Serialize + DeserializeOwned + Debug {} #[cfg(feature = "std")] @@ -352,8 +372,30 @@ pub type HashingFor = <::Header as Header>::Hashing; /// 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 + Send + Sync { + type Address: Member + MaybeDisplay; + type AccountId: Member + MaybeDisplay; type Checked: Member; - fn check(self) -> Result; + fn sender(&self) -> &Self::Address; + fn check Result + Send + Sync>(self, lookup: ThisLookup) -> Result; +} + +/// 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. +/// +/// This does that checking without requiring a lookup argument. +pub trait BlindCheckable: Sized + Send + Sync { + type Address: Member + MaybeDisplay; + type Checked: Member; + fn sender(&self) -> &Self::Address; + fn check(self) -> Result; +} + +impl Checkable for T { + type Address = ::Address; + type AccountId = ::Address; + type Checked = ::Checked; + fn sender(&self) -> &Self::Address { BlindCheckable::sender(self) } + fn check Result + Send + Sync>(self, _: ThisLookup) -> Result { BlindCheckable::check(self) } } /// An "executable" piece of information, used by the standard Substrate Executive in order to @@ -367,5 +409,5 @@ pub trait Applyable: Sized + Send + Sync { type Index: Member + MaybeDisplay + SimpleArithmetic; fn index(&self) -> &Self::Index; fn sender(&self) -> &Self::AccountId; - fn apply(self); + fn apply(self) -> Result<(), &'static str>; } diff --git a/substrate/runtime/session/src/lib.rs b/substrate/runtime/session/src/lib.rs index 5d95f28c539c1..14d78b8e94763 100644 --- a/substrate/runtime/session/src/lib.rs +++ b/substrate/runtime/session/src/lib.rs @@ -208,6 +208,7 @@ mod tests { use primitives::traits::{HasPublicAux, Identity, BlakeTwo256}; use primitives::testing::{Digest, Header}; + #[derive(Clone, Eq, PartialEq)] pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; diff --git a/substrate/runtime/staking/src/address.rs b/substrate/runtime/staking/src/address.rs new file mode 100644 index 0000000000000..dc0769e4ad124 --- /dev/null +++ b/substrate/runtime/staking/src/address.rs @@ -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 . + +//! Address type that is union of index and id for an account. + +use rstd::prelude::*; +#[cfg(feature = "std")] +use std::fmt; +use super::{Member, Slicable, As, Input}; + +/// A vetted and verified extrinsic from the external world. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash))] +pub enum Address where + AccountId: Member, + AccountIndex: Member, +{ + /// It's an account ID (pubkey). + #[cfg_attr(feature = "std", serde(deserialize_with="AccountId::deserialize"))] + Id(AccountId), + /// It's an account index. + #[cfg_attr(feature = "std", serde(deserialize_with="AccountIndex::deserialize"))] + Index(AccountIndex), +} + +#[cfg(feature = "std")] +impl fmt::Display for Address where + AccountId: Member, + AccountIndex: Member, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{:?}", self) + } +} + +impl From for Address where + AccountId: Member, + AccountIndex: Member, +{ + fn from(a: AccountId) -> Self { + Address::Id(a) + } +} + +fn need_more_than(a: T, b: T) -> Option { + if a < b { Some(a) } else { None } +} + +impl Slicable for Address where + AccountId: Member + Slicable, + AccountIndex: Member + Slicable + PartialOrd + Ord + As + As + As + Copy, +{ + fn decode(input: &mut I) -> Option { + Some(match input.read_byte()? { + x @ 0x00...0xef => Address::Index(As::sa(x)), + 0xfc => Address::Index(As::sa(need_more_than(0xef, u16::decode(input)?)?)), + 0xfd => Address::Index(As::sa(need_more_than(0xffff, u32::decode(input)?)?)), + 0xfe => Address::Index(need_more_than(As::sa(0xffffffffu32), Slicable::decode(input)?)?), + 0xff => Address::Id(Slicable::decode(input)?), + _ => return None, + }) + } + + fn encode(&self) -> Vec { + let mut v = Vec::new(); + + match *self { + Address::Id(ref i) => { + v.push(255); + i.using_encoded(|s| v.extend(s)); + } + Address::Index(i) if i > As::sa(0xffffffffu32) => { + v.push(254); + i.using_encoded(|s| v.extend(s)); + } + Address::Index(i) if i > As::sa(0xffffu32) => { + v.push(253); + As::::as_(i).using_encoded(|s| v.extend(s)); + } + Address::Index(i) if i >= As::sa(0xf0u32) => { + v.push(252); + As::::as_(i).using_encoded(|s| v.extend(s)); + } + Address::Index(i) => v.push(As::::as_(i)), + } + + v + } +} + +impl Default for Address where + AccountId: Member + Default, + AccountIndex: Member, +{ + fn default() -> Self { + Address::Id(Default::default()) + } +} diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index d1a646c4f7648..38d8df224d0a4 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -49,10 +49,26 @@ use rstd::prelude::*; use rstd::{cmp, result}; use rstd::cell::RefCell; use rstd::collections::btree_map::{BTreeMap, Entry}; -use codec::Slicable; +use codec::{Input, Slicable}; use runtime_support::{StorageValue, StorageMap, Parameter}; use runtime_support::dispatch::Result; -use primitives::traits::{Zero, One, As, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, Hashing as HashingT}; +use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, + As, AuxLookup, Hashing as HashingT, Member}; +use address::Address as RawAddress; + +pub mod address; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +/// Number of account IDs stored per enum set. +const ENUM_SET_SIZE: usize = 64; + +/// The byte to identify intention to reclaim an existing account index. +const RECLAIM_INDEX_MAGIC: usize = 0x69; + +pub type Address = RawAddress<::AccountId, ::AccountIndex>; #[cfg(test)] #[derive(Debug, PartialEq, Clone)] @@ -88,8 +104,12 @@ impl ContractAddressFor for Hashing where pub trait Trait: system::Trait + session::Trait { /// The balance of an account. - type Balance: Parameter + SimpleArithmetic + Slicable + Default + Copy; + type Balance: Parameter + SimpleArithmetic + Slicable + Default + Copy + As + As; + /// Function type to get the contract address given the creator. type DetermineContractAddress: ContractAddressFor; + /// Type used for storing an account's index; implies the maximum number of accounts the system + /// can hold. + type AccountIndex: Parameter + Member + Slicable + SimpleArithmetic + As + As + As + As + Copy; } decl_module! { @@ -97,7 +117,7 @@ decl_module! { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Call where aux: T::PublicAux { - fn transfer(aux, dest: T::AccountId, value: T::Balance) -> Result = 0; + fn transfer(aux, dest: RawAddress, value: T::Balance) -> Result = 0; fn stake(aux) -> Result = 1; fn unstake(aux) -> Result = 2; } @@ -126,6 +146,16 @@ decl_storage! { pub TransactionBaseFee get(transaction_base_fee): b"sta:basefee" => required T::Balance; // The fee to be paid for making a transaction; the per-byte portion. pub TransactionByteFee get(transaction_byte_fee): b"sta:bytefee" => required T::Balance; + // The minimum amount allowed to keep an account open. + pub ExistentialDeposit get(existential_deposit): b"sta:existential_deposit" => required T::Balance; + // The amount credited to a destination's account whose index was reclaimed. + pub ReclaimRebate get(reclaim_rebate): b"sta:reclaim_rebate" => required T::Balance; + // The fee required to make a transfer. + pub TransferFee get(transfer_fee): b"sta:transfer_fee" => required T::Balance; + // The fee required to create an account. At least as big as ReclaimRebate. + pub CreationFee get(creation_fee): b"sta:creation_fee" => required T::Balance; + // The fee required to create a contract. At least as big as ReclaimRebate. + pub ContractFee get(contract_fee): b"sta:contract_fee" => required T::Balance; // The current era index. pub CurrentEra get(current_era): b"sta:era" => required T::BlockNumber; @@ -136,11 +166,34 @@ decl_storage! { // The block number at which the era length last changed. pub LastEraLengthChange get(last_era_length_change): b"sta:lec" => default T::BlockNumber; - // The balance of a given account. + // The next free enumeration set. + pub NextEnumSet get(next_enum_set): b"sta:next_enum" => required T::AccountIndex; + // The enumeration sets. + pub EnumSet get(enum_set): b"sta:enum_set" => default map [ T::AccountIndex => Vec ]; + + // The "free" balance of a given account. + // + // This is the only balance that matters in terms of most operations on tokens. It is + // alone used to determine the balance when in the contract execution environment. When this + // balance falls below the value of `ExistentialDeposit`, then the "current account" is + // deleted: specifically, `Bondage`, `StorageOf`, `CodeOf` and `FreeBalance`. + // + // `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets + // collapsed to zero if it ever becomes less than `ExistentialDeposit`. pub FreeBalance get(free_balance): b"sta:bal:" => default map [ T::AccountId => T::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. + // + // This balance is a "reserve" balance that other subsystems use in order to set aside tokens + // that are still "owned" by the account holder, but which are unspendable. This is different + // and wholly unrelated to the `Bondage` system used for staking. + // + // When this balance falls below the value of `ExistentialDeposit`, then this "reserve account" + // is deleted: specifically, `ReservedBalance`. + // + // `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets + // collapsed to zero if it ever becomes less than `ExistentialDeposit`. pub ReservedBalance get(reserved_balance): b"sta:lbo:" => default map [ T::AccountId => T::Balance ]; // The block at which the `who`'s funds become entirely liquid. @@ -150,7 +203,20 @@ decl_storage! { pub CodeOf: b"sta:cod:" => default map [ T::AccountId => Vec ]; // TODO Vec values should be optimised to not do a length prefix. // The storage items associated with an account/key. - pub StorageOf: b"sta:sto:" => map [ (T::AccountId, Vec) => Vec ]; // TODO: keys should also be able to take AsRef to ensure Vecs can be passed as &[u8] + // TODO: keys should also be able to take AsRef to ensure Vecs can be passed as &[u8] + // TODO: This will need to be stored as a double-map, with `T::AccountId` using the usual XX hash + // function, and then the output of this concatenated onto a separate blake2 hash of the `Vec` + // key. We will then need a `remove_prefix` in addition to `set_storage` which removes all + // storage items with a particular prefix otherwise we'll suffer leakage with the removal + // of smart contracts. +// pub StorageOf: b"sta:sto:" => map [ T::AccountId => map(blake2) Vec => Vec ]; + pub StorageOf: b"sta:sto:" => map [ (T::AccountId, Vec) => Vec ]; +} + +enum NewAccountOutcome { + NoHint, + GoodHint, + BadHint, } impl Module { @@ -163,19 +229,19 @@ impl Module { } /// The combined balance of `who`. - pub fn balance(who: &T::AccountId) -> T::Balance { + pub fn voting_balance(who: &T::AccountId) -> T::Balance { Self::free_balance(who) + Self::reserved_balance(who) } /// Some result as `slash(who, value)` (but without the side-effects) assuming there are no - /// balance changes in the meantime. + /// balance changes in the meantime and only the reserved balance is not taken into account. pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool { - Self::balance(who) >= value + Self::free_balance(who) >= value } - /// Same result as `deduct_unbonded(who, value)` (but without the side-effects) assuming there + /// Same result as `reserve(who, value)` (but without the side-effects) assuming there /// are no balance changes in the meantime. - pub fn can_deduct_unbonded(who: &T::AccountId, value: T::Balance) -> bool { + pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool { if let LockStatus::Liquid = Self::unlock_block(who) { Self::free_balance(who) >= value } else { @@ -205,7 +271,8 @@ impl Module { /// Transfer some unlocked staking balance to another staker. /// TODO: probably want to state gas-limit and gas-price. - fn transfer(aux: &T::PublicAux, dest: T::AccountId, value: T::Balance) -> Result { + fn transfer(aux: &T::PublicAux, dest: Address, value: T::Balance) -> Result { + let dest = Self::lookup(dest)?; // commit anything that made it this far to storage if let Some(commit) = Self::effect_transfer(aux.ref_into(), &dest, value, &DirectAccountDb)? { >::merge(&mut DirectAccountDb, commit); @@ -267,77 +334,122 @@ impl Module { // PUBLIC MUTABLES (DANGEROUS) - /// Deduct from an unbonded balance. true if it happened. - pub fn deduct_unbonded(who: &T::AccountId, value: T::Balance) -> Result { - if let LockStatus::Liquid = Self::unlock_block(who) { - let b = Self::free_balance(who); - if b >= value { - >::insert(who, b - value); - return Ok(()) - } + /// Set the free balance of an account to some new value. Will enforce ExistentialDeposit law, + /// anulling the account as needed. + pub fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> bool { + if balance < Self::existential_deposit() { + Self::on_reserved_too_low(who); + false + } else { + >::insert(who, balance); + true } - Err("not enough liquid funds") } - /// Refund some balance. - pub fn refund(who: &T::AccountId, value: T::Balance) { - >::insert(who, Self::free_balance(who) + value) + /// Set the free balance of an account to some new value. Will enforce ExistentialDeposit + /// law anulling the account as needed. + /// + /// Doesn't do any preparatory work for creating a new account, so should only be used when it + /// is known that the account already exists. + pub fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> bool { + // Commented out for no - but consider it instructive. +// assert!(!Self::voting_balance(who).is_zero()); + if balance < Self::existential_deposit() { + Self::on_free_too_low(who); + false + } else { + >::insert(who, balance); + true + } } - /// Will slash any balance, but prefer free over reserved. - pub fn slash(who: &T::AccountId, value: T::Balance) -> Result { + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the + /// free balance. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Some(remaining)` will be retutned. Full completion is given by `None`. + pub fn slash(who: &T::AccountId, value: T::Balance) -> Option { let free_balance = Self::free_balance(who); let free_slash = cmp::min(free_balance, value); - >::insert(who, &(free_balance - free_slash)); + Self::set_free_balance(who, free_balance - free_slash); if free_slash < value { Self::slash_reserved(who, value - free_slash) - .map_err(|_| "not enough funds") } else { - Ok(()) + None } } /// Moves `value` from balance to reserved balance. - pub fn reserve_balance(who: &T::AccountId, value: T::Balance) -> Result { + /// + /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will + /// be returned to notify of this. This is different behaviour to `unreserve`. + pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result { let b = Self::free_balance(who); if b < value { return Err("not enough free funds") } - >::insert(who, b - value); - >::insert(who, Self::reserved_balance(who) + value); + if Self::unlock_block(who) != LockStatus::Liquid { + return Err("free funds are still bonded") + } + Self::set_reserved_balance(who, Self::reserved_balance(who) + value); + Self::set_free_balance(who, b - value); Ok(()) } - /// Moves `value` from reserved balance to balance. - pub fn unreserve_balance(who: &T::AccountId, value: T::Balance) { + /// Moves up to `value` from reserved balance to balance. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Some(remaining)` will be retutned. Full completion is given by `None`. + /// NOTE: This is different to `reserve`. + pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option { let b = Self::reserved_balance(who); - let value = cmp::min(b, value); - >::insert(who, b - value); - >::insert(who, Self::free_balance(who) + value); + let actual = cmp::min(b, value); + Self::set_free_balance(who, Self::free_balance(who) + actual); + Self::set_reserved_balance(who, b - actual); + if actual == value { + None + } else { + Some(value - actual) + } } - /// Moves `value` from reserved balance to balance. - pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Result { + /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Some(remaining)` will be retutned. Full completion is given by `None`. + pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option { let b = Self::reserved_balance(who); let slash = cmp::min(b, value); - >::insert(who, b - slash); + Self::set_reserved_balance(who, b - slash); if value == slash { - Ok(()) + None } else { - Err("not enough (reserved) funds") + Some(value - slash) } } - /// Moves `value` from reserved balance to balance. - pub fn transfer_reserved_balance(slashed: &T::AccountId, beneficiary: &T::AccountId, value: T::Balance) -> Result { + /// Moves up to `value` from reserved balance of account `slashed` to free balance of account + /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be + /// returned. + /// + /// As much funds up to `value` will be moved as possible. If this is less than `value`, then + /// `Ok(Some(remaining))` will be retutned. Full completion is given by `Ok(None)`. + pub fn transfer_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: T::Balance + ) -> result::Result, &'static str> { + if Self::voting_balance(beneficiary).is_zero() { + return Err("beneficiary account must pre-exist"); + } let b = Self::reserved_balance(slashed); let slash = cmp::min(b, value); - >::insert(slashed, b - slash); - >::insert(beneficiary, Self::free_balance(beneficiary) + slash); + Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash); + Self::set_reserved_balance(slashed, b - slash); if value == slash { - Ok(()) + Ok(None) } else { - Err("not enough (reserved) funds") + Ok(Some(value - slash)) } } @@ -371,7 +483,7 @@ impl Module { // >::get() of them. let mut intentions = >::get() .into_iter() - .map(|v| (Self::balance(&v), v)) + .map(|v| (Self::voting_balance(&v), v)) .collect::>(); intentions.sort_unstable_by(|&(ref b1, _), &(ref b2, _)| b2.cmp(&b1)); >::set_validators( @@ -381,6 +493,114 @@ impl Module { .collect::>() ); } + + fn enum_set_size() -> T::AccountIndex { + T::AccountIndex::sa(ENUM_SET_SIZE) + } + + /// Lookup an T::AccountIndex to get an Id, if there's one there. + pub fn lookup_index(index: T::AccountIndex) -> Option { + let enum_set_size = Self::enum_set_size(); + let set = Self::enum_set(index / enum_set_size); + let i: usize = (index % enum_set_size).as_(); + set.get(i).map(|x| x.clone()) + } + + /// `true` if the account `index` is ready for reclaim. + pub fn can_reclaim(try_index: T::AccountIndex) -> bool { + let enum_set_size = Self::enum_set_size(); + let try_set = Self::enum_set(try_index / enum_set_size); + let i = (try_index % enum_set_size).as_(); + i < try_set.len() && Self::voting_balance(&try_set[i]).is_zero() + } + + /// Register a new account (with existential balance). + fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome { + let enum_set_size = Self::enum_set_size(); + let next_set_index = Self::next_enum_set(); + let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC); + let reclaim_index_modulus = T::AccountIndex::sa(256usize); + let quantization = T::AccountIndex::sa(256usize); + + // A little easter-egg for reclaiming dead indexes.. + let ret = { + // we quantise the number of accounts so it stays constant over a reasonable + // period of time. + let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization; + // then modify the starting balance to be modulo this to allow it to potentially + // identify an account index for reuse. + let maybe_try_index = balance % >::sa(quantized_account_count * reclaim_index_modulus); + let maybe_try_index = As::::as_(maybe_try_index); + + // this identifier must end with magic byte 0x69 to trigger this check (a minor + // optimisation to ensure we don't check most unintended account creations). + if maybe_try_index % reclaim_index_modulus == reclaim_index_magic { + // reuse is probably intended. first, remove magic byte. + let try_index = maybe_try_index / reclaim_index_modulus; + + // then check to see if this balance identifies a dead account index. + let set_index = try_index / enum_set_size; + let mut try_set = Self::enum_set(set_index); + let item_index = (try_index % enum_set_size).as_(); + if item_index < try_set.len() { + if Self::voting_balance(&try_set[item_index]).is_zero() { + // yup - this index refers to a dead account. can be reused. + try_set[item_index] = who.clone(); + >::insert(set_index, try_set); + + return NewAccountOutcome::GoodHint; + } + } + NewAccountOutcome::BadHint + } else { + NewAccountOutcome::NoHint + } + }; + + // insert normally as a back up + let mut set_index = next_set_index; + // defensive only: this loop should never iterate since we keep NextEnumSet up to date later. + let mut set = loop { + let set = Self::enum_set(set_index); + if set.len() < ENUM_SET_SIZE { + break set; + } + set_index += One::one(); + }; + + // update set. + set.push(who.clone()); + + // keep NextEnumSet up to date + if set.len() == ENUM_SET_SIZE { + >::put(set_index + One::one()); + } + + // write set. + >::insert(set_index, set); + + ret + } + + /// Kill an account's free portion. + fn on_free_too_low(who: &T::AccountId) { + >::remove(who); + >::remove(who); + >::remove(who); + // TODO: >::remove_prefix(address.clone()); + + if Self::reserved_balance(who).is_zero() { + >::remove(who); + } + } + + /// Kill an account's reserved portion. + fn on_reserved_too_low(who: &T::AccountId) { + >::remove(who); + if Self::free_balance(who).is_zero() { + >::remove(who); + } + } } impl Executable for Module { @@ -389,6 +609,17 @@ impl Executable for Module { } } +impl AuxLookup for Module { + type Source = address::Address; + type Target = T::AccountId; + fn lookup(a: Self::Source) -> result::Result { + match a { + address::Address::Id(i) => Ok(i), + address::Address::Index(i) => >::lookup_index(i).ok_or("invalid account index"), + } + } +} + // Each identity's stake may be in one of three bondage states, given by an integer: // - n | n <= >::get(): inactive: free to be transferred. // - ~0: active: currently representing a validator. @@ -413,6 +644,9 @@ impl Default for ChangeEntry { } impl ChangeEntry { + pub fn contract_created(b: T::Balance, c: Vec) -> Self { + ChangeEntry { balance: Some(b), code: Some(c), storage: Default::default() } + } pub fn balance_changed(b: T::Balance) -> Self { ChangeEntry { balance: Some(b), code: None, storage: Default::default() } } @@ -425,10 +659,6 @@ trait AccountDb { fn get_code(&self, account: &T::AccountId) -> Vec; fn get_balance(&self, account: &T::AccountId) -> T::Balance; - fn set_storage(&mut self, account: &T::AccountId, location: Vec, value: Option>); - fn set_code(&mut self, account: &T::AccountId, code: Vec); - fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance); - fn merge(&mut self, state: State); } @@ -443,23 +673,36 @@ impl AccountDb for DirectAccountDb { fn get_balance(&self, account: &T::AccountId) -> T::Balance { >::get(account) } - fn set_storage(&mut self, account: &T::AccountId, location: Vec, value: Option>) { - if let Some(value) = value { - >::insert(&(account.clone(), location), &value); - } else { - >::remove(&(account.clone(), location)); - } - } - fn set_code(&mut self, account: &T::AccountId, code: Vec) { - >::insert(account, &code); - } - fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance) { - >::insert(account, balance); - } fn merge(&mut self, s: State) { + let ed = >::existential_deposit(); for (address, changed) in s.into_iter() { if let Some(balance) = changed.balance { - >::insert(&address, balance); + // If the balance is too low, then the account is reaped. + // NOTE: There are two balances for every account: `reserved_balance` and + // `free_balance`. This contract subsystem only cares about the latter: whenever + // the term "balance" is used *here* it should be assumed to mean "free balance" + // in the rest of the module. + // Free balance can never be less than ED. If that happens, it gets reduced to zero + // and the account information relevant to this subsystem is deleted (i.e. the + // account is reaped). + // NOTE: This is orthogonal to the `Bondage` value that an account has, a high + // value of which makes even the `free_balance` unspendable. + // TODO: enforce this for the other balance-altering functions. + if balance < ed { + >::on_free_too_low(&address); + continue; + } else { + if !>::exists(&address) { + let outcome = >::new_account(&address, balance); + let credit = match outcome { + NewAccountOutcome::GoodHint => balance + >::reclaim_rebate(), + _ => balance, + }; + >::insert(&address, credit); + } else { + >::insert(&address, balance); + } + } } if let Some(code) = changed.code { >::insert(&address, &code); @@ -490,7 +733,24 @@ impl<'a, T: Trait> OverlayAccountDb<'a, T> { fn into_state(self) -> State { self.local.into_inner() } + + fn set_storage(&mut self, account: &T::AccountId, location: Vec, value: Option>) { + self.local + .borrow_mut() + .entry(account.clone()) + .or_insert(Default::default()) + .storage + .insert(location, value); + } + fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance) { + self.local + .borrow_mut() + .entry(account.clone()) + .or_insert(Default::default()) + .balance = Some(balance); + } } + impl<'a, T: Trait> AccountDb for OverlayAccountDb<'a, T> { fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option> { self.local @@ -514,28 +774,6 @@ impl<'a, T: Trait> AccountDb for OverlayAccountDb<'a, T> { .and_then(|a| a.balance) .unwrap_or_else(|| self.underlying.get_balance(account)) } - fn set_storage(&mut self, account: &T::AccountId, location: Vec, value: Option>) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .storage - .insert(location, value); - } - fn set_code(&mut self, account: &T::AccountId, code: Vec) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .code = Some(code); - } - fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .balance = Some(balance); - } fn merge(&mut self, s: State) { let mut local = self.local.borrow_mut(); @@ -567,10 +805,18 @@ impl Module { account_db: &DB, ) -> result::Result>, &'static str> { let from_balance = account_db.get_balance(transactor); - // TODO: a fee. - if from_balance < value { + + let liability = value + Self::contract_fee(); + + if from_balance < liability { return Err("balance too low to send value"); } + if value < Self::existential_deposit() { + return Err("value too low to create account"); + } + if >::get(transactor) > >::block_number() { + return Err("bondage too high to send value"); + } let dest = T::DetermineContractAddress::contract_address_for(code, transactor); @@ -580,12 +826,10 @@ impl Module { } let mut local = BTreeMap::new(); - // two inserts are safe // note that we now know that `&dest != transactor` due to early-out before. - 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)); - + local.insert(dest, ChangeEntry::contract_created(value, code.to_vec())); + local.insert(transactor.clone(), ChangeEntry::balance_changed(from_balance - liability)); Ok(Some(local)) } @@ -595,20 +839,27 @@ impl Module { value: T::Balance, account_db: &DB, ) -> result::Result>, &'static str> { + let would_create = account_db.get_balance(transactor).is_zero(); + let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; + let liability = value + fee; + let from_balance = account_db.get_balance(transactor); - if from_balance < value { + if from_balance < liability { return Err("balance too low to send value"); } - - let to_balance = account_db.get_balance(dest); - if >::get(transactor) > >::get(dest) { + if would_create && value < Self::existential_deposit() { + return Err("value too low to create account"); + } + if >::get(transactor) > >::block_number() { return Err("bondage too high to send value"); } + + let to_balance = account_db.get_balance(dest); if to_balance + value <= to_balance { return Err("destination balance too high to receive value"); } - // TODO: a fee, based upon gaslimit/gasprice. + // TODO: an additional fee, based upon gaslimit/gasprice. let gas_limit = 100_000; // TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime @@ -618,7 +869,7 @@ impl Module { let mut overlay = OverlayAccountDb::new(account_db); if transactor != dest { - overlay.set_balance(transactor, from_balance - value); + overlay.set_balance(transactor, from_balance - liability); overlay.set_balance(dest, to_balance + value); } @@ -626,8 +877,8 @@ impl Module { let should_commit = if dest_code.is_empty() { true } else { - // TODO: logging (logs are just appended into a notable storage-based vector and cleared every - // block). + // TODO: logging (logs are just appended into a notable storage-based vector and + // cleared every block). let mut staking_ext = StakingExt { account_db: &mut overlay, account: dest.clone(), @@ -674,14 +925,14 @@ impl<'a, 'b: 'a, T: Trait> contract::Ext for StakingExt<'a, 'b, T> { } impl MakePayment for Module { - fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> bool { + fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { let b = Self::free_balance(transactor); let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * >::sa(encoded_len); if b < transaction_fee { - return false; + return Err("not enough funds for transaction fee"); } >::insert(transactor, b - transaction_fee); - true + Ok(()) } } @@ -704,6 +955,11 @@ pub struct GenesisConfig { pub bonding_duration: T::BlockNumber, pub transaction_base_fee: T::Balance, pub transaction_byte_fee: T::Balance, + pub transfer_fee: T::Balance, + pub creation_fee: T::Balance, + pub contract_fee: T::Balance, + pub reclaim_rebate: T::Balance, + pub existential_deposit: T::Balance, } #[cfg(any(feature = "std", test))] @@ -718,6 +974,11 @@ impl GenesisConfig where T::AccountId: From { bonding_duration: T::BlockNumber::sa(0), transaction_base_fee: T::Balance::sa(0), transaction_byte_fee: T::Balance::sa(0), + transfer_fee: T::Balance::sa(0), + creation_fee: T::Balance::sa(0), + contract_fee: T::Balance::sa(0), + existential_deposit: T::Balance::sa(0), + reclaim_rebate: T::Balance::sa(0), } } @@ -739,6 +1000,11 @@ impl GenesisConfig where T::AccountId: From { bonding_duration: T::BlockNumber::sa(0), transaction_base_fee: T::Balance::sa(1), transaction_byte_fee: T::Balance::sa(0), + transfer_fee: T::Balance::sa(0), + creation_fee: T::Balance::sa(0), + contract_fee: T::Balance::sa(0), + existential_deposit: T::Balance::sa(0), + reclaim_rebate: T::Balance::sa(0), } } } @@ -755,6 +1021,11 @@ impl Default for GenesisConfig { bonding_duration: T::BlockNumber::sa(1000), transaction_base_fee: T::Balance::sa(0), transaction_byte_fee: T::Balance::sa(0), + transfer_fee: T::Balance::sa(0), + creation_fee: T::Balance::sa(0), + contract_fee: T::Balance::sa(0), + existential_deposit: T::Balance::sa(0), + reclaim_rebate: T::Balance::sa(0), } } } @@ -768,436 +1039,30 @@ impl primitives::BuildExternalities for GenesisConfig { let total_stake: T::Balance = self.balances.iter().fold(Zero::zero(), |acc, &(_, n)| acc + n); let mut r: runtime_io::TestExternalities = map![ + twox_128(>::key()).to_vec() => T::AccountIndex::sa(self.balances.len() / ENUM_SET_SIZE).encode(), twox_128(>::key()).to_vec() => self.intentions.encode(), twox_128(>::key()).to_vec() => self.sessions_per_era.encode(), twox_128(>::key()).to_vec() => self.validator_count.encode(), twox_128(>::key()).to_vec() => self.bonding_duration.encode(), twox_128(>::key()).to_vec() => self.transaction_base_fee.encode(), twox_128(>::key()).to_vec() => self.transaction_byte_fee.encode(), + twox_128(>::key()).to_vec() => self.transfer_fee.encode(), + twox_128(>::key()).to_vec() => self.creation_fee.encode(), + twox_128(>::key()).to_vec() => self.contract_fee.encode(), + twox_128(>::key()).to_vec() => self.existential_deposit.encode(), + twox_128(>::key()).to_vec() => self.reclaim_rebate.encode(), twox_128(>::key()).to_vec() => self.current_era.encode(), twox_128(>::key()).to_vec() => total_stake.encode() ]; + let ids: Vec<_> = self.balances.iter().map(|x| x.0.clone()).collect(); + for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE { + r.insert(twox_128(&>::key_for(T::AccountIndex::sa(i))).to_vec(), + ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode()); + } for (who, value) in self.balances.into_iter() { r.insert(twox_128(&>::key_for(who)).to_vec(), value.encode()); } r } } - -#[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 PublicAux = ::PublicAux; - type SessionKey = u64; - } - impl system::Trait for Test { - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = ::primitives::traits::BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - } - impl session::Trait for Test { - type ConvertAccountIdToSessionKey = Identity; - } - impl Trait for Test { - type Balance = u64; - type DetermineContractAddress = DummyContractAddressFor; - } - - fn new_test_ext(session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_externalities(); - t.extend(consensus::GenesisConfig::{ - code: vec![], - authorities: vec![], - }.build_externalities()); - t.extend(session::GenesisConfig::{ - session_length, - validators: vec![10, 20], - }.build_externalities()); - t.extend(GenesisConfig::{ - sessions_per_era, - current_era, - balances: if monied { vec![(1, 10), (2, 20), (3, 30), (4, 40)] } else { vec![] }, - intentions: vec![], - validator_count: 2, - bonding_duration: 3, - transaction_base_fee: 0, - transaction_byte_fee: 0, - }.build_externalities()); - t - } - - type System = system::Module; - type Session = session::Module; - type Staking = Module; - - #[test] - fn staking_should_work() { - with_externalities(&mut new_test_ext(1, 2, 0, true), || { - assert_eq!(Staking::era_length(), 2); - assert_eq!(Staking::validator_count(), 2); - assert_eq!(Staking::bonding_duration(), 3); - assert_eq!(Session::validators(), vec![10, 20]); - - // Block 1: Add three validators. No obvious change. - System::set_block_number(1); - assert_ok!(Staking::stake(&1)); - assert_ok!(Staking::stake(&2)); - assert_ok!(Staking::stake(&4)); - Staking::check_new_era(); - assert_eq!(Session::validators(), vec![10, 20]); - - // Block 2: New validator set now. - System::set_block_number(2); - Staking::check_new_era(); - assert_eq!(Session::validators(), vec![4, 2]); - - // Block 3: Unstake highest, introduce another staker. No change yet. - System::set_block_number(3); - assert_ok!(Staking::stake(&3)); - assert_ok!(Staking::unstake(&4)); - Staking::check_new_era(); - - // Block 4: New era - validators change. - System::set_block_number(4); - Staking::check_new_era(); - assert_eq!(Session::validators(), vec![3, 2]); - - // Block 5: Transfer stake from highest to lowest. No change yet. - System::set_block_number(5); - assert_ok!(Staking::transfer(&4, 1, 40)); - Staking::check_new_era(); - - // Block 6: Lowest now validator. - System::set_block_number(6); - Staking::check_new_era(); - assert_eq!(Session::validators(), vec![1, 3]); - - // Block 7: Unstake three. No change yet. - System::set_block_number(7); - assert_ok!(Staking::unstake(&3)); - Staking::check_new_era(); - assert_eq!(Session::validators(), vec![1, 3]); - - // Block 8: Back to one and two. - System::set_block_number(8); - Staking::check_new_era(); - assert_eq!(Session::validators(), vec![1, 2]); - }); - } - - #[test] - fn staking_eras_work() { - with_externalities(&mut new_test_ext(1, 2, 0, true), || { - assert_eq!(Staking::era_length(), 2); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 0); - - // Block 1: No change. - System::set_block_number(1); - Staking::check_new_era(); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 0); - - // Block 2: Simple era change. - System::set_block_number(2); - Staking::check_new_era(); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 1); - - // Block 3: Schedule an era length change; no visible changes. - System::set_block_number(3); - assert_ok!(Staking::set_sessions_per_era(3)); - Staking::check_new_era(); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 1); - - // Block 4: Era change kicks in. - System::set_block_number(4); - Staking::check_new_era(); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 2); - - // Block 5: No change. - System::set_block_number(5); - Staking::check_new_era(); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 2); - - // Block 6: No change. - System::set_block_number(6); - Staking::check_new_era(); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 2); - - // Block 7: Era increment. - System::set_block_number(7); - Staking::check_new_era(); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 3); - }); - } - - #[test] - fn staking_balance_works() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 42); - assert_eq!(Staking::free_balance(&1), 42); - assert_eq!(Staking::reserved_balance(&1), 0); - assert_eq!(Staking::balance(&1), 42); - assert_eq!(Staking::free_balance(&2), 0); - assert_eq!(Staking::reserved_balance(&2), 0); - assert_eq!(Staking::balance(&2), 0); - }); - } - - #[test] - fn staking_balance_transfer_works() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::transfer(&1, 2, 69)); - assert_eq!(Staking::balance(&1), 42); - assert_eq!(Staking::balance(&2), 69); - }); - } - - #[test] - fn staking_balance_transfer_when_bonded_should_not_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::stake(&1)); - assert_noop!(Staking::transfer(&1, 2, 69), "bondage too high to send value"); - }); - } - - #[test] - fn reserving_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - - assert_eq!(Staking::balance(&1), 111); - assert_eq!(Staking::free_balance(&1), 111); - assert_eq!(Staking::reserved_balance(&1), 0); - - assert_ok!(Staking::reserve_balance(&1, 69)); - - assert_eq!(Staking::balance(&1), 111); - assert_eq!(Staking::free_balance(&1), 42); - assert_eq!(Staking::reserved_balance(&1), 69); - }); - } - - #[test] - fn staking_balance_transfer_when_reserved_should_not_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::reserve_balance(&1, 69)); - assert_noop!(Staking::transfer(&1, 2, 69), "balance too low to send value"); - }); - } - - #[test] - fn deducting_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::deduct_unbonded(&1, 69)); - assert_eq!(Staking::free_balance(&1), 42); - }); - } - - #[test] - fn deducting_balance_when_bonded_should_not_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - >::insert(1, 2); - System::set_block_number(1); - assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2)); - assert_noop!(Staking::deduct_unbonded(&1, 69), "not enough liquid funds"); - }); - } - - #[test] - fn refunding_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 42); - Staking::refund(&1, 69); - assert_eq!(Staking::free_balance(&1), 111); - }); - } - - #[test] - fn slashing_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::reserve_balance(&1, 69)); - assert_ok!(Staking::slash(&1, 69)); - assert_eq!(Staking::free_balance(&1), 0); - assert_eq!(Staking::reserved_balance(&1), 42); - }); - } - - #[test] - fn slashing_incomplete_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 42); - assert_ok!(Staking::reserve_balance(&1, 21)); - assert_err!(Staking::slash(&1, 69), "not enough funds"); - assert_eq!(Staking::free_balance(&1), 0); - assert_eq!(Staking::reserved_balance(&1), 0); - }); - } - - #[test] - fn unreserving_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::reserve_balance(&1, 111)); - Staking::unreserve_balance(&1, 42); - assert_eq!(Staking::reserved_balance(&1), 69); - assert_eq!(Staking::free_balance(&1), 42); - }); - } - - #[test] - fn slashing_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::reserve_balance(&1, 111)); - assert_ok!(Staking::slash_reserved(&1, 42)); - assert_eq!(Staking::reserved_balance(&1), 69); - assert_eq!(Staking::free_balance(&1), 0); - }); - } - - #[test] - fn slashing_incomplete_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::reserve_balance(&1, 42)); - assert_err!(Staking::slash_reserved(&1, 69), "not enough (reserved) funds"); - assert_eq!(Staking::free_balance(&1), 69); - assert_eq!(Staking::reserved_balance(&1), 0); - }); - } - - #[test] - fn transferring_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::reserve_balance(&1, 111)); - assert_ok!(Staking::transfer_reserved_balance(&1, &2, 42)); - assert_eq!(Staking::reserved_balance(&1), 69); - assert_eq!(Staking::free_balance(&1), 0); - assert_eq!(Staking::reserved_balance(&2), 0); - assert_eq!(Staking::free_balance(&2), 42); - }); - } - - #[test] - fn transferring_incomplete_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(1, 111); - assert_ok!(Staking::reserve_balance(&1, 42)); - assert_err!(Staking::transfer_reserved_balance(&1, &2, 69), "not enough (reserved) funds"); - assert_eq!(Staking::reserved_balance(&1), 0); - assert_eq!(Staking::free_balance(&1), 69); - assert_eq!(Staking::reserved_balance(&2), 0); - assert_eq!(Staking::free_balance(&2), 42); - }); - } - - const CODE_TRANSFER: &str = r#" -(module - ;; ext_transfer(transfer_to: u32, transfer_to_len: u32, value_ptr: u32, value_len: u32) - (import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32 i32))) - (import "env" "memory" (memory 1 1)) - (func (export "call") - (call $ext_transfer - (i32.const 4) ;; Pointer to "Transfer to" address. - (i32.const 8) ;; Length of "Transfer to" address. - (i32.const 12) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. - ) - ) - ;; Destination AccountId to transfer the funds. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 4) "\02\00\00\00\00\00\00\00") - ;; Amount of value to transfer. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 12) "\06\00\00\00\00\00\00\00") -) -"#; - - #[test] - fn contract_transfer() { - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - >::insert(0, 111); - >::insert(1, 0); - >::insert(2, 30); - - >::insert(1, code_transfer.to_vec()); - - assert_ok!(Staking::transfer(&0, 1, 11)); - - assert_eq!(Staking::balance(&0), 100); - assert_eq!(Staking::balance(&1), 5); - assert_eq!(Staking::balance(&2), 36); - }); - } - - const CODE_MEM: &str = -r#" -(module - ;; Internal memory is not allowed. - (memory 1 1) - (func (export "call") - nop - ) -) -"#; - - #[test] - fn contract_internal_mem() { - let code_mem = wabt::wat2wasm(CODE_MEM).unwrap(); - - with_externalities(&mut new_test_ext(1, 3, 1, false), || { - // Set initial balances. - >::insert(0, 111); - >::insert(1, 0); - - >::insert(1, code_mem.to_vec()); - - // Transfer some balance from 0 to 1. - assert_ok!(Staking::transfer(&0, 1, 11)); - - // The balance should remain unchanged since we are expecting - // validation error caused by internal memory declaration. - assert_eq!(Staking::balance(&0), 111); - }); - } -} diff --git a/substrate/runtime/staking/src/mock.rs b/substrate/runtime/staking/src/mock.rs new file mode 100644 index 0000000000000..263fb0a48d167 --- /dev/null +++ b/substrate/runtime/staking/src/mock.rs @@ -0,0 +1,91 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utilities + +#![cfg(test)] + +use primitives::BuildExternalities; +use primitives::traits::{HasPublicAux, Identity}; +use primitives::testing::{Digest, Header}; +use substrate_primitives::H256; +use runtime_io; +use {DummyContractAddressFor, GenesisConfig, Module, Trait, consensus, session, system}; + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub struct Test; +impl HasPublicAux for Test { + type PublicAux = u64; +} +impl consensus::Trait for Test { + type PublicAux = ::PublicAux; + type SessionKey = u64; +} +impl system::Trait for Test { + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::primitives::traits::BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; +} +impl session::Trait for Test { + type ConvertAccountIdToSessionKey = Identity; +} +impl Trait for Test { + type Balance = u64; + type DetermineContractAddress = DummyContractAddressFor; + type AccountIndex = u64; +} + +pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_externalities(); + let balance_factor = if ext_deposit > 0 { + 256 + } else { + 1 + }; + t.extend(consensus::GenesisConfig::{ + code: vec![], + authorities: vec![], + }.build_externalities()); + t.extend(session::GenesisConfig::{ + session_length, + validators: vec![10, 20], + }.build_externalities()); + t.extend(GenesisConfig::{ + sessions_per_era, + current_era, + balances: if monied { vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)] } else { vec![] }, + intentions: vec![], + validator_count: 2, + bonding_duration: 3, + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: ext_deposit, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, + }.build_externalities()); + t +} + +pub type System = system::Module; +pub type Session = session::Module; +pub type Staking = Module; diff --git a/substrate/runtime/staking/src/tests.rs b/substrate/runtime/staking/src/tests.rs new file mode 100644 index 0000000000000..08479c0696ec7 --- /dev/null +++ b/substrate/runtime/staking/src/tests.rs @@ -0,0 +1,395 @@ +// 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 . + +//! Tests for the module. + +use super::*; +use runtime_io::with_externalities; +use mock::*; + +#[test] +fn indexing_lookup_should_work() { + with_externalities(&mut new_test_ext(10, 1, 2, 0, true), || { + assert_eq!(Staking::lookup_index(0), Some(1)); + assert_eq!(Staking::lookup_index(1), Some(2)); + assert_eq!(Staking::lookup_index(2), Some(3)); + assert_eq!(Staking::lookup_index(3), Some(4)); + assert_eq!(Staking::lookup_index(4), None); + }); +} + +#[test] +fn default_indexing_on_new_accounts_should_work() { + with_externalities(&mut new_test_ext(10, 1, 2, 0, true), || { + assert_eq!(Staking::lookup_index(4), None); + assert_ok!(Staking::transfer(&1, 5.into(), 10)); + assert_eq!(Staking::lookup_index(4), Some(5)); + }); +} + +#[test] +fn dust_account_removal_should_work() { + with_externalities(&mut new_test_ext(256 * 10, 1, 2, 0, true), || { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Staking::voting_balance(&2), 256 * 20); + + assert_ok!(Staking::transfer(&2, 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie + assert_eq!(Staking::voting_balance(&2), 0); + assert_eq!(Staking::voting_balance(&5), 256 * 10 + 1); + assert_eq!(System::account_nonce(&2), 0); + }); +} + +#[test] +fn reclaim_indexing_on_new_accounts_should_work() { + with_externalities(&mut new_test_ext(256 * 1, 1, 2, 0, true), || { + assert_eq!(Staking::lookup_index(1), Some(2)); + assert_eq!(Staking::lookup_index(4), None); + assert_eq!(Staking::voting_balance(&2), 256 * 20); + + assert_ok!(Staking::transfer(&2, 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim) + assert_eq!(Staking::voting_balance(&2), 0); + + assert_ok!(Staking::transfer(&5, 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1. + assert_eq!(Staking::voting_balance(&6), 256 * 1 + 0x69); + assert_eq!(Staking::lookup_index(1), Some(6)); + }); +} + +#[test] +fn reserved_balance_should_prevent_reclaim_count() { + with_externalities(&mut new_test_ext(256 * 1, 1, 2, 0, true), || { + System::inc_account_nonce(&2); + assert_eq!(Staking::lookup_index(1), Some(2)); + assert_eq!(Staking::lookup_index(4), None); + assert_eq!(Staking::voting_balance(&2), 256 * 20); + + assert_ok!(Staking::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(Staking::free_balance(&2), 0); // "free" account deleted." + assert_eq!(Staking::voting_balance(&2), 256 * 19 + 1); // reserve still exists. + assert_eq!(System::account_nonce(&2), 1); + + assert_ok!(Staking::transfer(&4, 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5. + assert_eq!(Staking::voting_balance(&5), 256 * 1 + 0x69); + assert_eq!(Staking::lookup_index(1), Some(2)); // but fails. + assert_eq!(System::account_nonce(&2), 1); + + assert_eq!(Staking::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed + assert_eq!(Staking::voting_balance(&2), 0); // "free" account deleted." + assert_eq!(System::account_nonce(&2), 0); + + assert_ok!(Staking::transfer(&4, 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6. + assert_eq!(Staking::voting_balance(&6), 256 * 1 + 0x69); + assert_eq!(Staking::lookup_index(1), Some(6)); // and succeeds. + }); +} + +#[test] +fn staking_should_work() { + with_externalities(&mut new_test_ext(0, 1, 2, 0, true), || { + assert_eq!(Staking::era_length(), 2); + assert_eq!(Staking::validator_count(), 2); + assert_eq!(Staking::bonding_duration(), 3); + assert_eq!(Session::validators(), vec![10, 20]); + + // Block 1: Add three validators. No obvious change. + System::set_block_number(1); + assert_ok!(Staking::stake(&1)); + assert_ok!(Staking::stake(&2)); + assert_ok!(Staking::stake(&4)); + Staking::check_new_era(); + assert_eq!(Session::validators(), vec![10, 20]); + + // Block 2: New validator set now. + System::set_block_number(2); + Staking::check_new_era(); + assert_eq!(Session::validators(), vec![4, 2]); + + // Block 3: Unstake highest, introduce another staker. No change yet. + System::set_block_number(3); + assert_ok!(Staking::stake(&3)); + assert_ok!(Staking::unstake(&4)); + Staking::check_new_era(); + + // Block 4: New era - validators change. + System::set_block_number(4); + Staking::check_new_era(); + assert_eq!(Session::validators(), vec![3, 2]); + + // Block 5: Transfer stake from highest to lowest. No change yet. + System::set_block_number(5); + assert_ok!(Staking::transfer(&4, 1.into(), 40)); + Staking::check_new_era(); + + // Block 6: Lowest now validator. + System::set_block_number(6); + Staking::check_new_era(); + assert_eq!(Session::validators(), vec![1, 3]); + + // Block 7: Unstake three. No change yet. + System::set_block_number(7); + assert_ok!(Staking::unstake(&3)); + Staking::check_new_era(); + assert_eq!(Session::validators(), vec![1, 3]); + + // Block 8: Back to one and two. + System::set_block_number(8); + Staking::check_new_era(); + assert_eq!(Session::validators(), vec![1, 2]); + }); +} + +#[test] +fn staking_eras_work() { + with_externalities(&mut new_test_ext(0, 1, 2, 0, true), || { + assert_eq!(Staking::era_length(), 2); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 0); + + // Block 1: No change. + System::set_block_number(1); + Staking::check_new_era(); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 0); + + // Block 2: Simple era change. + System::set_block_number(2); + Staking::check_new_era(); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 1); + + // Block 3: Schedule an era length change; no visible changes. + System::set_block_number(3); + assert_ok!(Staking::set_sessions_per_era(3)); + Staking::check_new_era(); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 1); + + // Block 4: Era change kicks in. + System::set_block_number(4); + Staking::check_new_era(); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 2); + + // Block 5: No change. + System::set_block_number(5); + Staking::check_new_era(); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 2); + + // Block 6: No change. + System::set_block_number(6); + Staking::check_new_era(); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 2); + + // Block 7: Era increment. + System::set_block_number(7); + Staking::check_new_era(); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 3); + }); +} + +#[test] +fn staking_balance_works() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 42); + assert_eq!(Staking::free_balance(&1), 42); + assert_eq!(Staking::reserved_balance(&1), 0); + assert_eq!(Staking::voting_balance(&1), 42); + assert_eq!(Staking::free_balance(&2), 0); + assert_eq!(Staking::reserved_balance(&2), 0); + assert_eq!(Staking::voting_balance(&2), 0); + }); +} + +#[test] +fn staking_balance_transfer_works() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::transfer(&1, 2.into(), 69)); + assert_eq!(Staking::voting_balance(&1), 42); + assert_eq!(Staking::voting_balance(&2), 69); + }); +} + +#[test] +fn staking_balance_transfer_when_bonded_should_not_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::stake(&1)); + assert_noop!(Staking::transfer(&1, 2.into(), 69), "bondage too high to send value"); + }); +} + +#[test] +fn reserving_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + + assert_eq!(Staking::voting_balance(&1), 111); + assert_eq!(Staking::free_balance(&1), 111); + assert_eq!(Staking::reserved_balance(&1), 0); + + assert_ok!(Staking::reserve(&1, 69)); + + assert_eq!(Staking::voting_balance(&1), 111); + assert_eq!(Staking::free_balance(&1), 42); + assert_eq!(Staking::reserved_balance(&1), 69); + }); +} + +#[test] +fn staking_balance_transfer_when_reserved_should_not_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::reserve(&1, 69)); + assert_noop!(Staking::transfer(&1, 2.into(), 69), "balance too low to send value"); + }); +} + +#[test] +fn deducting_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::reserve(&1, 69)); + assert_eq!(Staking::free_balance(&1), 42); + }); +} + +#[test] +fn deducting_balance_when_bonded_should_not_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + >::insert(1, 2); + System::set_block_number(1); + assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2)); + assert_noop!(Staking::reserve(&1, 69), "free funds are still bonded"); + }); +} + +#[test] +fn refunding_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 42); + >::insert(1, 69); + Staking::unreserve(&1, 69); + assert_eq!(Staking::free_balance(&1), 111); + assert_eq!(Staking::reserved_balance(&1), 0); + }); +} + +#[test] +fn slashing_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::reserve(&1, 69)); + assert!(Staking::slash(&1, 69).is_none()); + assert_eq!(Staking::free_balance(&1), 0); + assert_eq!(Staking::reserved_balance(&1), 42); + }); +} + +#[test] +fn slashing_incomplete_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 42); + assert_ok!(Staking::reserve(&1, 21)); + assert!(Staking::slash(&1, 69).is_some()); + assert_eq!(Staking::free_balance(&1), 0); + assert_eq!(Staking::reserved_balance(&1), 0); + }); +} + +#[test] +fn unreserving_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::reserve(&1, 111)); + Staking::unreserve(&1, 42); + assert_eq!(Staking::reserved_balance(&1), 69); + assert_eq!(Staking::free_balance(&1), 42); + }); +} + +#[test] +fn slashing_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::reserve(&1, 111)); + assert!(Staking::slash_reserved(&1, 42).is_none()); + assert_eq!(Staking::reserved_balance(&1), 69); + assert_eq!(Staking::free_balance(&1), 0); + }); +} + +#[test] +fn slashing_incomplete_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::reserve(&1, 42)); + assert!(Staking::slash_reserved(&1, 69).is_some()); + assert_eq!(Staking::free_balance(&1), 69); + assert_eq!(Staking::reserved_balance(&1), 0); + }); +} + +#[test] +fn transferring_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 110); + >::insert(2, 1); + assert_ok!(Staking::reserve(&1, 110)); + assert_ok!(Staking::transfer_reserved(&1, &2, 41), None); + assert_eq!(Staking::reserved_balance(&1), 69); + assert_eq!(Staking::free_balance(&1), 0); + assert_eq!(Staking::reserved_balance(&2), 0); + assert_eq!(Staking::free_balance(&2), 42); + }); +} + +#[test] +fn transferring_reserved_balance_to_nonexistent_should_fail() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 111); + assert_ok!(Staking::reserve(&1, 111)); + assert_noop!(Staking::transfer_reserved(&1, &2, 42), "beneficiary account must pre-exist"); + }); +} + +#[test] +fn transferring_incomplete_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false), || { + >::insert(1, 110); + >::insert(2, 1); + assert_ok!(Staking::reserve(&1, 41)); + assert!(Staking::transfer_reserved(&1, &2, 69).unwrap().is_some()); + assert_eq!(Staking::reserved_balance(&1), 0); + assert_eq!(Staking::free_balance(&1), 69); + assert_eq!(Staking::reserved_balance(&2), 0); + assert_eq!(Staking::free_balance(&2), 42); + }); +} diff --git a/substrate/runtime/system/Cargo.toml b/substrate/runtime/system/Cargo.toml index 89d4e48e201b8..38fb97b582f5c 100644 --- a/substrate/runtime/system/Cargo.toml +++ b/substrate/runtime/system/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Parity Technologies "] [dependencies] hex-literal = "0.1.0" 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 = "../../codec", default_features = false } substrate-primitives = { path = "../../primitives", default_features = false } @@ -18,6 +19,7 @@ substrate-runtime-primitives = { path = "../primitives", default_features = fals default = ["std"] std = [ "serde/std", + "serde_derive", "safe-mix/std", "substrate-codec/std", "substrate-primitives/std", diff --git a/substrate/runtime/system/src/lib.rs b/substrate/runtime/system/src/lib.rs index af7f8f7aaae26..0d48b359cfdc6 100644 --- a/substrate/runtime/system/src/lib.rs +++ b/substrate/runtime/system/src/lib.rs @@ -28,6 +28,10 @@ extern crate substrate_runtime_support as runtime_support; #[cfg(feature = "std")] extern crate serde; +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + extern crate substrate_runtime_io as runtime_io; extern crate substrate_codec as codec; extern crate substrate_runtime_primitives as primitives; @@ -58,7 +62,7 @@ pub fn extrinsics_data_root(xts: Vec>) -> H::Output { H::enumerated_trie_root(&xts) } -pub trait Trait { +pub trait Trait: Eq + Clone { type Index: Parameter + Member + Default + MaybeDisplay + SimpleArithmetic + Copy; type BlockNumber: Parameter + Member + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy + rstd::hash::Hash; type Hash: Parameter + Member + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]>; @@ -80,7 +84,7 @@ decl_module! { decl_storage! { trait Store for Module; - pub AccountIndex get(account_index): b"sys:non" => default map [ T::AccountId => T::Index ]; + pub AccountNonce get(account_nonce): b"sys:non" => default map [ T::AccountId => T::Index ]; pub BlockHash get(block_hash): b"sys:old" => required map [ T::BlockNumber => T::Hash ]; pub ExtrinsicIndex get(extrinsic_index): b"sys:xti" => required u32; @@ -170,8 +174,8 @@ impl Module { } /// Increment a particular account's nonce by 1. - pub fn inc_account_index(who: &T::AccountId) { - >::insert(who, Self::account_index(who) + T::Index::one()); + pub fn inc_account_nonce(who: &T::AccountId) { + >::insert(who, Self::account_nonce(who) + T::Index::one()); } /// Note what the extrinsic data of the current extrinsic index is. If this is called, then diff --git a/substrate/runtime/timestamp/src/lib.rs b/substrate/runtime/timestamp/src/lib.rs index b6711468deabe..7680183bf9cf3 100644 --- a/substrate/runtime/timestamp/src/lib.rs +++ b/substrate/runtime/timestamp/src/lib.rs @@ -120,6 +120,7 @@ mod tests { use runtime_primitives::traits::{HasPublicAux, BlakeTwo256}; use runtime_primitives::testing::{Digest, Header}; + #[derive(Clone, Eq, PartialEq)] pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; diff --git a/substrate/test-runtime/src/lib.rs b/substrate/test-runtime/src/lib.rs index 837572efc0030..8454a8c7bd60c 100644 --- a/substrate/test-runtime/src/lib.rs +++ b/substrate/test-runtime/src/lib.rs @@ -51,7 +51,7 @@ pub mod system; use rstd::prelude::*; use codec::Slicable; -use runtime_primitives::traits::{Checkable, BlakeTwo256}; +use runtime_primitives::traits::{BlindCheckable, BlakeTwo256}; use runtime_primitives::Ed25519Signature; pub use primitives::hash::H256; @@ -101,14 +101,18 @@ impl Slicable for Extrinsic { } } -impl Checkable for Extrinsic { +impl BlindCheckable for Extrinsic { type Checked = Self; + type Address = AccountId; - fn check(self) -> Result { + fn sender(&self) -> &Self::Address { + &self.transfer.from + } + fn check(self) -> Result { if ::runtime_primitives::verify_encoded_lazy(&self.signature, &self.transfer, &self.transfer.from) { Ok(self) } else { - Err(self) + Err("bad signature") } } } diff --git a/substrate/test-runtime/src/system.rs b/substrate/test-runtime/src/system.rs index f71bbd0155d94..f549954aa8a70 100644 --- a/substrate/test-runtime/src/system.rs +++ b/substrate/test-runtime/src/system.rs @@ -110,7 +110,7 @@ pub fn finalise_block() -> Header { } fn execute_transaction_backend(utx: &Extrinsic) { - use runtime_primitives::traits::Checkable; + use runtime_primitives::traits::BlindCheckable; // check signature let utx = match utx.clone().check() { diff --git a/substrate/test-runtime/wasm/Cargo.lock b/substrate/test-runtime/wasm/Cargo.lock index a57d2e899b0bb..c795055b8e86e 100644 --- a/substrate/test-runtime/wasm/Cargo.lock +++ b/substrate/test-runtime/wasm/Cargo.lock @@ -483,6 +483,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "substrate-codec" version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "substrate-primitives" diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 94bf42b3f97dd..a16b0b0db6cc9 100644 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm index 45ee6c9b1867f..5cdc00652a1fa 100755 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm differ