mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 08:21:05 +00:00
Enumeratable accounts (#195)
* Merge remote-tracking branch 'origin/master' into gav-xts-dont-panic * Update wasm. * consensus, session and staking all panic-safe. * Democracy doesn't panic in apply. * Fix tests. * Extra helper macro, council depanicked. * Fix one test. * Fix up all council tests. No panics! * Council voting depanicked. * Dispatch returns result. * session & staking tests updated * Fix democracy tests. * Fix council tests. * Fix up polkadot parachains in runtime * Fix borked merge * More Slicable support Support general `Option` and array types. * Basic storage types. * Existential deposit for contract creation * Basic implemnetation along with removals * Fix tests. * externalities builder fix. * Tests. * Fix up the runtime. * Fix tests. * Add generic `Address` type. * Initial function integration of Address into Extrinsic. * Fix build * All tests compile. * Fix (some) tests. * Fix signing. * Push error. * transfer can accept Address * Make Address generic over AccountIndex * Fix test * Make Council use Address for dispatch. * Fix build * Bend over backwards to support braindead derive. * Repot some files. * Fix tests. * Fix grumbles * Remove Default bound * Fix build for new nightly. * Make `apply_extrinsic` never panic, return useful Result. * More merge hell * Doesn't build, but might do soon * Serde woes * get substrate-runtime-staking compiling * Polkadot builds again! * Fix all build. * Fix tests & binaries. * Reserve some extra initial byte values of address for future format changes * Make semantic of `ReservedBalance` clear. * Fix panic handler. * Integrate other balance transformations into the new model Fix up staking tests. * Fix runtime tests. * Fix panic build. * Tests for demonstrating interaction between balance types. * Repot some runtime code * Fix checkedblock in non-std builds * Get rid of `DoLookup` phantom. * Attempt to make transaction_pool work with lookups. * Remove vscode settings * New attempt at making transaction pool work. * It builds again! * --all builds * Fix tests. * New build. * Test account nonce reset. * polkadot transaction pool tests/framework. * Address grumbles. * Revert bad `map_or` * Rebuild binaries, workaround. * Avoid casting to usize early. * reenable sync tests
This commit is contained in:
Generated
+9
-1
@@ -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",
|
||||
|
||||
@@ -122,6 +122,11 @@ pub fn run<I, T>(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.
|
||||
|
||||
@@ -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::<Concrete>(bob(), 69)),
|
||||
function: Call::Staking(staking::Call::transfer::<Concrete>(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(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "initialise_block", &vec![].and(&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(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "initialise_block", &vec![].and(&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(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::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(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::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<Extrinsic>) -> (Vec<u8>, Hash) {
|
||||
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec<BareExtrinsic>) -> (Vec<u8>, 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::<Vec<_>>();
|
||||
|
||||
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(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&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(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm");
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<runtime_primitives::Ed25519Signature>;
|
||||
|
||||
@@ -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<Concrete>;
|
||||
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<Concrete>;
|
||||
/// Block header type as expected by this runtime.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256, Vec<u8>>;
|
||||
/// Block type as expected by this runtime.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
/// Extrinsic type as expected by this runtime.
|
||||
pub type Extrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call, Signature>;
|
||||
/// Extrinsic type as expected by this runtime. This is not the type that is signed.
|
||||
pub type Extrinsic = generic::Extrinsic<Address, Index, Call>;
|
||||
/// Extrinsic type that is signed.
|
||||
pub type BareExtrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||
/// Executive: handles dispatch to the various modules.
|
||||
pub type Executive = executive::Executive<Concrete, Block, Staking,
|
||||
pub type Executive = executive::Executive<Concrete, Block, Staking, Staking,
|
||||
(((((), Council), Democracy), Staking), Session)>;
|
||||
|
||||
impl_outer_config! {
|
||||
|
||||
Generated
+5
@@ -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",
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
@@ -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" }
|
||||
|
||||
@@ -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<B: LocalBackend<Block>> PolkadotApi for Client<B, LocalCallExecutor<B, Nati
|
||||
}
|
||||
|
||||
fn index(&self, at: &CheckedId, account: AccountId) -> Result<Index> {
|
||||
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<Option<AccountId>> {
|
||||
with_runtime!(self, at, || <::runtime::Staking as AuxLookup>::lookup(address).ok())
|
||||
}
|
||||
|
||||
fn active_parachains(&self, at: &CheckedId) -> Result<Vec<ParaId>> {
|
||||
|
||||
@@ -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<Timestamp>;
|
||||
|
||||
/// 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<Index>;
|
||||
|
||||
/// Get the account id of an address at a block.
|
||||
fn lookup(&self, at: &Self::CheckedBlockId, address: Address) -> Result<Option<AccountId>>;
|
||||
|
||||
/// Get the active parachains at a block.
|
||||
fn active_parachains(&self, at: &Self::CheckedBlockId) -> Result<Vec<ParaId>>;
|
||||
|
||||
|
||||
@@ -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<B: Backend<Block>, E: CallExecutor<Block>> PolkadotApi for RemotePolkadotAp
|
||||
Err(ErrorKind::UnknownRuntime.into())
|
||||
}
|
||||
|
||||
fn lookup(&self, _at: &CheckedId, _address: Address) -> Result<Option<AccountId>> {
|
||||
Err(ErrorKind::UnknownRuntime.into())
|
||||
}
|
||||
|
||||
fn active_parachains(&self, _at: &Self::CheckedBlockId) -> Result<Vec<ParaId>> {
|
||||
Err(ErrorKind::UnknownRuntime.into())
|
||||
}
|
||||
|
||||
@@ -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<C, R, P> bft::Proposer<Block> for Proposer<C, R, P>
|
||||
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<C, R, P> bft::Proposer<Block> for Proposer<C, R, P>
|
||||
=> 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<C, R, P> bft::Proposer<Block> for Proposer<C, R, P>
|
||||
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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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;
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<Self, Block> {
|
||||
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!())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::{AccountId, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature};
|
||||
use primitives::parachain::CandidateReceipt;
|
||||
#[cfg(feature = "std")]
|
||||
pub use checked_block::CheckedBlock;
|
||||
pub use utils::{inherent_extrinsics, check_extrinsic};
|
||||
pub use staking::address::Address as RawAddress;
|
||||
|
||||
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<Concrete>;
|
||||
/// Block Id type for this block.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
/// Extrinsic type as expected by this runtime.
|
||||
pub type Extrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call, Signature>;
|
||||
/// Extrinsic type as expected by this runtime. This is not the type that is signed.
|
||||
pub type Extrinsic = generic::Extrinsic<Address, Index, Call>;
|
||||
/// Extrinsic type that is signed.
|
||||
pub type BareExtrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||
/// Block type as expected by this runtime.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
|
||||
/// 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<Self, Block> {
|
||||
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<Concrete>;
|
||||
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<Concrete>;
|
||||
@@ -280,7 +203,7 @@ impl_outer_dispatch! {
|
||||
}
|
||||
|
||||
/// Executive: handles dispatch to the various modules.
|
||||
pub type Executive = executive::Executive<Concrete, Block, Staking,
|
||||
pub type Executive = executive::Executive<Concrete, Block, Staking, Staking,
|
||||
(((((((), Parachains), Council), Democracy), Staking), Session), Timestamp)>;
|
||||
|
||||
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<CandidateReceipt>) -> Vec<UncheckedExtrinsic> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<CandidateReceipt>) -> Vec<UncheckedExtrinsic> {
|
||||
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()
|
||||
}
|
||||
+5
@@ -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",
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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<AuthorityId>) -> 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,
|
||||
|
||||
@@ -6,11 +6,13 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[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" }
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = <UncheckedExtrinsic as Checkable>::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<Option<CheckedExtrinsic>>,
|
||||
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<Self> {
|
||||
if !xt.is_signed() {
|
||||
bail!(ErrorKind::IsInherent(xt))
|
||||
fn create(original: UncheckedExtrinsic) -> Result<Self> {
|
||||
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<F>(&self, lookup: F) -> Result<()> where
|
||||
F: FnOnce(Address) -> result::Result<AccountId, &'static str> + Send + Sync
|
||||
{
|
||||
let inner: result::Result<CheckedExtrinsic, Error> = 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<CheckedExtrinsic> {
|
||||
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<AccountId> {
|
||||
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<CheckedExtrinsic> 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<VerifiedTransaction> 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<VerifiedTransaction> for Scoring {
|
||||
pub struct Ready<'a, T: 'a + PolkadotApi> {
|
||||
at_block: T::CheckedBlockId,
|
||||
api: &'a T,
|
||||
known_indices: HashMap<AccountId, ::primitives::Index>,
|
||||
}
|
||||
|
||||
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<AccountId, (::primitives::Index, bool)>,
|
||||
known_indexes: HashMap<AccountIndex, AccountId>,
|
||||
}
|
||||
|
||||
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> txpool::Ready<VerifiedTransaction> for Ready<'a, T> {
|
||||
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<VerifiedTransaction> 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));
|
||||
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_index, xt.inner.index);
|
||||
trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_nonce, xt.original.extrinsic.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
|
||||
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<Arc<VerifiedTransaction>> {
|
||||
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<FutureProofUncheckedExtrinsic, Hash> 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<Block> { 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<TestCheckedBlockId> { Ok(TestCheckedBlockId(id)) }
|
||||
fn session_keys(&self, _at: &TestCheckedBlockId) -> Result<Vec<SessionKey>> { unimplemented!() }
|
||||
fn validators(&self, _at: &TestCheckedBlockId) -> Result<Vec<AccountId>> { unimplemented!() }
|
||||
fn random_seed(&self, _at: &TestCheckedBlockId) -> Result<Hash> { unimplemented!() }
|
||||
fn duty_roster(&self, _at: &TestCheckedBlockId) -> Result<DutyRoster> { unimplemented!() }
|
||||
fn timestamp(&self, _at: &TestCheckedBlockId) -> Result<u64> { unimplemented!() }
|
||||
fn evaluate_block(&self, _at: &TestCheckedBlockId, _block: Block) -> Result<bool> { unimplemented!() }
|
||||
fn active_parachains(&self, _at: &TestCheckedBlockId) -> Result<Vec<ParaId>> { unimplemented!() }
|
||||
fn parachain_code(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> { unimplemented!() }
|
||||
fn parachain_head(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> { unimplemented!() }
|
||||
fn build_block(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder> { unimplemented!() }
|
||||
fn inherent_extrinsics(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec<CandidateReceipt>) -> Result<Vec<Vec<u8>>> { unimplemented!() }
|
||||
|
||||
fn index(&self, _at: &TestCheckedBlockId, _account: AccountId) -> Result<Index> {
|
||||
Ok((_account[0] as u32) + number_of(_at))
|
||||
}
|
||||
fn lookup(&self, _at: &TestCheckedBlockId, _address: RawAddress<AccountId, AccountIndex>) -> Result<Option<AccountId>> {
|
||||
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![]);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
arrayvec = { version = "0.4", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<T: Slicable, E: Slicable> Slicable for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Option<bool> {
|
||||
/// Shim type because we can't do a specialised implementation for `Option<bool>` directly.
|
||||
pub struct OptionBool(pub Option<bool>);
|
||||
|
||||
impl Slicable for OptionBool {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
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, F: FnOnce(&[u8]) -> 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<T: Slicable> Slicable for Option<T> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
match input.read_byte()? {
|
||||
0 => Some(None),
|
||||
1 => Some(Some(T::decode(input)?)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
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<T: Slicable> Slicable for [T; $n] {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let mut r = ArrayVec::new();
|
||||
for _ in 0..$n {
|
||||
r.push(T::decode(input)?);
|
||||
}
|
||||
r.into_inner().ok()
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
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<T: Slicable> Slicable for Box<T> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
@@ -145,12 +196,8 @@ impl<T: Slicable> Slicable for Vec<T> {
|
||||
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<T: Slicable> Slicable for Vec<T> {
|
||||
|
||||
let mut r: Vec<u8> = 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<I: Input>(_: &mut I) -> Option<()> {
|
||||
Some(())
|
||||
|
||||
@@ -150,18 +150,18 @@ pub fn with<T: ?Sized, R, F: FnOnce(&mut T) -> 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]
|
||||
|
||||
+18
@@ -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)" = "<none>"
|
||||
"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)" = "<none>"
|
||||
"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"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![feature(lang_items)]
|
||||
#![feature(panic_implementation)]
|
||||
#![cfg_attr(feature = "strict", deny(warnings))]
|
||||
|
||||
#![feature(alloc)]
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.")]
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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))]
|
||||
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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>);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ pub type KeyValue = (Vec<u8>, Vec<u8>);
|
||||
|
||||
pub trait Trait: system::Trait {
|
||||
type PublicAux: RefInto<Self::AccountId>;
|
||||
type SessionKey: Parameter + Default;
|
||||
type SessionKey: Parameter + Default;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<bool>, 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<T::AccountId, T::AccountIndex>, 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<T::AccountId, T::AccountIndex>, 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<T::AccountId, T::AccountIndex>) -> 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<T: Trait> Module<T> {
|
||||
if !<LastActiveOf<T>>::exists(aux.ref_into()) {
|
||||
// not yet a voter - deduct bond.
|
||||
// NOTE: this must be the last potential bailer, since it changes state.
|
||||
<staking::Module<T>>::reserve_balance(aux.ref_into(), Self::voting_bond())?;
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), Self::voting_bond())?;
|
||||
|
||||
<Voters<T>>::put({
|
||||
let mut v = Self::voters();
|
||||
@@ -260,10 +261,11 @@ impl<T: Trait> Module<T> {
|
||||
fn reap_inactive_voter(
|
||||
aux: &T::PublicAux,
|
||||
signed_index: u32,
|
||||
who: T::AccountId,
|
||||
who: Address<T::AccountId, T::AccountIndex>,
|
||||
who_index: u32,
|
||||
assumed_vote_index: VoteIndex
|
||||
) -> Result {
|
||||
let who = <staking::Module<T>>::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<T: Trait> Module<T> {
|
||||
voters
|
||||
);
|
||||
if valid {
|
||||
<staking::Module<T>>::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.
|
||||
<staking::Module<T>>::transfer_reserved(&who, aux.ref_into(), Self::voting_bond())?;
|
||||
} else {
|
||||
<staking::Module<T>>::slash_reserved(aux.ref_into(), Self::voting_bond())
|
||||
<staking::Module<T>>::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<T: Trait> Module<T> {
|
||||
ensure!(&voters[index] == aux.ref_into(), "retraction index mismatch");
|
||||
|
||||
Self::remove_voter(aux.ref_into(), index, voters);
|
||||
<staking::Module<T>>::unreserve_balance(aux.ref_into(), Self::voting_bond());
|
||||
<staking::Module<T>>::unreserve(aux.ref_into(), Self::voting_bond());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -325,7 +330,7 @@ impl<T: Trait> Module<T> {
|
||||
"invalid candidate slot"
|
||||
);
|
||||
// NOTE: This must be last as it has side-effects.
|
||||
<staking::Module<T>>::deduct_unbonded(aux.ref_into(), Self::candidacy_bond())
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), Self::candidacy_bond())
|
||||
.map_err(|_| "candidate has not enough funds")?;
|
||||
|
||||
let mut candidates = candidates;
|
||||
@@ -345,10 +350,11 @@ impl<T: Trait> Module<T> {
|
||||
/// `signed` should have at least
|
||||
fn present_winner(
|
||||
aux: &T::PublicAux,
|
||||
candidate: T::AccountId,
|
||||
candidate: Address<T::AccountId, T::AccountIndex>,
|
||||
total: T::Balance,
|
||||
index: VoteIndex
|
||||
) -> Result {
|
||||
let candidate = <staking::Module<T>>::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<T: Trait> Module<T> {
|
||||
leaderboard[0] = (total, candidate);
|
||||
leaderboard.sort_by_key(|&(t, _)| t);
|
||||
<Leaderboard<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 _ = <staking::Module<T>>::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<T: Trait> Module<T> {
|
||||
/// 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<T::AccountId, T::AccountIndex>) -> Result {
|
||||
let who = <staking::Module<T>>::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<T: Trait> Module<T> {
|
||||
<NextFinalise<T>>::put((number + Self::presentation_duration(), empty_seats as u32, expiring));
|
||||
|
||||
let voters = Self::voters();
|
||||
let votes = voters.iter().map(<staking::Module<T>>::balance).collect::<Vec<_>>();
|
||||
let votes = voters.iter().map(<staking::Module<T>>::voting_balance).collect::<Vec<_>>();
|
||||
<SnapshotedStakes<T>>::put(votes);
|
||||
|
||||
// initialise leaderboard.
|
||||
@@ -487,7 +495,7 @@ impl<T: Trait> Module<T> {
|
||||
.take_while(|&&(b, _)| !b.is_zero())
|
||||
.take(coming as usize)
|
||||
{
|
||||
<staking::Module<T>>::refund(w, candidacy_bond);
|
||||
<staking::Module<T>>::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::<Test>{
|
||||
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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -145,7 +145,7 @@ impl<T: Trait> Module<T> {
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) {
|
||||
Self::voters_for(ref_index).iter()
|
||||
.map(|a| (<staking::Module<T>>::balance(a), Self::vote_of((ref_index, a.clone())).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| (<staking::Module<T>>::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<T: Trait> Module<T> {
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) -> Result {
|
||||
ensure!(value >= Self::minimum_deposit(), "value too low");
|
||||
<staking::Module<T>>::deduct_unbonded(aux.ref_into(), value)
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), value)
|
||||
.map_err(|_| "proposer's balance too low")?;
|
||||
|
||||
let index = Self::public_prop_count();
|
||||
@@ -172,7 +172,7 @@ impl<T: Trait> Module<T> {
|
||||
fn second(aux: &T::PublicAux, proposal: PropIndex) -> Result {
|
||||
let mut deposit = Self::deposit_of(proposal)
|
||||
.ok_or("can only second an existing proposal")?;
|
||||
<staking::Module<T>>::deduct_unbonded(aux.ref_into(), deposit.0)
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), deposit.0)
|
||||
.map_err(|_| "seconder's balance too low")?;
|
||||
deposit.1.push(aux.ref_into().clone());
|
||||
<DepositOf<T>>::insert(proposal, deposit);
|
||||
@@ -183,7 +183,7 @@ impl<T: Trait> Module<T> {
|
||||
/// 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!(!<staking::Module<T>>::balance(aux.ref_into()).is_zero(),
|
||||
ensure!(!<staking::Module<T>>::voting_balance(aux.ref_into()).is_zero(),
|
||||
"transactor must have balance to signal approval.");
|
||||
if !<VoteOf<T>>::exists(&(ref_index, aux.ref_into().clone())) {
|
||||
let mut voters = Self::voters_for(ref_index);
|
||||
@@ -261,7 +261,7 @@ impl<T: Trait> Module<T> {
|
||||
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (T::Balance, Vec<T::AccountId>) =
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
<staking::Module<T>>::refund(d, deposit);
|
||||
<staking::Module<T>>::unreserve(d, deposit);
|
||||
}
|
||||
<PublicProps<T>>::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::<Test>{
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[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",
|
||||
|
||||
@@ -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<Header = System::Header,Hash = System::Hash>,
|
||||
Block: traits::Block<Header=System::Header, Hash=System::Hash>,
|
||||
Lookup: AuxLookup<Source=<Block::Extrinsic as Checkable>::Address, Target=System::AccountId>,
|
||||
Payment: MakePayment<System::AccountId>,
|
||||
Finalisation: Executable,
|
||||
> Executive<System, Block, Payment, Finalisation> where
|
||||
Block::Extrinsic: Checkable + Slicable,
|
||||
<Block::Extrinsic as Checkable>::Checked: Applyable<Index = System::Index, AccountId = System::AccountId>
|
||||
> Executive<System, Block, Lookup, Payment, Finalisation> where
|
||||
Block::Extrinsic: Checkable<AccountId=System::AccountId> + Slicable,
|
||||
<Block::Extrinsic as Checkable>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>
|
||||
{
|
||||
/// Start the execution of a particular block.
|
||||
pub fn initialise_block(header: &System::Header) {
|
||||
@@ -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<ApplyOutcome, ApplyError> {
|
||||
let encoded = uxt.encode();
|
||||
let encoded_len = encoded.len();
|
||||
<system::Module<System>>::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<internal::ApplyOutcome, internal::ApplyError> {
|
||||
// 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 = <system::Module<System>>::account_index(xt.sender());
|
||||
assert!(xt.index() == &expected_index, "All extrinsics should have the correct nonce");
|
||||
let expected_index = <system::Module<System>>::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
|
||||
<system::Module<System>>::inc_account_index(xt.sender());
|
||||
<system::Module<System>>::inc_account_nonce(xt.sender());
|
||||
}
|
||||
|
||||
// decode parameters and dispatch
|
||||
xt.apply();
|
||||
let r = xt.apply();
|
||||
|
||||
<system::ExtrinsicIndex<System>>::put(<system::ExtrinsicIndex<System>>::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<Self::Target, &'static str> {
|
||||
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<Call<Test>>;
|
||||
type Executive = super::Executive<Test, Block<TestXt>, staking::Module<Test>, (session::Module<Test>, staking::Module<Test>)>;
|
||||
type Executive = super::Executive<Test, Block<TestXt>, NullLookup, staking::Module<Test>, (session::Module<Test>, staking::Module<Test>)>;
|
||||
|
||||
#[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!(<staking::Module<Test>>::balance(&1), 32);
|
||||
assert_eq!(<staking::Module<Test>>::balance(&2), 69);
|
||||
Executive::apply_extrinsic(xt).unwrap();
|
||||
assert_eq!(<staking::Module<Test>>::voting_balance(&1), 32);
|
||||
assert_eq!(<staking::Module<Test>>::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![], },
|
||||
},
|
||||
|
||||
@@ -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<AccountId, Index, Call> {
|
||||
pub struct Extrinsic<Address, Index, Call> {
|
||||
/// 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<AccountId, Index, Call> Slicable for Extrinsic<AccountId, Index, Call> where
|
||||
AccountId: Member + Slicable + MaybeDisplay,
|
||||
Index: Member + Slicable + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + Slicable
|
||||
impl<Address, Index, Call> Slicable for Extrinsic<Address, Index, Call> where
|
||||
Address: Member + Slicable + MaybeDisplay,
|
||||
Index: Member + Slicable + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Extrinsic {
|
||||
@@ -65,53 +65,83 @@ impl<AccountId, Index, Call> Slicable for Extrinsic<AccountId, Index, Call> 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<AccountId, Index, Call, Signature> {
|
||||
pub struct UncheckedExtrinsic<Address, Index, Call, Signature> {
|
||||
/// The actual extrinsic information.
|
||||
pub extrinsic: Extrinsic<AccountId, Index, Call>,
|
||||
/// The signature; should be an Ed25519 signature applied to the serialised `extrinsic` field.
|
||||
pub extrinsic: Extrinsic<Address, Index, Call>,
|
||||
/// The signature.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> traits::Checkable for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member + traits::Verify<Signer = AccountId>,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable,
|
||||
{
|
||||
type Checked = CheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
|
||||
fn check(self) -> Result<Self::Checked, Self> {
|
||||
if ::verify_encoded_lazy(&self.signature, &self.extrinsic, &self.extrinsic.signed) {
|
||||
Ok(CheckedExtrinsic(self))
|
||||
} else {
|
||||
Err(self)
|
||||
impl<Address, Index, Call, Signature> UncheckedExtrinsic<Address, Index, Call, Signature> {
|
||||
/// New instance.
|
||||
pub fn new(extrinsic: Extrinsic<Address, Index, Call>, signature: Signature) -> Self {
|
||||
UncheckedExtrinsic {
|
||||
extrinsic,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> UncheckedExtrinsic<AccountId, Index, Call, ::MaybeUnsigned<Signature>> where
|
||||
AccountId: Member + Default + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member + Default + traits::Verify<Signer = AccountId>,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable,
|
||||
impl<Address, AccountId, Index, Call, Signature> UncheckedExtrinsic<Address, Index, Call, ::MaybeUnsigned<Signature>> where
|
||||
Signature: traits::Verify<Signer=AccountId> + 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<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member + Slicable,
|
||||
impl<Address, AccountId, Index, Call, Signature> traits::Checkable
|
||||
for UncheckedExtrinsic<Address, Index, Call, ::MaybeUnsigned<Signature>>
|
||||
where
|
||||
Address: Member + Default + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: traits::Verify<Signer=AccountId> + Eq + Default,
|
||||
AccountId: Member + Default + MaybeDisplay,
|
||||
::MaybeUnsigned<Signature>: Member,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable,
|
||||
{
|
||||
type Address = Address;
|
||||
type AccountId = AccountId;
|
||||
type Checked = CheckedExtrinsic<AccountId, Index, Call>;
|
||||
|
||||
fn sender(&self) -> &Address {
|
||||
&self.extrinsic.signed
|
||||
}
|
||||
|
||||
fn check<ThisLookup>(self, lookup: ThisLookup) -> Result<Self::Checked, &'static str> where
|
||||
ThisLookup: FnOnce(Address) -> Result<AccountId, &'static str> + Send + Sync,
|
||||
{
|
||||
if !self.is_signed() {
|
||||
Ok(CheckedExtrinsic(Extrinsic {
|
||||
signed: Default::default(),
|
||||
index: self.extrinsic.index,
|
||||
function: self.extrinsic.function,
|
||||
}))
|
||||
} else {
|
||||
let extrinsic: Extrinsic<AccountId, Index, Call>
|
||||
= 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<Address, Index, Call, Signature> Slicable for UncheckedExtrinsic<Address, Index, Call, Signature> where
|
||||
Signature: Slicable,
|
||||
Extrinsic<Address, Index, Call>: Slicable,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
@@ -120,10 +150,10 @@ impl<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountI
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = Slicable::decode(input)?;
|
||||
|
||||
Some(UncheckedExtrinsic {
|
||||
extrinsic: Slicable::decode(input)?,
|
||||
signature: Slicable::decode(input)?,
|
||||
})
|
||||
Some(UncheckedExtrinsic::new(
|
||||
Slicable::decode(input)?,
|
||||
Slicable::decode(input)?
|
||||
))
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
@@ -133,9 +163,6 @@ impl<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountI
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
/* self.extrinsic.signed.using_encoded(|s| v.extend(s));
|
||||
self.extrinsic.index.using_encoded(|s| v.extend(s));
|
||||
self.extrinsic.function.using_encoded(|s| v.extend(s));*/
|
||||
self.extrinsic.using_encoded(|s| v.extend(s));
|
||||
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
@@ -147,11 +174,12 @@ impl<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountI
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: use derive when possible.
|
||||
#[cfg(feature = "std")]
|
||||
impl<AccountId, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: fmt::Debug,
|
||||
Index: fmt::Debug,
|
||||
Call: fmt::Debug,
|
||||
impl<Address, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<Address, Index, Call, Signature> 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<AccountId, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<Accoun
|
||||
/// A type-safe indicator that a extrinsic has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
(UncheckedExtrinsic<AccountId, Index, Call, Signature>);
|
||||
pub struct CheckedExtrinsic<AccountId, Index, Call>
|
||||
(Extrinsic<AccountId, Index, Call>);
|
||||
|
||||
impl<AccountId, Index, Call, Signature> CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
impl<AccountId, Index, Call> ops::Deref
|
||||
for CheckedExtrinsic<AccountId, Index, Call>
|
||||
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<AccountId, Index, Call, Signature> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get a reference to the checked signature.
|
||||
pub fn into_unchecked(self) -> UncheckedExtrinsic<AccountId, Index, Call, Signature> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> ops::Deref
|
||||
for CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
{
|
||||
type Target = Extrinsic<AccountId, Index, Call>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0.extrinsic
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
impl<AccountId, Index, Call> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Index, Call>
|
||||
where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + AuxDispatchable<Aux = AccountId>,
|
||||
Signature: Member
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + AuxDispatchable<Aux = AccountId>,
|
||||
{
|
||||
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<Item> {
|
||||
}
|
||||
|
||||
impl<Item> Slicable for Digest<Item> where
|
||||
Item: Member + Default + Slicable
|
||||
Item: Member + Default + Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Digest { logs: Slicable::decode(input)? })
|
||||
@@ -244,7 +247,7 @@ impl<Item> Slicable for Digest<Item> where
|
||||
}
|
||||
}
|
||||
impl<Item> traits::Digest for Digest<Item> where
|
||||
Item: Member + Default + Slicable
|
||||
Item: Member + Default + Slicable
|
||||
{
|
||||
type Item = Item;
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
@@ -338,7 +341,7 @@ impl<Number, Hashing, DigestItem> Slicable for Header<Number, Hashing, DigestIte
|
||||
}
|
||||
|
||||
impl<Number, Hashing, DigestItem> traits::Header for Header<Number, Hashing, DigestItem> 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<Number, Hashing, DigestItem> traits::Header for Header<Number, Hashing, Dig
|
||||
}
|
||||
|
||||
impl<Number, Hashing, DigestItem> Header<Number, Hashing, DigestItem> 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<Header: Slicable, Extrinsic: Slicable> Slicable for Block<Header, Extrinsic
|
||||
impl<Header, Extrinsic> traits::Block for Block<Header, Extrinsic>
|
||||
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();
|
||||
|
||||
@@ -83,6 +83,61 @@ impl From<H512> 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<I: codec::Input>(input: &mut I) -> Option<Self> {
|
||||
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, F: FnOnce(&[u8]) -> 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<I: codec::Input>(input: &mut I) -> Option<Self> {
|
||||
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, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(&[*self as u8])
|
||||
}
|
||||
}
|
||||
|
||||
/// Result from attempt to apply an extrinsic.
|
||||
pub type ApplyResult = Result<ApplyOutcome, ApplyError>;
|
||||
|
||||
/// Potentially "unsigned" signature verification.
|
||||
#[derive(Eq, PartialEq, Clone, Default)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
@@ -92,8 +147,12 @@ impl<T: Verify> MaybeUnsigned<T> where
|
||||
T: Default + Eq,
|
||||
<T as Verify>::Signer: Default + Eq,
|
||||
{
|
||||
fn is_signed(&self, signer: &<Self as Verify>::Signer) -> bool {
|
||||
self.0 != T::default() || signer != &<Self as Verify>::Signer::default()
|
||||
fn is_signed(&self) -> bool {
|
||||
self.0 != T::default()
|
||||
}
|
||||
|
||||
fn is_addressed(&self, signer: &<Self as Verify>::Signer) -> bool {
|
||||
signer != &Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,8 +162,8 @@ impl<T: Verify> Verify for MaybeUnsigned<T> where
|
||||
{
|
||||
type Signer = T::Signer;
|
||||
fn verify<L: Lazy<[u8]>>(&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)
|
||||
}
|
||||
|
||||
@@ -159,12 +159,15 @@ impl<Call: AuxDispatchable + Slicable + Sized + Send + Sync + Serialize + Deseri
|
||||
}
|
||||
impl<Call: 'static + AuxDispatchable + Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Checkable for TestXt<Call> {
|
||||
type Checked = Self;
|
||||
fn check(self) -> Result<Self, Self> { Ok(self) }
|
||||
type Address = u64;
|
||||
type AccountId = u64;
|
||||
fn sender(&self) -> &u64 { &(self.0).0 }
|
||||
fn check<ThisLookup: FnOnce(Self::Address) -> Result<Self::AccountId, &'static str> + Send + Sync>(self, _lookup: ThisLookup) -> Result<Self::Checked, &'static str> { Ok(self) }
|
||||
}
|
||||
impl<Call: AuxDispatchable<Aux = u64> + Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Applyable for TestXt<Call> {
|
||||
type AccountId = u64;
|
||||
type Index = u64;
|
||||
fn sender(&self) -> &u64 { &(self.0).0 }
|
||||
fn index(&self) -> &u64 { &(self.0).1 }
|
||||
fn apply(self) { (self.0).2.dispatch(&(self.0).0); }
|
||||
fn apply(self) -> Result<(), &'static str> { (self.0).2.dispatch(&(self.0).0) }
|
||||
}
|
||||
|
||||
@@ -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<L: Lazy<[u8]>>(&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<Self::Target, &'static str>;
|
||||
}
|
||||
|
||||
/// Simple payment making trait, operating on a single generic `AccountId` type.
|
||||
pub trait MakePayment<AccountId> {
|
||||
/// 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<T> MakePayment<T> 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<T: Serialize + Debug> MaybeSerializeDebugButNotDeserialize for T {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait MaybeSerializeDebugButNotDeserialize {}
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> MaybeSerializeDebugButNotDeserialize for T {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait MaybeSerializeDebug: Serialize + DeserializeOwned + Debug {}
|
||||
#[cfg(feature = "std")]
|
||||
@@ -352,8 +372,30 @@ pub type HashingFor<B> = <<B as Block>::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<Self::Checked, Self>;
|
||||
fn sender(&self) -> &Self::Address;
|
||||
fn check<ThisLookup: FnOnce(Self::Address) -> Result<Self::AccountId, &'static str> + Send + Sync>(self, lookup: ThisLookup) -> Result<Self::Checked, &'static str>;
|
||||
}
|
||||
|
||||
/// 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<Self::Checked, &'static str>;
|
||||
}
|
||||
|
||||
impl<T: BlindCheckable> Checkable for T {
|
||||
type Address = <Self as BlindCheckable>::Address;
|
||||
type AccountId = <Self as BlindCheckable>::Address;
|
||||
type Checked = <Self as BlindCheckable>::Checked;
|
||||
fn sender(&self) -> &Self::Address { BlindCheckable::sender(self) }
|
||||
fn check<ThisLookup: FnOnce(Self::Address) -> Result<Self::AccountId, &'static str> + Send + Sync>(self, _: ThisLookup) -> Result<Self::Checked, &'static str> { 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>;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<AccountId, AccountIndex> 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<AccountId, AccountIndex> fmt::Display for Address<AccountId, AccountIndex> where
|
||||
AccountId: Member,
|
||||
AccountIndex: Member,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, AccountIndex> From<AccountId> for Address<AccountId, AccountIndex> where
|
||||
AccountId: Member,
|
||||
AccountIndex: Member,
|
||||
{
|
||||
fn from(a: AccountId) -> Self {
|
||||
Address::Id(a)
|
||||
}
|
||||
}
|
||||
|
||||
fn need_more_than<T: PartialOrd>(a: T, b: T) -> Option<T> {
|
||||
if a < b { Some(a) } else { None }
|
||||
}
|
||||
|
||||
impl<AccountId, AccountIndex> Slicable for Address<AccountId, AccountIndex> where
|
||||
AccountId: Member + Slicable,
|
||||
AccountIndex: Member + Slicable + PartialOrd<AccountIndex> + Ord + As<u32> + As<u16> + As<u8> + Copy,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
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<u8> {
|
||||
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::<u32>::as_(i).using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Address::Index(i) if i >= As::sa(0xf0u32) => {
|
||||
v.push(252);
|
||||
As::<u16>::as_(i).using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Address::Index(i) => v.push(As::<u8>::as_(i)),
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, AccountIndex> Default for Address<AccountId, AccountIndex> where
|
||||
AccountId: Member + Default,
|
||||
AccountIndex: Member,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Address::Id(Default::default())
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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 = <Self as HasPublicAux>::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::<Test>::default().build_externalities();
|
||||
let balance_factor = if ext_deposit > 0 {
|
||||
256
|
||||
} else {
|
||||
1
|
||||
};
|
||||
t.extend(consensus::GenesisConfig::<Test>{
|
||||
code: vec![],
|
||||
authorities: vec![],
|
||||
}.build_externalities());
|
||||
t.extend(session::GenesisConfig::<Test>{
|
||||
session_length,
|
||||
validators: vec![10, 20],
|
||||
}.build_externalities());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
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<Test>;
|
||||
pub type Session = session::Module<Test>;
|
||||
pub type Staking = Module<Test>;
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::insert(1, 111);
|
||||
<Bondage<Test>>::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), || {
|
||||
<FreeBalance<Test>>::insert(1, 42);
|
||||
<ReservedBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::insert(1, 110);
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::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), || {
|
||||
<FreeBalance<Test>>::insert(1, 110);
|
||||
<FreeBalance<Test>>::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);
|
||||
});
|
||||
}
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[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",
|
||||
|
||||
@@ -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<H: Hashing>(xts: Vec<Vec<u8>>) -> 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<T: Trait>;
|
||||
|
||||
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<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Increment a particular account's nonce by 1.
|
||||
pub fn inc_account_index(who: &T::AccountId) {
|
||||
<AccountIndex<T>>::insert(who, Self::account_index(who) + T::Index::one());
|
||||
pub fn inc_account_nonce(who: &T::AccountId) {
|
||||
<AccountNonce<T>>::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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Self, Self> {
|
||||
fn sender(&self) -> &Self::Address {
|
||||
&self.transfer.from
|
||||
}
|
||||
fn check(self) -> Result<Self, &'static str> {
|
||||
if ::runtime_primitives::verify_encoded_lazy(&self.signature, &self.transfer, &self.transfer.from) {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(self)
|
||||
Err("bad signature")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user