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:
Gav Wood
2018-06-18 20:23:41 +02:00
committed by GitHub
parent aebfd38250
commit e53c17d646
67 changed files with 2346 additions and 1272 deletions
@@ -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
+106 -86
View File
@@ -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;