mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-10 15:47:33 +00:00
Introduce Runtime Events (#607)
* Squashed commit. New slashing mechanism (#554) … * Slashing improvements - unstake when balance too low - unstake after N slashes according to val prefs - don't early-terminate session/era unless unstaked - offline grace period before punishment * Fix warning * Cleanups and ensure slash_count decays * Bump authoring version and introduce needed authoring stub * Rename * Fix offline tracker * Fix offline tracker * Renames * Add test * Tests * Tests. Remove accidental merge files. Merge remote-tracking branch 'origin/master' into gav-new-pos Version bump, fixes (#572) … * Bump version, don't propose invalid blocks * Fix build. * Fixes. * More fixes. * Fix tests. * Fix more tests * More tests fixed Fix merge Fix accidental merge bug Fixes. Staking failsafes … - Don't slash/unstake/change session when too few staking participants - Introduce set_balance PrivCall Make minimum validator count dynamic. test fixes Fix tests. Fix tests Fix tests, update readme. Merge remote-tracking branch 'origin/master' into gav-new-pos Test with release. Use safe math when dealing with total stake Fix test again. Introduce events into runtime. Fix tests Add events for account new/reap Integration-style tests for events. * Remove old code
This commit is contained in:
@@ -83,3 +83,26 @@ macro_rules! assert_ok {
|
||||
assert_eq!($x, Ok($y));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_event {
|
||||
($(#[$attr:meta])* pub enum $name:ident for $trait:ident { $( $module:ident ),* }) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum $name {
|
||||
$(
|
||||
$module($module::Event<$trait>),
|
||||
)*
|
||||
}
|
||||
$(
|
||||
impl From<$module::Event<$trait>> for $name {
|
||||
fn from(x: $module::Event<$trait>) -> Self {
|
||||
$name::$module(x)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
|
||||
if let staking::UpdateBalanceOutcome::AccountKilled =
|
||||
staking::Module::<T>::set_free_balance_creating(&address, balance)
|
||||
{
|
||||
// Account killed. This will ultimately lead to calling `OnAccountKill` callback
|
||||
// Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback
|
||||
// which will make removal of CodeOf and StorageOf for this account.
|
||||
// In order to avoid writing over the deleted properties we `continue` here.
|
||||
continue;
|
||||
|
||||
@@ -247,8 +247,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> staking::OnAccountKill<T::AccountId> for Module<T> {
|
||||
fn on_account_kill(who: &T::AccountId) {
|
||||
impl<T: Trait> staking::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
fn on_free_balance_zero(who: &T::AccountId) {
|
||||
<CodeOf<T>>::remove(who);
|
||||
<StorageOf<T>>::remove_prefix(who.clone());
|
||||
}
|
||||
|
||||
@@ -44,20 +44,23 @@ impl system::Trait for Test {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
type Moment = u64;
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnAccountKill = Contract;
|
||||
type OnFreeBalanceZero = Contract;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = Staking;
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Gas = u64;
|
||||
@@ -89,7 +92,6 @@ fn new_test_ext(existential_deposit: u64, gas_price: u64) -> runtime_io::TestExt
|
||||
session::GenesisConfig::<Test> {
|
||||
session_length: 1,
|
||||
validators: vec![10, 20],
|
||||
broken_percent_late: 100,
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
@@ -651,16 +651,19 @@ mod tests {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = staking::Module<Test>;
|
||||
type Event = ();
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnAccountKill = ();
|
||||
type OnFreeBalanceZero = ();
|
||||
type Event = ();
|
||||
}
|
||||
impl democracy::Trait for Test {
|
||||
type Proposal = Proposal;
|
||||
@@ -680,7 +683,6 @@ mod tests {
|
||||
t.extend(session::GenesisConfig::<Test>{
|
||||
session_length: 1, //??? or 2?
|
||||
validators: vec![10, 20],
|
||||
broken_percent_late: 100,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test>{
|
||||
sessions_per_era: 1,
|
||||
|
||||
@@ -393,16 +393,19 @@ mod tests {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = staking::Module<Test>;
|
||||
type Event = ();
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnAccountKill = ();
|
||||
type OnFreeBalanceZero = ();
|
||||
type Event = ();
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
@@ -421,7 +424,6 @@ mod tests {
|
||||
t.extend(session::GenesisConfig::<Test>{
|
||||
session_length: 1, //??? or 2?
|
||||
validators: vec![10, 20],
|
||||
broken_percent_late: 100,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test>{
|
||||
sessions_per_era: 1,
|
||||
|
||||
@@ -8,6 +8,7 @@ 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-codec-derive = { path = "../../codec/derive", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
@@ -29,6 +30,7 @@ std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"substrate-codec/std",
|
||||
"substrate-codec-derive/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-system/std",
|
||||
|
||||
@@ -24,12 +24,19 @@ extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate substrate_codec_derive;
|
||||
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_system as system;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
|
||||
@@ -52,7 +59,6 @@ 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, Hash, AuxLookup};
|
||||
use codec::{Codec, Encode};
|
||||
@@ -125,6 +131,7 @@ impl<
|
||||
extrinsics.into_iter().for_each(Self::apply_extrinsic_no_note);
|
||||
|
||||
// post-transactional book-keeping.
|
||||
<system::Module<System>>::note_finished_extrinsics();
|
||||
Finalisation::execute();
|
||||
|
||||
// any final checks
|
||||
@@ -134,6 +141,7 @@ impl<
|
||||
/// Finalise the block - it is up the caller to ensure that all header fields are valid
|
||||
/// except state-root.
|
||||
pub fn finalise_block() -> System::Header {
|
||||
<system::Module<System>>::note_finished_extrinsics();
|
||||
Finalisation::execute();
|
||||
|
||||
// setup extrinsics
|
||||
@@ -194,7 +202,7 @@ impl<
|
||||
// decode parameters and dispatch
|
||||
let r = xt.apply();
|
||||
|
||||
<system::ExtrinsicIndex<System>>::put(<system::ExtrinsicIndex<System>>::get() + 1u32);
|
||||
<system::Module<System>>::note_applied_extrinsic();
|
||||
|
||||
r.map(|_| internal::ApplyOutcome::Success).or_else(|e| Ok(internal::ApplyOutcome::Fail(e)))
|
||||
}
|
||||
@@ -232,6 +240,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_event!{
|
||||
pub enum MetaEvent for Test {
|
||||
session, staking
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Test;
|
||||
@@ -250,16 +264,19 @@ mod tests {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = staking::Module<Test>;
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnAccountKill = ();
|
||||
type OnFreeBalanceZero = ();
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
@@ -319,7 +336,7 @@ mod tests {
|
||||
// Blake
|
||||
// state_root: hex!("02532989c613369596025dfcfc821339fc9861987003924913a5a1382f87034a").into(),
|
||||
// Keccak
|
||||
state_root: hex!("ed456461b82664990b6ebd1caf1360056f6e8a062e73fada331e1c92cd81cad4").into(),
|
||||
state_root: hex!("246ea6d86eefe3fc32f746fdcb1749a5f245570c70a04b43d08b5defac44505a").into(),
|
||||
extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
@@ -353,7 +370,7 @@ mod tests {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("ed456461b82664990b6ebd1caf1360056f6e8a062e73fada331e1c92cd81cad4").into(),
|
||||
state_root: hex!("246ea6d86eefe3fc32f746fdcb1749a5f245570c70a04b43d08b5defac44505a").into(),
|
||||
extrinsics_root: [0u8; 32].into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
@@ -369,7 +386,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default()));
|
||||
assert!(Executive::apply_extrinsic(xt).is_err());
|
||||
assert_eq!(<system::Module<Test>>::extrinsic_index(), 0);
|
||||
assert_eq!(<system::Module<Test>>::extrinsic_index(), Some(0));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ pub struct Identity;
|
||||
impl<T> Convert<T, T> for Identity {
|
||||
fn convert(a: T) -> T { a }
|
||||
}
|
||||
pub struct Empty;
|
||||
impl<T> Convert<T, ()> for Empty {
|
||||
fn convert(_: T) -> () { () }
|
||||
}
|
||||
|
||||
pub trait MaybeEmpty {
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
@@ -10,6 +10,7 @@ serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { version = "1.0", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-codec-derive = { path = "../../codec/derive", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
@@ -27,6 +28,7 @@ std = [
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
"substrate-codec-derive/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
|
||||
@@ -38,6 +38,9 @@ extern crate substrate_runtime_std as rstd;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_codec_derive;
|
||||
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
@@ -46,7 +49,7 @@ extern crate substrate_runtime_system as system;
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{Zero, One, RefInto, MaybeEmpty, Executable, Convert, As};
|
||||
use primitives::traits::{Zero, One, RefInto, Executable, Convert, As};
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use runtime_support::dispatch::Result;
|
||||
|
||||
@@ -64,20 +67,19 @@ impl<T> OnSessionChange<T> for () {
|
||||
}
|
||||
|
||||
pub trait Trait: timestamp::Trait {
|
||||
// the position of the required note_missed_proposal extrinsic.
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32;
|
||||
|
||||
type ConvertAccountIdToSessionKey: Convert<Self::AccountId, Self::SessionKey>;
|
||||
type OnSessionChange: OnSessionChange<Self::Moment>;
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
|
||||
pub type Event<T> = RawEvent<<T as system::Trait>::BlockNumber>;
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set_key(aux, key: T::SessionKey) -> Result = 0;
|
||||
fn note_offline(aux, offline_val_indices: Vec<u32>) -> Result = 1;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
@@ -87,6 +89,19 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
/// An event in this module.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone)]
|
||||
pub enum RawEvent<BlockNumber> {
|
||||
/// New session has happened. Note that the argument is the session index, not the block number
|
||||
/// as the type might suggest.
|
||||
NewSession(BlockNumber),
|
||||
}
|
||||
|
||||
impl<N> From<RawEvent<N>> for () {
|
||||
fn from(_: RawEvent<N>) -> () { () }
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait>;
|
||||
|
||||
@@ -98,8 +113,6 @@ decl_storage! {
|
||||
pub CurrentIndex get(current_index): b"ses:ind" => required T::BlockNumber;
|
||||
// Timestamp when current session started.
|
||||
pub CurrentStart get(current_start): b"ses:current_start" => required T::Moment;
|
||||
// Percent by which the session must necessarily finish late before we early-exit the session.
|
||||
pub BrokenPercentLate get(broken_percent_late): b"ses:broken_percent_late" => required T::Moment;
|
||||
|
||||
// Opinions of the current validator set about the activeness of their peers.
|
||||
// Gets cleared when the validator set changes.
|
||||
@@ -147,20 +160,6 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Notes which of the validators appear to be online from the point of the view of the block author.
|
||||
pub fn note_offline(aux: &T::PublicAux, offline_val_indices: Vec<u32>) -> Result {
|
||||
assert!(aux.is_empty());
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == T::NOTE_MISSED_PROPOSAL_POSITION,
|
||||
"note_missed_proposal extrinsic must be at position {} in the block",
|
||||
T::NOTE_MISSED_PROPOSAL_POSITION
|
||||
);
|
||||
|
||||
let vs = Self::validators();
|
||||
<BadValidators<T>>::put(offline_val_indices.into_iter().map(|i| vs[i as usize].clone()).collect::<Vec<T::AccountId>>());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// INTERNAL API (available to other runtime modules)
|
||||
|
||||
/// Set the current set of validators.
|
||||
@@ -188,13 +187,21 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
fn deposit_event(event: Event<T>) {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
/// Move onto next session: register the new authority set.
|
||||
pub fn rotate_session(is_final_block: bool, apply_rewards: bool) {
|
||||
let now = <timestamp::Module<T>>::get();
|
||||
let time_elapsed = now.clone() - Self::current_start();
|
||||
let session_index = <CurrentIndex<T>>::get() + One::one();
|
||||
|
||||
Self::deposit_event(RawEvent::NewSession(session_index));
|
||||
|
||||
// Increment current session index.
|
||||
<CurrentIndex<T>>::put(<CurrentIndex<T>>::get() + One::one());
|
||||
<CurrentIndex<T>>::put(session_index);
|
||||
<CurrentStart<T>>::put(now);
|
||||
|
||||
// Enact era length change.
|
||||
@@ -250,7 +257,6 @@ impl<T: Trait> Executable for Module<T> {
|
||||
pub struct GenesisConfig<T: Trait> {
|
||||
pub session_length: T::BlockNumber,
|
||||
pub validators: Vec<T::AccountId>,
|
||||
pub broken_percent_late: T::Moment,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
@@ -260,7 +266,6 @@ impl<T: Trait> Default for GenesisConfig<T> {
|
||||
GenesisConfig {
|
||||
session_length: T::BlockNumber::sa(1000),
|
||||
validators: vec![],
|
||||
broken_percent_late: T::Moment::sa(30),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,8 +281,7 @@ impl<T: Trait> primitives::BuildStorage for GenesisConfig<T>
|
||||
Self::hash(<SessionLength<T>>::key()).to_vec() => self.session_length.encode(),
|
||||
Self::hash(<CurrentIndex<T>>::key()).to_vec() => T::BlockNumber::sa(0).encode(),
|
||||
Self::hash(<CurrentStart<T>>::key()).to_vec() => T::Moment::zero().encode(),
|
||||
Self::hash(<Validators<T>>::key()).to_vec() => self.validators.encode(),
|
||||
Self::hash(<BrokenPercentLate<T>>::key()).to_vec() => self.broken_percent_late.encode()
|
||||
Self::hash(<Validators<T>>::key()).to_vec() => self.validators.encode()
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -308,15 +312,16 @@ mod tests {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
type Moment = u64;
|
||||
}
|
||||
impl Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = ();
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
type System = system::Module<Test>;
|
||||
@@ -336,7 +341,6 @@ mod tests {
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
session_length: 2,
|
||||
validators: vec![1, 2, 3],
|
||||
broken_percent_late: 30,
|
||||
}.build_storage().unwrap());
|
||||
t.into()
|
||||
}
|
||||
|
||||
@@ -77,6 +77,12 @@ const RECLAIM_INDEX_MAGIC: usize = 0x69;
|
||||
|
||||
pub type Address<T> = RawAddress<<T as system::Trait>::AccountId, <T as Trait>::AccountIndex>;
|
||||
|
||||
pub type Event<T> = RawEvent<
|
||||
<T as Trait>::Balance,
|
||||
<T as system::Trait>::AccountId,
|
||||
<T as Trait>::AccountIndex
|
||||
>;
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum LockStatus<BlockNumber: Debug + PartialEq + Clone> {
|
||||
@@ -94,13 +100,13 @@ pub enum LockStatus<BlockNumber: PartialEq + Clone> {
|
||||
}
|
||||
|
||||
/// The account was the given id was killed.
|
||||
pub trait OnAccountKill<AccountId> {
|
||||
pub trait OnFreeBalanceZero<AccountId> {
|
||||
/// The account was the given id was killed.
|
||||
fn on_account_kill(who: &AccountId);
|
||||
fn on_free_balance_zero(who: &AccountId);
|
||||
}
|
||||
|
||||
impl<AccountId> OnAccountKill<AccountId> for () {
|
||||
fn on_account_kill(_who: &AccountId) {}
|
||||
impl<AccountId> OnFreeBalanceZero<AccountId> for () {
|
||||
fn on_free_balance_zero(_who: &AccountId) {}
|
||||
}
|
||||
|
||||
/// Preference of what happens on a slash event.
|
||||
@@ -121,17 +127,21 @@ impl Default for SlashPreference {
|
||||
|
||||
pub trait Trait: system::Trait + session::Trait {
|
||||
/// The allowed extrinsic position for `missed_proposal` inherent.
|
||||
// const NOTE_MISSED_PROPOSAL_POSITION: u32; // TODO: uncomment when removed from session::Trait
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32;
|
||||
|
||||
/// The balance of an account.
|
||||
type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As<Self::AccountIndex> + As<usize> + As<u64>;
|
||||
/// Type used for storing an account's index; implies the maximum number of accounts the system
|
||||
/// can hold.
|
||||
type AccountIndex: Parameter + Member + Codec + SimpleArithmetic + As<u8> + As<u16> + As<u32> + As<u64> + As<usize> + Copy;
|
||||
/// A function which is invoked when the given account is dead.
|
||||
/// A function which is invoked when the free-balance has fallen below the existential deposit and
|
||||
/// has been reduced to zero.
|
||||
///
|
||||
/// Gives a chance to clean up resources associated with the given account.
|
||||
type OnAccountKill: OnAccountKill<Self::AccountId>;
|
||||
type OnFreeBalanceZero: OnFreeBalanceZero<Self::AccountId>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
@@ -159,6 +169,26 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
/// An event in this module.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone)]
|
||||
pub enum RawEvent<Balance, AccountId, AccountIndex> {
|
||||
/// All validators have been rewarded by the given balance.
|
||||
Reward(Balance),
|
||||
/// One validator (and their nominators) has been given a offline-warning (they're still within
|
||||
/// their grace). The accrued number of slashes is recorded, too.
|
||||
OfflineWarning(AccountId, u32),
|
||||
/// One validator (and their nominators) has been slashed by the given amount.
|
||||
OfflineSlash(AccountId, Balance),
|
||||
/// A new account was created.
|
||||
NewAccount(AccountId, AccountIndex, NewAccountOutcome),
|
||||
/// An account was reaped.
|
||||
ReapedAccount(AccountId),
|
||||
}
|
||||
impl<B, A, I> From<RawEvent<B, A, I>> for () {
|
||||
fn from(_: RawEvent<B, A, I>) -> () { () }
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait>;
|
||||
|
||||
@@ -227,7 +257,7 @@ decl_storage! {
|
||||
// This is the only balance that matters in terms of most operations on tokens. It is
|
||||
// alone used to determine the balance when in the contract execution environment. When this
|
||||
// balance falls below the value of `ExistentialDeposit`, then the "current account" is
|
||||
// deleted: specifically, `Bondage` and `FreeBalance`. Furthermore, `OnAccountKill` callback
|
||||
// deleted: specifically, `Bondage` and `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback
|
||||
// is invoked, giving a chance to external modules to cleanup data associated with
|
||||
// the deleted account.
|
||||
//
|
||||
@@ -253,7 +283,10 @@ decl_storage! {
|
||||
pub Bondage get(bondage): b"sta:bon:" => default map [ T::AccountId => T::BlockNumber ];
|
||||
}
|
||||
|
||||
enum NewAccountOutcome {
|
||||
/// Whatever happened about the hint given when creating the new account.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum NewAccountOutcome {
|
||||
NoHint,
|
||||
GoodHint,
|
||||
BadHint,
|
||||
@@ -485,7 +518,7 @@ impl<T: Trait> Module<T> {
|
||||
fn note_missed_proposal(aux: &T::PublicAux, offline_val_indices: Vec<u32>) -> Result {
|
||||
assert!(aux.is_empty());
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == T::NOTE_MISSED_PROPOSAL_POSITION,
|
||||
<system::Module<T>>::extrinsic_index() == Some(T::NOTE_MISSED_PROPOSAL_POSITION),
|
||||
"note_missed_proposal extrinsic must be at position {} in the block",
|
||||
T::NOTE_MISSED_PROPOSAL_POSITION
|
||||
);
|
||||
@@ -496,7 +529,7 @@ impl<T: Trait> Module<T> {
|
||||
<SlashCount<T>>::insert(v.clone(), slash_count + 1);
|
||||
let grace = Self::offline_slash_grace();
|
||||
|
||||
if slash_count >= grace {
|
||||
let event = if slash_count >= grace {
|
||||
let instances = slash_count - grace;
|
||||
let slash = Self::early_era_slash() << instances;
|
||||
let next_slash = slash << 1u32;
|
||||
@@ -512,7 +545,11 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
let _ = Self::force_new_era(false);
|
||||
}
|
||||
}
|
||||
RawEvent::OfflineSlash(v, slash)
|
||||
} else {
|
||||
RawEvent::OfflineWarning(v, slash_count)
|
||||
};
|
||||
Self::deposit_event(event);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -520,6 +557,11 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
// PRIV DISPATCH
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
fn deposit_event(event: Event<T>) {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
/// Set the number of sessions in an era.
|
||||
fn set_sessions_per_era(new: T::BlockNumber) -> Result {
|
||||
<NextSessionsPerEra<T>>::put(&new);
|
||||
@@ -806,6 +848,7 @@ impl<T: Trait> Module<T> {
|
||||
for v in <session::Module<T>>::validators().iter() {
|
||||
Self::reward_validator(v, reward);
|
||||
}
|
||||
Self::deposit_event(RawEvent::Reward(reward));
|
||||
}
|
||||
|
||||
let session_index = <session::Module<T>>::current_index();
|
||||
@@ -911,7 +954,9 @@ impl<T: Trait> Module<T> {
|
||||
try_set[item_index] = who.clone();
|
||||
<EnumSet<T>>::insert(set_index, try_set);
|
||||
|
||||
return NewAccountOutcome::GoodHint;
|
||||
Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint));
|
||||
|
||||
return NewAccountOutcome::GoodHint
|
||||
}
|
||||
}
|
||||
NewAccountOutcome::BadHint
|
||||
@@ -931,6 +976,8 @@ impl<T: Trait> Module<T> {
|
||||
set_index += One::one();
|
||||
};
|
||||
|
||||
let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len());
|
||||
|
||||
// update set.
|
||||
set.push(who.clone());
|
||||
|
||||
@@ -942,18 +989,26 @@ impl<T: Trait> Module<T> {
|
||||
// write set.
|
||||
<EnumSet<T>>::insert(set_index, set);
|
||||
|
||||
Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret));
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn reap_account(who: &T::AccountId) {
|
||||
<system::AccountNonce<T>>::remove(who);
|
||||
Self::deposit_event(RawEvent::ReapedAccount(who.clone()));
|
||||
}
|
||||
|
||||
/// Kill an account's free portion.
|
||||
fn on_free_too_low(who: &T::AccountId) {
|
||||
Self::decrease_total_stake_by(Self::free_balance(who));
|
||||
<FreeBalance<T>>::remove(who);
|
||||
<Bondage<T>>::remove(who);
|
||||
T::OnAccountKill::on_account_kill(who);
|
||||
|
||||
T::OnFreeBalanceZero::on_free_balance_zero(who);
|
||||
|
||||
if Self::reserved_balance(who).is_zero() {
|
||||
<system::AccountNonce<T>>::remove(who);
|
||||
Self::reap_account(who);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -961,8 +1016,9 @@ impl<T: Trait> Module<T> {
|
||||
fn on_reserved_too_low(who: &T::AccountId) {
|
||||
Self::decrease_total_stake_by(Self::reserved_balance(who));
|
||||
<ReservedBalance<T>>::remove(who);
|
||||
|
||||
if Self::free_balance(who).is_zero() {
|
||||
<system::AccountNonce<T>>::remove(who);
|
||||
Self::reap_account(who);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,20 +43,23 @@ impl system::Trait for Test {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 0;
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = Staking;
|
||||
type Event = ();
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
type Moment = u64;
|
||||
}
|
||||
impl Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnAccountKill = ();
|
||||
type OnFreeBalanceZero = ();
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool, reward: u64) -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
@@ -73,7 +76,6 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64
|
||||
t.extend(session::GenesisConfig::<Test>{
|
||||
session_length,
|
||||
validators: vec![10, 20],
|
||||
broken_percent_late: 30,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
sessions_per_era,
|
||||
|
||||
@@ -28,6 +28,7 @@ fn note_null_missed_proposal_should_work() {
|
||||
assert_eq!(Staking::offline_slash_grace(), 0);
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 1);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![]));
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 1);
|
||||
@@ -42,6 +43,7 @@ fn note_missed_proposal_should_work() {
|
||||
assert_eq!(Staking::offline_slash_grace(), 0);
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 70);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::free_balance(&10), 50);
|
||||
@@ -56,9 +58,11 @@ fn note_missed_proposal_exponent_should_work() {
|
||||
assert_eq!(Staking::offline_slash_grace(), 0);
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 150);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::free_balance(&10), 130);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
assert_eq!(Staking::slash_count(&10), 2);
|
||||
assert_eq!(Staking::free_balance(&10), 90);
|
||||
@@ -77,12 +81,14 @@ fn note_missed_proposal_grace_should_work() {
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 70);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::free_balance(&10), 70);
|
||||
assert_eq!(Staking::slash_count(&20), 0);
|
||||
assert_eq!(Staking::free_balance(&20), 70);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::slash_count(&10), 2);
|
||||
assert_eq!(Staking::free_balance(&10), 50);
|
||||
@@ -104,11 +110,13 @@ fn note_missed_proposal_force_unstake_session_change_should_work() {
|
||||
assert_eq!(Staking::intentions(), vec![10, 20, 1]);
|
||||
assert_eq!(Session::validators(), vec![10, 20]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
assert_eq!(Staking::free_balance(&10), 50);
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::intentions(), vec![10, 20, 1]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
assert_eq!(Staking::intentions(), vec![1, 20]);
|
||||
assert_eq!(Staking::free_balance(&10), 10);
|
||||
@@ -125,23 +133,27 @@ fn note_missed_proposal_auto_unstake_session_change_should_work() {
|
||||
|
||||
assert_eq!(Staking::intentions(), vec![10, 20]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6980);
|
||||
assert_eq!(Staking::free_balance(&20), 6980);
|
||||
assert_eq!(Staking::intentions(), vec![10, 20]);
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6940);
|
||||
assert_eq!(Staking::free_balance(&20), 6940);
|
||||
assert_eq!(Staking::intentions(), vec![20]);
|
||||
assert!(Staking::forcing_new_era().is_some());
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6940);
|
||||
assert_eq!(Staking::free_balance(&20), 6860);
|
||||
assert_eq!(Staking::intentions(), vec![20]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6940);
|
||||
assert_eq!(Staking::free_balance(&20), 6700);
|
||||
@@ -213,6 +225,7 @@ fn slashing_should_work() {
|
||||
assert_eq!(Staking::voting_balance(&10), 21);
|
||||
|
||||
System::set_block_number(7);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::voting_balance(&10), 1);
|
||||
});
|
||||
@@ -433,6 +446,7 @@ fn nominating_slashes_should_work() {
|
||||
assert_eq!(Staking::voting_balance(&4), 40);
|
||||
|
||||
System::set_block_number(5);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::voting_balance(&1), 0);
|
||||
assert_eq!(Staking::voting_balance(&2), 20);
|
||||
|
||||
@@ -9,6 +9,7 @@ serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { version = "1.0", default_features = false}
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-codec-derive = { path = "../../codec/derive", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
@@ -22,6 +23,7 @@ std = [
|
||||
"serde_derive",
|
||||
"safe-mix/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-codec-derive/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[cfg_attr(any(feature = "std", test), macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
@@ -32,8 +35,11 @@ extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
#[macro_use]
|
||||
extern crate substrate_codec_derive;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate safe_mix;
|
||||
|
||||
@@ -74,26 +80,50 @@ pub trait Trait: Eq + Clone {
|
||||
Hash = Self::Hash,
|
||||
Digest = Self::Digest
|
||||
>;
|
||||
type Event: Parameter + Member;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
}
|
||||
|
||||
/// A phase of a block's execution.
|
||||
#[derive(Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))]
|
||||
pub enum Phase {
|
||||
/// Applying an extrinsic.
|
||||
ApplyExtrinsic(u32),
|
||||
/// The end.
|
||||
Finalization,
|
||||
}
|
||||
|
||||
/// Record of an event happening.
|
||||
#[derive(Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))]
|
||||
pub struct EventRecord<E: Parameter + Member> {
|
||||
/// The phase of the block it happened in.
|
||||
pub phase: Phase,
|
||||
/// The event itself.
|
||||
pub event: E,
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait>;
|
||||
|
||||
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;
|
||||
pub ExtrinsicData get(extrinsic_data): b"sys:xtd" => required map [ u32 => Vec<u8> ];
|
||||
ExtrinsicCount: b"sys:extrinsic_count" => u32;
|
||||
pub ExtrinsicIndex get(extrinsic_index): b"sys:xti" => u32;
|
||||
ExtrinsicData get(extrinsic_data): b"sys:xtd" => required map [ u32 => Vec<u8> ];
|
||||
RandomSeed get(random_seed): b"sys:rnd" => required T::Hash;
|
||||
// The current block number being processed. Set by `execute_block`.
|
||||
Number get(block_number): b"sys:num" => required T::BlockNumber;
|
||||
ParentHash get(parent_hash): b"sys:pha" => required T::Hash;
|
||||
ExtrinsicsRoot get(extrinsics_root): b"sys:txr" => required T::Hash;
|
||||
Digest get(digest): b"sys:dig" => default T::Digest;
|
||||
|
||||
Events get(events): b"sys:events" => default Vec<EventRecord<T::Event>>;
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
@@ -105,19 +135,23 @@ impl<T: Trait> Module<T> {
|
||||
<BlockHash<T>>::insert(*number - One::one(), parent_hash);
|
||||
<ExtrinsicsRoot<T>>::put(txs_root);
|
||||
<RandomSeed<T>>::put(Self::calculate_random());
|
||||
<ExtrinsicIndex<T>>::put(0);
|
||||
<ExtrinsicIndex<T>>::put(0u32);
|
||||
<Events<T>>::kill();
|
||||
}
|
||||
|
||||
/// Remove temporary "environment" entries in storage.
|
||||
pub fn finalise() -> T::Header {
|
||||
<RandomSeed<T>>::kill();
|
||||
<ExtrinsicIndex<T>>::kill();
|
||||
<ExtrinsicCount<T>>::kill();
|
||||
|
||||
let number = <Number<T>>::take();
|
||||
let parent_hash = <ParentHash<T>>::take();
|
||||
let digest = <Digest<T>>::take();
|
||||
let extrinsics_root = <ExtrinsicsRoot<T>>::take();
|
||||
let storage_root = T::Hashing::storage_root();
|
||||
|
||||
// <Events<T>> stays to be inspected by the client.
|
||||
|
||||
<T::Header as traits::Header>::new(number, extrinsics_root, storage_root, parent_hash, digest)
|
||||
}
|
||||
|
||||
@@ -128,6 +162,14 @@ impl<T: Trait> Module<T> {
|
||||
<Digest<T>>::put(l);
|
||||
}
|
||||
|
||||
/// Deposits an event onto this block's event record.
|
||||
pub fn deposit_event(event: T::Event) {
|
||||
let phase = <ExtrinsicIndex<T>>::get().map_or(Phase::Finalization, |c| Phase::ApplyExtrinsic(c));
|
||||
let mut events = Self::events();
|
||||
events.push(EventRecord { phase, event });
|
||||
<Events<T>>::put(events);
|
||||
}
|
||||
|
||||
/// Calculate the current block's random seed.
|
||||
fn calculate_random() -> T::Hash {
|
||||
assert!(Self::block_number() > Zero::zero(), "Block number may never be zero");
|
||||
@@ -180,12 +222,24 @@ impl<T: Trait> Module<T> {
|
||||
/// Note what the extrinsic data of the current extrinsic index is. If this is called, then
|
||||
/// ensure `derive_extrinsics` is also called before block-building is completed.
|
||||
pub fn note_extrinsic(encoded_xt: Vec<u8>) {
|
||||
<ExtrinsicData<T>>::insert(Self::extrinsic_index(), encoded_xt);
|
||||
<ExtrinsicData<T>>::insert(<ExtrinsicIndex<T>>::get().unwrap_or_default(), encoded_xt);
|
||||
}
|
||||
|
||||
/// To be called immediately after an extrinsic has been applied.
|
||||
pub fn note_applied_extrinsic() {
|
||||
<ExtrinsicIndex<T>>::put(<ExtrinsicIndex<T>>::get().unwrap_or_default() + 1u32);
|
||||
}
|
||||
|
||||
/// To be called immediately after `note_applied_extrinsic` of the last extrinsic of the block
|
||||
/// has been called.
|
||||
pub fn note_finished_extrinsics() {
|
||||
<ExtrinsicCount<T>>::put(<ExtrinsicIndex<T>>::get().unwrap_or_default());
|
||||
<ExtrinsicIndex<T>>::kill();
|
||||
}
|
||||
|
||||
/// Remove all extrinsics data and save the extrinsics trie root.
|
||||
pub fn derive_extrinsics() {
|
||||
let extrinsics = (0..Self::extrinsic_index()).map(<ExtrinsicData<T>>::take).collect();
|
||||
let extrinsics = (0..<ExtrinsicCount<T>>::get().unwrap_or_default()).map(<ExtrinsicData<T>>::take).collect();
|
||||
let xts_root = extrinsics_data_root::<T::Hashing>(extrinsics);
|
||||
<ExtrinsicsRoot<T>>::put(xts_root);
|
||||
}
|
||||
@@ -219,3 +273,57 @@ impl<T: Trait> primitives::BuildStorage for GenesisConfig<T>
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::H256;
|
||||
use primitives::BuildStorage;
|
||||
use primitives::traits::BlakeTwo256;
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl Trait for Test {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = u16;
|
||||
}
|
||||
|
||||
type System = Module<Test>;
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
GenesisConfig::<Test>::default().build_storage().unwrap().into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deposit_event_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::initialise(&1, &[0u8; 32].into(), &[0u8; 32].into());
|
||||
System::note_finished_extrinsics();
|
||||
System::deposit_event(1u16);
|
||||
System::finalise();
|
||||
assert_eq!(System::events(), vec![EventRecord { phase: Phase::Finalization, event: 1u16 }]);
|
||||
|
||||
System::initialise(&2, &[0u8; 32].into(), &[0u8; 32].into());
|
||||
System::deposit_event(42u16);
|
||||
System::note_applied_extrinsic();
|
||||
System::deposit_event(69u16);
|
||||
System::note_applied_extrinsic();
|
||||
System::note_finished_extrinsics();
|
||||
System::deposit_event(3u16);
|
||||
System::finalise();
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord { phase: Phase::ApplyExtrinsic(0), event: 42u16 },
|
||||
EventRecord { phase: Phase::ApplyExtrinsic(1), event: 69u16 },
|
||||
EventRecord { phase: Phase::Finalization, event: 3u16 }
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ impl<T: Trait> Module<T> {
|
||||
assert!(aux.is_empty());
|
||||
assert!(!<Self as Store>::DidUpdate::exists(), "Timestamp must be updated only once in the block");
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == T::TIMESTAMP_SET_POSITION,
|
||||
<system::Module<T>>::extrinsic_index() == Some(T::TIMESTAMP_SET_POSITION),
|
||||
"Timestamp extrinsic must be at position {} in the block",
|
||||
T::TIMESTAMP_SET_POSITION
|
||||
);
|
||||
@@ -158,6 +158,7 @@ mod tests {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = u64;
|
||||
|
||||
Reference in New Issue
Block a user