diff --git a/polkadot/consensus/Cargo.toml b/polkadot/consensus/Cargo.toml index 60c8009b7c..b7ccbc6534 100644 --- a/polkadot/consensus/Cargo.toml +++ b/polkadot/consensus/Cargo.toml @@ -9,6 +9,7 @@ parking_lot = "0.4" tokio-timer = "0.1.2" ed25519 = { path = "../../substrate/ed25519" } error-chain = "0.11" +log = "0.4" polkadot-api = { path = "../api" } polkadot-collator = { path = "../collator" } polkadot-primitives = { path = "../primitives" } diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 7321948dd7..a5c34e30ee 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -45,6 +45,9 @@ extern crate substrate_primitives as primitives; #[macro_use] extern crate error_chain; +#[macro_use] +extern crate log; + use std::collections::{HashMap, HashSet}; use std::sync::Arc; @@ -55,9 +58,9 @@ use polkadot_api::{PolkadotApi, BlockBuilder}; use polkadot_primitives::{Hash, Timestamp}; use polkadot_primitives::block::Block as PolkadotBlock; use polkadot_primitives::parachain::{Id as ParaId, DutyRoster, BlockData, Extrinsic, CandidateReceipt}; -use primitives::block::{Block as SubstrateBlock, Header as SubstrateHeader, HeaderHash, Id as BlockId}; +use primitives::block::{Block as SubstrateBlock, Header as SubstrateHeader, HeaderHash, Id as BlockId, Number as BlockNumber}; use primitives::AuthorityId; -use transaction_pool::TransactionPool; +use transaction_pool::{Ready, TransactionPool}; use futures::prelude::*; use futures::future; @@ -477,17 +480,19 @@ impl bft::ProposerFactory for ProposerFactory let duty_roster = self.client.duty_roster(&checked_id)?; let group_info = make_group_info(duty_roster, authorities)?; - let table = Arc::new(SharedTable::new(group_info, sign_with, parent_hash)); + let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash)); let router = self.network.table_router(table.clone()); // TODO [PoC-2]: kick off collation process. Ok(Proposer { parent_hash, + parent_number: parent_header.number, parent_id: checked_id, - _table: table, - _router: router, + local_key: sign_with, client: self.client.clone(), transaction_pool: self.transaction_pool.clone(), + _table: table, + _router: router, }) } } @@ -503,8 +508,10 @@ fn current_timestamp() -> Timestamp { /// The Polkadot proposer logic. pub struct Proposer { parent_hash: HeaderHash, + parent_number: BlockNumber, parent_id: C::CheckedBlockId, client: Arc, + local_key: Arc, transaction_pool: Arc>, _table: Arc, _router: R, @@ -516,8 +523,6 @@ impl bft::Proposer for Proposer { type Evaluate = Result; fn propose(&self) -> Result { - use transaction_pool::Ready; - // TODO: handle case when current timestamp behind that in state. let mut block_builder = self.client.build_block( &self.parent_id, @@ -565,6 +570,65 @@ impl bft::Proposer for Proposer { fn evaluate(&self, proposal: &SubstrateBlock) -> Result { evaluate_proposal(proposal, &*self.client, current_timestamp(), &self.parent_hash, &self.parent_id) } + + fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior)>) { + use bft::generic::Misbehavior as GenericMisbehavior; + use primitives::bft::{MisbehaviorKind, MisbehaviorReport}; + use polkadot_primitives::transaction::{Function, Transaction, UncheckedTransaction}; + + let local_id = self.local_key.public().0; + let mut pool = self.transaction_pool.lock(); + let mut next_nonce = { + let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client); + + let cur_nonce = pool.pending(readiness_evaluator) + .filter(|tx| tx.as_transaction().transaction.signed == local_id) + .last() + .map(|tx| Ok(tx.as_transaction().transaction.nonce)) + .unwrap_or_else(|| self.client.nonce(&self.parent_id, local_id)); + + match cur_nonce { + Ok(cur_nonce) => cur_nonce + 1, + Err(e) => { + warn!(target: "consensus", "Error computing next transaction nonce: {}", e); + return; + } + } + }; + + for (target, misbehavior) in misbehavior { + let report = MisbehaviorReport { + parent_hash: self.parent_hash, + parent_number: self.parent_number, + target, + misbehavior: match misbehavior { + GenericMisbehavior::ProposeOutOfTurn(_, _, _) => continue, + GenericMisbehavior::DoublePropose(_, _, _) => continue, + GenericMisbehavior::DoublePrepare(round, (h1, s1), (h2, s2)) + => MisbehaviorKind::BftDoublePrepare(round as u32, (h1, s1.signature), (h2, s2.signature)), + GenericMisbehavior::DoubleCommit(round, (h1, s1), (h2, s2)) + => MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)), + } + }; + + let tx = Transaction { + signed: local_id, + nonce: next_nonce, + function: Function::ReportMisbehavior(report), + }; + + next_nonce += 1; + + let message = tx.encode(); + let signature = self.local_key.sign(&message); + let tx = UncheckedTransaction { + transaction: tx, + signature, + }; + + pool.import(tx).expect("locally signed transaction is valid; qed"); + } + } } fn evaluate_proposal( diff --git a/polkadot/executor/src/lib.rs b/polkadot/executor/src/lib.rs index a1f472a859..186e5469cc 100644 --- a/polkadot/executor/src/lib.rs +++ b/polkadot/executor/src/lib.rs @@ -162,7 +162,7 @@ mod tests { construct_block( 2, block1().1, - hex!("c8776c92e8012bf6b3f206448eda3f00bca26d77f220f4714c81cbc92a30e1e2").into(), + hex!("5604fe023cd6effd93aec9b4a008398abdd32afb3fec988a19aa853ab0424a7c").into(), 200_000, vec![ Transaction { diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index d65291476a..fb02e3d121 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -80,3 +80,9 @@ pub type Signature = primitives::hash::H512; /// A timestamp: seconds since the unix epoch. pub type Timestamp = u64; + +/// The balance of an account. +pub type Balance = u64; + +/// The amount of bonding period left in an account. Measured in eras. +pub type Bondage = u64; diff --git a/polkadot/primitives/src/transaction.rs b/polkadot/primitives/src/transaction.rs index 3d4095b86c..f13cf223a9 100644 --- a/polkadot/primitives/src/transaction.rs +++ b/polkadot/primitives/src/transaction.rs @@ -18,6 +18,7 @@ use rstd::vec::Vec; use codec::{Input, Slicable}; +use primitives::bft::MisbehaviorReport; use ::Signature; #[cfg(feature = "std")] @@ -168,6 +169,8 @@ enum FunctionId { StakingUnstake = 0x21, /// Staking subsystem: transfer stake. StakingTransfer = 0x22, + /// Report misbehavior. + StakingReportMisbehavior = 0x23, /// Make a proposal for the governance system. GovernancePropose = 0x30, /// Approve a proposal for the governance system. @@ -178,9 +181,16 @@ impl FunctionId { /// Derive `Some` value from a `u8`, or `None` if it's invalid. fn from_u8(value: u8) -> Option { use self::*; - let functions = [FunctionId::StakingStake, FunctionId::StakingUnstake, - FunctionId::StakingTransfer, FunctionId::SessionSetKey, FunctionId::TimestampSet, - FunctionId::GovernancePropose, FunctionId::GovernanceApprove]; + let functions = [ + FunctionId::StakingStake, + FunctionId::StakingUnstake, + FunctionId::StakingTransfer, + FunctionId::StakingReportMisbehavior, + FunctionId::SessionSetKey, + FunctionId::TimestampSet, + FunctionId::GovernancePropose, + FunctionId::GovernanceApprove, + ]; functions.iter().map(|&f| f).find(|&f| value == f as u8) } } @@ -222,6 +232,8 @@ pub enum Function { StakingUnstake, /// Staking subsystem: transfer stake. StakingTransfer(::AccountId, u64), + /// Staking subsystem: report misbehavior of a validator. + ReportMisbehavior(MisbehaviorReport), /// Make a proposal for the governance system. GovernancePropose(Proposal), /// Approve a proposal for the governance system. @@ -269,6 +281,7 @@ impl Slicable for Function { Function::StakingTransfer(to, amount) } + FunctionId::StakingReportMisbehavior => Function::ReportMisbehavior(MisbehaviorReport::decode(input)?), FunctionId::GovernancePropose => Function::GovernancePropose(try_opt!(Slicable::decode(input))), FunctionId::GovernanceApprove => @@ -293,6 +306,10 @@ impl Slicable for Function { Function::StakingUnstake => { (FunctionId::StakingUnstake as u8).using_encoded(|s| v.extend(s)); } + Function::ReportMisbehavior(ref report) => { + (FunctionId::StakingReportMisbehavior as u8).using_encoded(|s| v.extend(s)); + report.using_encoded(|s| v.extend(s)); + } Function::StakingTransfer(ref to, ref amount) => { (FunctionId::StakingTransfer as u8).using_encoded(|s| v.extend(s)); to.using_encoded(|s| v.extend(s)); diff --git a/polkadot/runtime/Cargo.toml b/polkadot/runtime/Cargo.toml index 4efe5c15e7..ce4565025a 100644 --- a/polkadot/runtime/Cargo.toml +++ b/polkadot/runtime/Cargo.toml @@ -12,6 +12,7 @@ substrate-runtime-std = { path = "../../substrate/runtime-std" } substrate-runtime-io = { path = "../../substrate/runtime-io" } substrate-runtime-support = { path = "../../substrate/runtime-support" } substrate-primitives = { path = "../../substrate/primitives" } +substrate-misbehavior-check = { path = "../../substrate/misbehavior-check" } polkadot-primitives = { path = "../primitives" } [dev-dependencies] @@ -25,6 +26,7 @@ std = [ "substrate-runtime-io/std", "substrate-runtime-support/std", "substrate-primitives/std", + "substrate-misbehavior-check/std", "polkadot-primitives/std", "log" ] diff --git a/polkadot/runtime/src/environment.rs b/polkadot/runtime/src/environment.rs index 5b311ce1f9..b7fef7cdd8 100644 --- a/polkadot/runtime/src/environment.rs +++ b/polkadot/runtime/src/environment.rs @@ -31,8 +31,6 @@ pub struct Environment { pub parent_hash: Hash, /// The current block digest. pub digest: Digest, - /// The current transaction index - pub transaction_index: u64, } /// Do something with the environment and return its value. Keep the function short. diff --git a/polkadot/runtime/src/genesismap.rs b/polkadot/runtime/src/genesismap.rs index 4100f411b0..d2b370fe46 100644 --- a/polkadot/runtime/src/genesismap.rs +++ b/polkadot/runtime/src/genesismap.rs @@ -21,8 +21,7 @@ use std::collections::HashMap; use runtime_io::twox_128; use runtime_support::Hashable; use primitives::Block; -use polkadot_primitives::{BlockNumber, AccountId}; -use runtime::staking::Balance; +use polkadot_primitives::{Balance, BlockNumber, AccountId}; /// Configuration of a general Polkadot genesis block. pub struct GenesisConfig { diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index 16379cac8e..caf8cb47c1 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -19,23 +19,33 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate substrate_runtime_std as rstd; -#[macro_use] extern crate substrate_runtime_io as runtime_io; extern crate substrate_runtime_support as runtime_support; -#[cfg(all(feature = "std", test))] extern crate substrate_keyring as keyring; - -#[cfg(feature = "std")] extern crate rustc_hex; - extern crate substrate_codec as codec; -#[cfg(feature = "std")] #[macro_use] extern crate substrate_primitives as primitives; +extern crate substrate_misbehavior_check as misbehavior_check; extern crate polkadot_primitives; -#[cfg(test)] #[macro_use] extern crate hex_literal; +#[cfg(all(feature = "std", test))] +extern crate substrate_keyring as keyring; + +#[cfg(feature = "std")] +extern crate rustc_hex; + +#[cfg_attr(any(test, feature = "std"), macro_use)] +extern crate substrate_primitives as primitives; + +#[macro_use] +extern crate substrate_runtime_io as runtime_io; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; pub mod api; pub mod environment; pub mod runtime; -#[cfg(feature = "std")] pub mod genesismap; +#[cfg(feature = "std")] +pub mod genesismap; /// Type definitions and helpers for transactions. pub mod transaction { diff --git a/polkadot/runtime/src/runtime/consensus.rs b/polkadot/runtime/src/runtime/consensus.rs index 7d50f6abd4..7841f1d47d 100644 --- a/polkadot/runtime/src/runtime/consensus.rs +++ b/polkadot/runtime/src/runtime/consensus.rs @@ -23,7 +23,7 @@ use polkadot_primitives::SessionKey; struct AuthorityStorageVec {} impl StorageVec for AuthorityStorageVec { type Item = SessionKey; - const PREFIX: &'static[u8] = b":auth:"; + const PREFIX: &'static [u8] = b":auth:"; } /// Get the current set of authorities. These are the session keys. @@ -37,7 +37,7 @@ pub mod internal { /// Set the current set of authorities' session keys. /// /// Called by `next_session` only. - pub fn set_authorities(authorities: &[SessionKey]) { + pub fn set_authorities<'a, I: IntoIterator>(authorities: I) { AuthorityStorageVec::set_items(authorities); } diff --git a/polkadot/runtime/src/runtime/session.rs b/polkadot/runtime/src/runtime/session.rs index e9bf5f3765..01a5f78bbc 100644 --- a/polkadot/runtime/src/runtime/session.rs +++ b/polkadot/runtime/src/runtime/session.rs @@ -25,14 +25,23 @@ use runtime::{system, staking, consensus}; const SESSION_LENGTH: &[u8] = b"ses:len"; const CURRENT_INDEX: &[u8] = b"ses:ind"; +const CURRENT_SESSION_START: &[u8] = b"ses:sta"; +const LAST_SESSION_START: &[u8] = b"ses:lst"; const LAST_LENGTH_CHANGE: &[u8] = b"ses:llc"; const NEXT_KEY_FOR: &[u8] = b"ses:nxt:"; const NEXT_SESSION_LENGTH: &[u8] = b"ses:nln"; -struct ValidatorStorageVec {} +struct ValidatorStorageVec; impl StorageVec for ValidatorStorageVec { type Item = AccountId; - const PREFIX: &'static[u8] = b"ses:val:"; + const PREFIX: &'static [u8] = b"ses:val:"; +} + +// the session keys before the previous. +struct LastValidators; +impl StorageVec for LastValidators { + type Item = (AccountId, SessionKey); + const PREFIX: &'static [u8] = b"ses:old:"; } /// Get the current set of validators. @@ -50,11 +59,31 @@ pub fn validator_count() -> u32 { ValidatorStorageVec::count() as u32 } -/// The current era index. +/// The current session index. pub fn current_index() -> BlockNumber { storage::get_or(CURRENT_INDEX, 0) } +/// Get the starting block of the current session. +pub fn current_start_block() -> BlockNumber { + // this seems like it's computable just by examining the current block number, session length, + // and last length change, but it's not simple to tell whether we are before or after + // a session rotation on a block which will have one. + storage::get_or(CURRENT_SESSION_START, 0) +} + +/// Get the last session's validators, paired with their authority keys. +pub fn last_session_keys() -> Vec<(AccountId, SessionKey)> { + LastValidators::items() +} + +/// Get the start block of the last session. +/// In general this is computable from the session length, +/// but when the current session is the first with a new length it is uncomputable. +pub fn last_session_start() -> Option { + storage::get(LAST_SESSION_START) +} + /// The block number at which the era length last changed. pub fn last_length_change() -> BlockNumber { storage::get_or(LAST_LENGTH_CHANGE, 0) @@ -90,11 +119,14 @@ pub mod privileged { pub mod internal { use super::*; - /// Set the current set of validators. + /// Transition to a new era, with a new set of valiators. /// /// Called by staking::next_era() only. `next_session` should be called after this in order to /// update the session keys to the next validator set. pub fn set_validators(new: &[AccountId]) { + LastValidators::set_items( + new.iter().cloned().zip(consensus::authorities()) + ); ValidatorStorageVec::set_items(new); consensus::internal::set_authorities(new); } @@ -114,7 +146,6 @@ pub mod internal { fn rotate_session() { // Increment current session index. storage::put(CURRENT_INDEX, &(current_index() + 1)); - // Enact era length change. if let Some(next_len) = storage::get::(NEXT_SESSION_LENGTH) { storage::put(SESSION_LENGTH, &next_len); @@ -122,10 +153,23 @@ fn rotate_session() { storage::kill(NEXT_SESSION_LENGTH); } + let validators = validators(); + + storage::put(LAST_SESSION_START, ¤t_start_block()); + storage::put(CURRENT_SESSION_START, &system::block_number()); + LastValidators::set_items( + validators.iter() + .cloned() + .zip(consensus::authorities()) + ); + + // Update any changes in session keys. - validators().iter().enumerate().for_each(|(i, v)| { + validators.iter().enumerate().for_each(|(i, v)| { let k = v.to_keyed_vec(NEXT_KEY_FOR); if let Some(n) = storage::take(&k) { + // this is fine because the authorities vector currently + // matches the validators length perfectly. consensus::internal::set_authority(i as u32, &n); } }); diff --git a/polkadot/runtime/src/runtime/staking.rs b/polkadot/runtime/src/runtime/staking.rs index 1b69698265..cca0a92718 100644 --- a/polkadot/runtime/src/runtime/staking.rs +++ b/polkadot/runtime/src/runtime/staking.rs @@ -22,18 +22,16 @@ use runtime_io::print; use codec::KeyedVec; use runtime_support::{storage, StorageVec}; use polkadot_primitives::{BlockNumber, AccountId}; -use runtime::{system, session, governance}; +use primitives::bft::{MisbehaviorReport, MisbehaviorKind}; +use runtime::{system, session, governance, consensus}; -/// The balance of an account. -pub type Balance = u64; - -/// The amount of bonding period left in an account. Measured in eras. -pub type Bondage = u64; +type Balance = u64; +type Bondage = u64; struct IntentionStorageVec {} impl StorageVec for IntentionStorageVec { type Item = AccountId; - const PREFIX: &'static[u8] = b"sta:wil:"; + const PREFIX: &'static [u8] = b"sta:wil:"; } const BONDING_DURATION: &[u8] = b"sta:loc"; @@ -81,11 +79,16 @@ pub fn balance(who: &AccountId) -> Balance { storage::get_or_default(&who.to_keyed_vec(BALANCE_OF)) } -/// The liquidity-state of a given account. +/// Gives the index of the era where the account's balance will no longer +/// be bonded. pub fn bondage(who: &AccountId) -> Bondage { storage::get_or_default(&who.to_keyed_vec(BONDAGE_OF)) } +fn set_balance(who: &AccountId, amount: Balance) { + storage::put(&who.to_keyed_vec(BALANCE_OF), &amount) +} + // Each identity's stake may be in one of three bondage states, given by an integer: // - n | n <= current_era(): inactive: free to be transferred. // - ~0: active: currently representing a validator. @@ -114,7 +117,7 @@ pub mod public { pub fn stake(transactor: &AccountId) { let mut intentions = IntentionStorageVec::items(); // can't be in the list twice. - assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked."); + assert!(intentions.iter().find(|t| t == &transactor).is_none(), "Cannot stake if already staked."); intentions.push(transactor.clone()); IntentionStorageVec::set_items(&intentions); storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &u64::max_value()); @@ -133,6 +136,46 @@ pub mod public { IntentionStorageVec::set_items(&intentions); storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &(current_era() + bonding_duration())); } + + /// Report misbehavior. Only validators may do this, signing under + /// the authority key of the session the report corresponds to. + /// + /// Reports older than one session in the past will be ignored. + pub fn report_misbehavior(transactor: &AccountId, report: &MisbehaviorReport) { + let (validators, authorities) = if report.parent_number < session::last_session_start().unwrap_or(0) { + panic!("report is too old"); + } else if report.parent_number < session::current_start_block() { + session::last_session_keys().into_iter().unzip() + } else { + (session::validators(), consensus::authorities()) + }; + + if report.parent_hash != system::block_hash(report.parent_number) { + // report out of chain. + panic!("report not from this blockchain"); + } + + let reporting_validator = match authorities.iter().position(|x| x == transactor) { + None => panic!("only validators may report"), + Some(pos) => validators.get(pos).expect("validators and authorities have same cardinality; qed"), + }; + + // any invalidity beyond this point is actually its own misbehavior. + let target = match authorities.iter().position(|x| x == &report.target) { + None => { + slash(reporting_validator, None); + return; + } + Some(pos) => validators.get(pos).expect("validators and authorities have same cardinality; qed"), + }; + + let misbehaved = ::misbehavior_check::evaluate_misbehavior(&report.target, report.parent_hash, &report.misbehavior); + if misbehaved { + slash(target, Some(reporting_validator)) + } else { + slash(reporting_validator, None); + } + } } pub mod privileged { @@ -172,6 +215,23 @@ pub mod internal { } } +/// Slash a validator, with an optional benefactor. +fn slash(who: &AccountId, benefactor: Option<&AccountId>) { + // the reciprocal of the proportion of the amount slashed to give + // to the benefactor. + const SLASH_REWARD_DENOMINATOR: Balance = 10; + + let slashed = balance(who); + set_balance(who, 0); + + if let Some(benefactor) = benefactor { + let reward = slashed / SLASH_REWARD_DENOMINATOR; + + let prior = balance(benefactor); + set_balance(benefactor, prior + reward); + } +} + /// The era has changed - enact new staking set. /// /// NOTE: This always happens immediately before a session change to ensure that new validators @@ -406,4 +466,31 @@ mod tests { transfer(&one, &two, 69); }); } + + #[test] + #[should_panic] + fn misbehavior_report_by_non_validator_panics() { + let one = Keyring::One.to_raw_public(); + let two = Keyring::Two.to_raw_public(); + + let mut t: TestExternalities = map![ + twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64) + ]; + + with_externalities(&mut t, || { + // the misbehavior report here is invalid, but that + // actually doesn't panic; instead it would slash the bad + // reporter. + report_misbehavior(&one, &MisbehaviorReport { + parent_hash: [0; 32].into(), + parent_number: 0, + target: two, + misbehavior: MisbehaviorKind::BftDoubleCommit( + 2, + ([1; 32].into(), [2; 64].into()), + ([3; 32].into(), [4; 64].into()), + ), + }) + }); + } } diff --git a/polkadot/runtime/src/runtime/system.rs b/polkadot/runtime/src/runtime/system.rs index aa6cfc45ce..c7972c949a 100644 --- a/polkadot/runtime/src/runtime/system.rs +++ b/polkadot/runtime/src/runtime/system.rs @@ -167,6 +167,9 @@ fn dispatch_function(function: &Function, transactor: &AccountId) { Function::StakingTransfer(dest, value) => { ::runtime::staking::public::transfer(transactor, &dest, value); } + Function::ReportMisbehavior(ref report) => { + ::runtime::staking::public::report_misbehavior(transactor, report) + } Function::SessionSetKey(session) => { ::runtime::session::public::set_key(transactor, &session); } diff --git a/polkadot/runtime/wasm/Cargo.lock b/polkadot/runtime/wasm/Cargo.lock index 11fe2d48f8..d2bf5b3e78 100644 --- a/polkadot/runtime/wasm/Cargo.lock +++ b/polkadot/runtime/wasm/Cargo.lock @@ -392,6 +392,7 @@ version = "0.1.0" dependencies = [ "polkadot-primitives 0.1.0", "substrate-codec 0.1.0", + "substrate-misbehavior-check 0.1.0", "substrate-primitives 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-std 0.1.0", @@ -599,6 +600,15 @@ dependencies = [ "substrate-runtime-std 0.1.0", ] +[[package]] +name = "substrate-misbehavior-check" +version = "0.1.0" +dependencies = [ + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", +] + [[package]] name = "substrate-primitives" version = "0.1.0" diff --git a/polkadot/runtime/wasm/Cargo.toml b/polkadot/runtime/wasm/Cargo.toml index 2452ce9bd2..0e30ce9ee6 100644 --- a/polkadot/runtime/wasm/Cargo.toml +++ b/polkadot/runtime/wasm/Cargo.toml @@ -12,6 +12,7 @@ substrate-runtime-std = { path = "../../../substrate/runtime-std", default-featu substrate-runtime-io = { path = "../../../substrate/runtime-io", default-features = false } substrate-runtime-support = { path = "../../../substrate/runtime-support", default-features = false } substrate-primitives = { path = "../../../substrate/primitives", default-features = false } +substrate-misbehavior-check = { path = "../../../substrate/misbehavior-check", default-features = false } polkadot-primitives = { path = "../../primitives", default-features = false } [features] @@ -22,6 +23,7 @@ std = [ "substrate-runtime-std/std", "substrate-runtime-support/std", "substrate-primitives/std", + "substrate-misbehavior-check/std", "polkadot-primitives/std", ]