Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
142f6ac
notify when an authority appears to have missed their block
rphmeier Aug 9, 2018
3f09f5c
Runtime API
gavofyork Aug 9, 2018
6095a6d
offline tracker
rphmeier Aug 9, 2018
21f40e5
Merge branch 'rh-note-offline-validator' of github.com:paritytech/pol…
rphmeier Aug 9, 2018
13a5e89
Move to consensus
gavofyork Aug 9, 2018
8c0fd55
Merge branch 'rh-note-offline-validator' of github.com:paritytech/pol…
gavofyork Aug 9, 2018
de23e91
generating reports of offline indices
rphmeier Aug 9, 2018
5433ef3
stubbed-out evaluation logic
rphmeier Aug 9, 2018
603e5a2
Slashing data pathwat
gavofyork Aug 9, 2018
08f598e
Merge branch 'rh-note-offline-validator' of github.com:paritytech/pol…
rphmeier Aug 9, 2018
4e2ceef
usize -> u32
rphmeier Aug 9, 2018
cff0577
Slash bad validators.
gavofyork Aug 9, 2018
825e1a4
Merge branch 'rh-note-offline-validator' of github.com:paritytech/pol…
gavofyork Aug 9, 2018
14b27cc
update to rhododendron 0.3
rphmeier Aug 9, 2018
00f23eb
Merge branch 'rh-note-offline-validator' of github.com:paritytech/pol…
rphmeier Aug 9, 2018
b827eec
fix compilation of polkadot-consensus
rphmeier Aug 9, 2018
6dd3524
Support offline noting in checked_block
gavofyork Aug 9, 2018
1cb58de
Merge branch 'rh-note-offline-validator' of github.com:paritytech/pol…
gavofyork Aug 9, 2018
241f28f
include offline reports in block authorship voting
rphmeier Aug 10, 2018
614b011
do not vote validators offline after some time
rphmeier Aug 10, 2018
b122ed4
add test for offline-tracker
rphmeier Aug 10, 2018
9724a69
fix test build
rphmeier Aug 10, 2018
60aadd3
bump spec version
rphmeier Aug 10, 2018
2fb0343
Merge remote-tracking branch 'upstream/master' into rh-note-offline-v…
rphmeier Aug 10, 2018
7602e59
update wasm
rphmeier Aug 10, 2018
922035d
Only allow validators that are possible to slash
gavofyork Aug 10, 2018
a6bea42
Fix grumble
tomusdrw Aug 3, 2018
a61396e
More idiomatic
gavofyork Aug 10, 2018
1d787e1
New Wasm.
gavofyork Aug 10, 2018
ab6957f
update rhododendron
rphmeier Aug 10, 2018
7696119
improve logging and reduce round time exponent
rphmeier Aug 10, 2018
2446ded
format offline validators in ss58
rphmeier Aug 10, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions polkadot/api/src/full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ use state_machine;

use runtime::Address;
use runtime_primitives::traits::AuxLookup;
use primitives::{AccountId, Block, Header, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic};
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
use primitives::{
AccountId, Block, Header, BlockId, Hash, Index, InherentData,
SessionKey, Timestamp, UncheckedExtrinsic,
};
use primitives::parachain::{DutyRoster, Id as ParaId};

use {BlockBuilder, PolkadotApi, LocalPolkadotApi, ErrorKind, Error, Result};

Expand Down Expand Up @@ -132,20 +135,20 @@ impl<B: LocalBackend<Block>> PolkadotApi for Client<B, LocalCallExecutor<B, Nati
with_runtime!(self, at, || ::runtime::Parachains::parachain_head(parachain))
}

fn build_block(&self, at: &BlockId, timestamp: Timestamp, new_heads: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder> {
fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result<Self::BlockBuilder> {
let mut block_builder = self.new_block_at(at)?;
for inherent in self.inherent_extrinsics(at, timestamp, new_heads)? {
for inherent in self.inherent_extrinsics(at, inherent_data)? {
block_builder.push(inherent)?;
}

Ok(block_builder)
}

fn inherent_extrinsics(&self, at: &BlockId, timestamp: Timestamp, new_heads: Vec<CandidateReceipt>) -> Result<Vec<UncheckedExtrinsic>> {
fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result<Vec<UncheckedExtrinsic>> {
use codec::{Encode, Decode};

with_runtime!(self, at, || {
let extrinsics = ::runtime::inherent_extrinsics(timestamp, new_heads);
let extrinsics = ::runtime::inherent_extrinsics(inherent_data);
extrinsics.into_iter()
.map(|x| x.encode()) // get encoded representation
.map(|x| Decode::decode(&mut &x[..])) // get byte-vec equivalent to extrinsic
Expand Down
12 changes: 7 additions & 5 deletions polkadot/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ extern crate substrate_keyring as keyring;
pub mod full;
pub mod light;

use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp,
UncheckedExtrinsic};
use primitives::{
AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp,
UncheckedExtrinsic, InherentData,
};
use runtime::Address;
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
use primitives::parachain::{DutyRoster, Id as ParaId};

error_chain! {
errors {
Expand Down Expand Up @@ -128,11 +130,11 @@ pub trait PolkadotApi {
fn evaluate_block(&self, at: &BlockId, block: Block) -> Result<bool>;

/// Build a block on top of the given, with inherent extrinsics pre-pushed.
fn build_block(&self, at: &BlockId, timestamp: Timestamp, new_heads: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder>;
fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result<Self::BlockBuilder>;

/// Attempt to produce the (encoded) inherent extrinsics for a block being built upon the given.
/// This may vary by runtime and will fail if a runtime doesn't follow the same API.
fn inherent_extrinsics(&self, at: &BlockId, timestamp: Timestamp, new_heads: Vec<CandidateReceipt>) -> Result<Vec<UncheckedExtrinsic>>;
fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result<Vec<UncheckedExtrinsic>>;
}

/// Mark for all Polkadot API implementations, that are making use of state data, stored locally.
Expand Down
11 changes: 7 additions & 4 deletions polkadot/api/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ use std::sync::Arc;
use client::backend::{Backend, RemoteBackend};
use client::{Client, CallExecutor};
use codec::Decode;
use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic};
use primitives::{
AccountId, Block, BlockId, Hash, Index, InherentData,
SessionKey, Timestamp, UncheckedExtrinsic,
};
use runtime::Address;
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
use primitives::parachain::{DutyRoster, Id as ParaId};
use {PolkadotApi, BlockBuilder, RemotePolkadotApi, Result, ErrorKind};

/// Light block builder. TODO: make this work (efficiently)
Expand Down Expand Up @@ -92,11 +95,11 @@ impl<B: Backend<Block>, E: CallExecutor<Block>> PolkadotApi for RemotePolkadotAp
Err(ErrorKind::UnknownRuntime.into())
}

fn build_block(&self, _at: &BlockId, _timestamp: Timestamp, _new_heads: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder> {
fn build_block(&self, _at: &BlockId, _inherent: InherentData) -> Result<Self::BlockBuilder> {
Err(ErrorKind::UnknownRuntime.into())
}

fn inherent_extrinsics(&self, _at: &BlockId, _timestamp: Timestamp, _new_heads: Vec<CandidateReceipt>) -> Result<Vec<Vec<u8>>> {
fn inherent_extrinsics(&self, _at: &BlockId, _inherent: InherentData) -> Result<Vec<Vec<u8>>> {
Err(ErrorKind::UnknownRuntime.into())
}
}
Expand Down
2 changes: 1 addition & 1 deletion polkadot/consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ed25519 = { path = "../../substrate/ed25519" }
error-chain = "0.12"
log = "0.3"
exit-future = "0.1"
rhododendron = "0.2"
rhododendron = "0.3"
polkadot-api = { path = "../api" }
polkadot-availability-store = { path = "../availability-store" }
polkadot-parachain = { path = "../parachain" }
Expand Down
3 changes: 3 additions & 0 deletions polkadot/consensus/src/dynamic_inclusion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ impl DynamicInclusion {
Some(now + until)
}
}

/// Get the start instant.
pub fn started_at(&self) -> Instant { self.start }
}

#[cfg(test)]
Expand Down
107 changes: 96 additions & 11 deletions polkadot/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ use std::time::{Duration, Instant};
use codec::{Decode, Encode};
use extrinsic_store::Store as ExtrinsicStore;
use polkadot_api::PolkadotApi;
use polkadot_primitives::{Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey};
use polkadot_primitives::{AccountId, Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey};
use polkadot_primitives::parachain::{Id as ParaId, Chain, DutyRoster, BlockData, Extrinsic as ParachainExtrinsic, CandidateReceipt, CandidateSignature};
use primitives::AuthorityId;
use transaction_pool::TransactionPool;
Expand All @@ -80,20 +80,26 @@ use futures::prelude::*;
use futures::future;
use collation::CollationFetch;
use dynamic_inclusion::DynamicInclusion;
use parking_lot::RwLock;

pub use self::collation::{validate_collation, Collators};
pub use self::error::{ErrorKind, Error};
pub use self::offline_tracker::OfflineTracker;
pub use self::shared_table::{SharedTable, StatementProducer, ProducedStatements, Statement, SignedStatement, GenericStatement};
pub use service::Service;

mod dynamic_inclusion;
mod evaluation;
mod error;
mod offline_tracker;
mod service;
mod shared_table;

pub mod collation;

/// Shared offline validator tracker.
pub type SharedOfflineTracker = Arc<RwLock<OfflineTracker>>;

// block size limit.
const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024;

Expand Down Expand Up @@ -240,6 +246,8 @@ pub struct ProposerFactory<C, N, P> {
pub parachain_empty_duration: Duration,
/// Store for extrinsic data.
pub extrinsic_store: ExtrinsicStore,
/// Offline-tracker.
pub offline: SharedOfflineTracker,
}

impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
Expand All @@ -255,10 +263,11 @@ impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
type Output = N::Output;
type Error = Error;

fn init(&self,
fn init(
&self,
parent_header: &Header,
authorities: &[AuthorityId],
sign_with: Arc<ed25519::Pair>
sign_with: Arc<ed25519::Pair>,
) -> Result<(Self::Proposer, Self::Input, Self::Output), Error> {
use runtime_primitives::traits::{Hash as HashT, BlakeTwo256};

Expand All @@ -269,6 +278,9 @@ impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
let random_seed = self.client.random_seed(&id)?;
let random_seed = BlakeTwo256::hash(&*random_seed);

let validators = self.client.validators(&id)?;
self.offline.write().note_new_block(&validators[..]);

let (group_info, local_duty) = make_group_info(
duty_roster,
authorities,
Expand Down Expand Up @@ -326,6 +338,8 @@ impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
random_seed,
table,
transaction_pool: self.transaction_pool.clone(),
offline: self.offline.clone(),
validators,
_drop_signal: drop_signal,
};

Expand Down Expand Up @@ -403,9 +417,22 @@ pub struct Proposer<C: PolkadotApi> {
random_seed: Hash,
table: Arc<SharedTable>,
transaction_pool: Arc<TransactionPool<C>>,
offline: SharedOfflineTracker,
validators: Vec<AccountId>,
_drop_signal: exit_future::Signal,
}

impl<C: PolkadotApi + Send + Sync> Proposer<C> {
fn primary_index(&self, round_number: usize, len: usize) -> usize {
use primitives::uint::U256;

let big_len = U256::from(len);
let offset = U256::from_big_endian(&self.random_seed.0) % big_len;
let offset = offset.low_u64() as usize + round_number;
offset % len
}
}

impl<C> bft::Proposer<Block> for Proposer<C>
where
C: PolkadotApi + Send + Sync,
Expand Down Expand Up @@ -441,6 +468,8 @@ impl<C> bft::Proposer<Block> for Proposer<C>
client: self.client.clone(),
transaction_pool: self.transaction_pool.clone(),
table: self.table.clone(),
offline: self.offline.clone(),
validators: self.validators.clone(),
timing,
})
}
Expand Down Expand Up @@ -515,6 +544,13 @@ impl<C> bft::Proposer<Block> for Proposer<C>
includability_tracker.join(temporary_delay)
};

// refuse to vote if this block says a validator is offline that we
// think isn't.
let offline = proposal.noted_offline();
if !self.offline.read().check_consistency(&self.validators[..], offline) {
return Box::new(futures::empty());
}

// evaluate whether the block is actually valid.
// TODO: is it better to delay this until the delays are finished?
let evaluated = self.client
Expand All @@ -536,13 +572,8 @@ impl<C> bft::Proposer<Block> for Proposer<C>
}

fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId {
use primitives::uint::U256;

let len: U256 = authorities.len().into();
let offset = U256::from_big_endian(&self.random_seed.0) % len;
let offset = offset.low_u64() as usize + round_number;

let proposer = authorities[offset % authorities.len()].clone();
let offset = self.primary_index(round_number, authorities.len());
let proposer = authorities[offset].clone();
trace!(target: "bft", "proposer for round {} is {}", round_number, proposer);

proposer
Expand Down Expand Up @@ -611,6 +642,34 @@ impl<C> bft::Proposer<Block> for Proposer<C>
.expect("locally signed extrinsic is valid; qed");
}
}

fn on_round_end(&self, round_number: usize, was_proposed: bool) {
let primary_validator = self.validators[
self.primary_index(round_number, self.validators.len())
];

// alter the message based on whether we think the empty proposer was forced to skip the round.
// this is determined by checking if our local validator would have been forced to skip the round.
let consider_online = was_proposed || {
let forced_delay = self.dynamic_inclusion.acceptable_in(Instant::now(), self.table.includable_count());
match forced_delay {
None => info!(
"Potential Offline Validator: {:?} failed to propose during assigned slot: {}",
primary_validator,
round_number,
),
Some(_) => info!(
"Potential Offline Validator {:?} potentially forced to skip assigned slot: {}",
primary_validator,
round_number,
),
}

forced_delay.is_some()
};

self.offline.write().note_round_end(primary_validator, consider_online);
}
}

fn current_timestamp() -> Timestamp {
Expand Down Expand Up @@ -667,16 +726,42 @@ pub struct CreateProposal<C: PolkadotApi> {
transaction_pool: Arc<TransactionPool<C>>,
table: Arc<SharedTable>,
timing: ProposalTiming,
validators: Vec<AccountId>,
offline: SharedOfflineTracker,
}

impl<C> CreateProposal<C> where C: PolkadotApi {
fn propose_with(&self, candidates: Vec<CandidateReceipt>) -> Result<Block, Error> {
use polkadot_api::BlockBuilder;
use runtime_primitives::traits::{Hash as HashT, BlakeTwo256};
use polkadot_primitives::InherentData;

const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60);

// TODO: handle case when current timestamp behind that in state.
let timestamp = current_timestamp();
let mut block_builder = self.client.build_block(&self.parent_id, timestamp, candidates)?;

let elapsed_since_start = self.timing.dynamic_inclusion.started_at().elapsed();
let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS {
Vec::new()
} else {
self.offline.read().reports(&self.validators[..])
};

if !offline_indices.is_empty() {
info!(
"Submitting offline validators {:?} for slash-vote",
offline_indices.iter().map(|&i| self.validators[i as usize]).collect::<Vec<_>>(),
)
}

let inherent_data = InherentData {
timestamp,
parachain_heads: candidates,
offline_indices,
};

let mut block_builder = self.client.build_block(&self.parent_id, inherent_data)?;

{
let mut unqueue_invalid = Vec::new();
Expand Down
Loading