Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Init store for slots-headers #2492

Merged
merged 24 commits into from
May 15, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
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
6 changes: 3 additions & 3 deletions core/consensus/aura/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ srml-consensus = { path = "../../../srml/consensus" }
srml-aura = { path = "../../../srml/aura" }
client = { package = "substrate-client", path = "../../client" }
substrate-telemetry = { path = "../../telemetry" }
consensus_common = { package = "substrate-consensus-common", path = "../common" }
authorities = { package = "substrate-consensus-authorities", path = "../authorities" }
runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives" }
futures = "0.1.17"
tokio = "0.1.7"
parking_lot = "0.7.1"
error-chain = "0.12"
log = "0.4"
consensus_common = { package = "substrate-consensus-common", path = "../common" }
authorities = { package = "substrate-consensus-authorities", path = "../authorities" }
runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives" }

[dev-dependencies]
keyring = { package = "substrate-keyring", path = "../../keyring" }
Expand Down
101 changes: 90 additions & 11 deletions core/consensus/aura/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ use srml_aura::{
};
use substrate_telemetry::{telemetry, CONSENSUS_TRACE, CONSENSUS_DEBUG, CONSENSUS_WARN, CONSENSUS_INFO};

use slots::{CheckedHeader, SlotWorker, SlotInfo, SlotCompatible, slot_now};
use slots::{CheckedHeader, SlotWorker, SlotInfo, SlotCompatible, slot_now, check_equivocation};

pub use aura_primitives::*;
pub use consensus_common::{SyncOracle, ExtraVerification};
Expand Down Expand Up @@ -193,7 +193,7 @@ pub fn start_aura_thread<B, C, SC, E, I, P, SO, Error, OnExit>(
force_authoring: bool,
) -> Result<(), consensus_common::Error> where
B: Block + 'static,
C: ProvideRuntimeApi + ProvideCache<B> + Send + Sync + 'static,
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore + Send + Sync + 'static,
C::Api: AuthoritiesApi<B>,
SC: SelectChain<B> + Clone + 'static,
E: Environment<B, Error=Error> + Send + Sync + 'static,
Expand Down Expand Up @@ -244,7 +244,7 @@ pub fn start_aura<B, C, SC, E, I, P, SO, Error, OnExit>(
force_authoring: bool,
) -> Result<impl Future<Item=(), Error=()>, consensus_common::Error> where
B: Block,
C: ProvideRuntimeApi + ProvideCache<B>,
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore,
C::Api: AuthoritiesApi<B>,
SC: SelectChain<B> + Clone,
E: Environment<B, Error=Error>,
Expand Down Expand Up @@ -289,7 +289,7 @@ struct AuraWorker<C, E, I, P, SO> {
}

impl<B: Block, C, E, I, P, Error, SO> SlotWorker<B> for AuraWorker<C, E, I, P, SO> where
C: ProvideRuntimeApi + ProvideCache<B>,
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore,
C::Api: AuthoritiesApi<B>,
E: Environment<B, Error=Error>,
E::Proposer: Proposer<B, Error=Error>,
Expand Down Expand Up @@ -456,16 +456,18 @@ impl<B: Block, C, E, I, P, Error, SO> SlotWorker<B> for AuraWorker<C, E, I, P, S
/// This digest item will always return `Some` when used with `as_aura_seal`.
//
// FIXME #1018 needs misbehavior types
fn check_header<B: Block, P: Pair>(
fn check_header<C, B: Block, P: Pair>(
client: &Arc<C>,
slot_now: u64,
mut header: B::Header,
hash: B::Hash,
authorities: &[AuthorityId<P>],
allow_old_seals: bool,
) -> Result<CheckedHeader<B::Header, DigestItemFor<B>>, String>
where DigestItemFor<B>: CompatibleDigestItem<P>,
P::Public: AsRef<P::Public>,
P::Signature: Decode,
C: client::backend::AuxStore,
P::Public: AsRef<P::Public> + Encode + Decode + PartialEq,
{
let digest_item = match header.digest_mut().pop() {
Some(x) => x,
Expand Down Expand Up @@ -498,7 +500,26 @@ fn check_header<B: Block, P: Pair>(
let public = expected_author;

if P::verify(&sig, &to_sign[..], public) {
Ok(CheckedHeader::Checked(header, digest_item))
match check_equivocation::<_, _, <P as Pair>::Public>(
client,
slot_now,
slot_num,
header.clone(),
public.clone(),
) {
Ok(Some(equivocation_proof)) => {
let log_str = format!(
"Slot author is equivocating at slot {} with headers {:?} and {:?}",
slot_num,
equivocation_proof.fst_header().hash(),
equivocation_proof.snd_header().hash(),
);
info!("{}", log_str);
Err(log_str)
},
Ok(None) => Ok(CheckedHeader::Checked(header, digest_item)),
Err(e) => Err(e.to_string()),
}
} else {
Err(format!("Bad signature on {:?}", hash))
}
Expand Down Expand Up @@ -580,7 +601,7 @@ impl<B: Block> ExtraVerification<B> for NothingExtra {

#[forbid(deprecated)]
impl<B: Block, C, E, P> Verifier<B> for AuraVerifier<C, E, P> where
C: ProvideRuntimeApi + Send + Sync,
C: ProvideRuntimeApi + Send + Sync + client::backend::AuxStore,
C::Api: BlockBuilderApi<B>,
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>>,
E: ExtraVerification<B>,
Expand Down Expand Up @@ -611,7 +632,8 @@ impl<B: Block, C, E, P> Verifier<B> for AuraVerifier<C, E, P> where

// we add one to allow for some small drift.
// FIXME #1019 in the future, alter this queue to allow deferring of headers
let checked_header = check_header::<B, P>(
let checked_header = check_header::<C, B, P>(
&self.client,
slot_now + 1,
header,
hash,
Expand Down Expand Up @@ -736,7 +758,7 @@ pub fn import_queue<B, C, E, P>(
inherent_data_providers: InherentDataProviders,
) -> Result<AuraImportQueue<B>, consensus_common::Error> where
B: Block,
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync,
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + AuxStore,
C::Api: BlockBuilderApi<B> + AuthoritiesApi<B>,
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>>,
E: 'static + ExtraVerification<B>,
Expand Down Expand Up @@ -772,7 +794,7 @@ pub fn import_queue_accept_old_seals<B, C, E, P>(
inherent_data_providers: InherentDataProviders,
) -> Result<AuraImportQueue<B>, consensus_common::Error> where
B: Block,
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync,
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + AuxStore,
C::Api: BlockBuilderApi<B> + AuthoritiesApi<B>,
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>>,
E: 'static + ExtraVerification<B>,
Expand Down Expand Up @@ -808,6 +830,9 @@ mod tests {
use primitives::sr25519;
use client::{LongestChain, BlockchainEvents};
use test_client;
use primitives::hash::H256;
use runtime_primitives::testing::{Header as HeaderTest, Digest as DigestTest, Block as RawBlock, ExtrinsicWrapper};
use slots::{MAX_SLOT_CAPACITY, PRUNING_BOUND};

type Error = client::error::Error;

Expand Down Expand Up @@ -899,6 +924,26 @@ mod tests {
}
}

fn create_header(slot_num: u64, number: u64, pair: &sr25519::Pair) -> (HeaderTest, H256) {
let mut header = HeaderTest {
parent_hash: Default::default(),
number,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: DigestTest { logs: vec![], },
};
let header_hash: H256 = header.hash();
let to_sign = (slot_num, header_hash).encode();
let signature = pair.sign(&to_sign[..]);

let item = <generic::DigestItem<_, _, _> as CompatibleDigestItem<sr25519::Pair>>::aura_seal(
slot_num,
signature,
);
header.digest_mut().push(item);
(header, header_hash)
}

#[test]
fn authoring_blocks() {
let _ = ::env_logger::try_init();
Expand Down Expand Up @@ -981,4 +1026,38 @@ mod tests {
Keyring::Charlie.into()
]);
}

#[test]
fn check_header_works_with_equivocation() {
let client = test_client::new();
let pair = sr25519::Pair::generate();
let public = pair.public();
let authorities = vec![public.clone()];

let (header1, header1_hash) = create_header(1, 1, &pair);
let (header2, header2_hash) = create_header(1, 2, &pair);
let (header3, header3_hash) = create_header(2, 2, &pair);
let (header4, header4_hash) = create_header(MAX_SLOT_CAPACITY + 2, 3, &pair);
let (header5, header5_hash) = create_header(MAX_SLOT_CAPACITY + 2, 4, &pair);

type B = RawBlock<ExtrinsicWrapper<u64>>;
type P = sr25519::Pair;

let c = Arc::new(client);

// It's ok to sign same headers.
assert!(check_header::<_, B, P>(&c, 1, header1.clone(), header1_hash, &authorities, false).is_ok());
assert!(check_header::<_, B, P>(&c, 1, header1, header1_hash, &authorities, false).is_ok());

// But not two different headers at the same slot.
assert!(check_header::<_, B, P>(&c, 1, header2, header2_hash, &authorities, false).is_err());

// Different slot is ok.
assert!(check_header::<_, B, P>(&c, 2, header3.clone(), header3_hash, &authorities, false).is_ok());

// Pruning works.
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND, header4, header4_hash, &authorities, false).is_ok());
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 1, header5, header5_hash, &authorities, false).is_err());
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 2, header3, header3_hash, &authorities, false).is_ok());
}
}
106 changes: 100 additions & 6 deletions core/consensus/babe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ use client::{
error::Result as CResult,
backend::AuxStore,
};
use slots::CheckedHeader;
use slots::{CheckedHeader, check_equivocation};
use futures::{Future, IntoFuture, future};
use tokio::timer::Timeout;
use log::{error, warn, debug, info, trace};
Expand Down Expand Up @@ -534,7 +534,8 @@ impl<B: Block, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> whe
//
// FIXME #1018 needs misbehavior types
#[forbid(warnings)]
fn check_header<B: Block + Sized>(
fn check_header<B: Block + Sized, C: AuxStore>(
client: &Arc<C>,
slot_now: u64,
mut header: B::Header,
hash: B::Hash,
Expand All @@ -551,7 +552,7 @@ fn check_header<B: Block + Sized>(

let BabeSeal {
slot_num,
signature: LocalizedSignature {signer, signature },
signature: LocalizedSignature { signer, signature },
proof,
vrf_output,
} = digest_item.as_babe_seal().ok_or_else(|| {
Expand Down Expand Up @@ -584,8 +585,27 @@ fn check_header<B: Block + Sized>(
format!("VRF verification failed")
})?
};

if check(&inout, threshold) {
Ok(CheckedHeader::Checked(header, digest_item))
match check_equivocation(&client, slot_now, slot_num, header.clone(), signer.clone()) {
Ok(Some(equivocation_proof)) => {
let log_str = format!(
"Slot author {:?} is equivocating at slot {} with headers {:?} and {:?}",
signer,
slot_num,
equivocation_proof.fst_header().hash(),
equivocation_proof.snd_header().hash(),
);
info!("{}", log_str);
Err(log_str)
},
Ok(None) => {
Ok(CheckedHeader::Checked(header, digest_item))
},
Err(e) => {
Err(e.to_string())
},
}
} else {
debug!(target: "babe", "VRF verification failed: threshold {} exceeded", threshold);
Err(format!("Validator {:?} made seal when it wasn’t its turn", signer))
Expand Down Expand Up @@ -643,7 +663,7 @@ impl<B: Block> ExtraVerification<B> for NothingExtra {
}

impl<B: Block, C, E> Verifier<B> for BabeVerifier<C, E> where
C: ProvideRuntimeApi + Send + Sync,
C: ProvideRuntimeApi + Send + Sync + AuxStore,
C::Api: BlockBuilderApi<B>,
DigestItemFor<B>: CompatibleDigestItem + DigestItem<AuthorityId=Public>,
E: ExtraVerification<B>,
Expand Down Expand Up @@ -683,7 +703,8 @@ impl<B: Block, C, E> Verifier<B> for BabeVerifier<C, E> where
// we add one to allow for some small drift.
// FIXME #1019 in the future, alter this queue to allow deferring of
// headers
let checked_header = check_header::<B>(
let checked_header = check_header::<B, C>(
&self.client,
slot_now + 1,
header,
hash,
Expand Down Expand Up @@ -871,6 +892,10 @@ mod tests {
use futures::stream::Stream;
use log::debug;
use std::time::Duration;
use test_client::AuthorityKeyring;
use primitives::hash::H256;
use runtime_primitives::testing::{Header as HeaderTest, Digest as DigestTest, Block as RawBlock, ExtrinsicWrapper};
use slots::{MAX_SLOT_CAPACITY, PRUNING_BOUND};

type Error = client::error::Error;

Expand Down Expand Up @@ -974,6 +999,40 @@ mod tests {
}
}

fn create_header(slot_num: u64, number: u64, pair: &sr25519::Pair) -> (HeaderTest, H256) {
let mut header = HeaderTest {
parent_hash: Default::default(),
number,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: DigestTest { logs: vec![], },
};

let transcript = make_transcript(
Default::default(),
slot_num,
Default::default(),
0,
);

let (inout, proof, _batchable_proof) = get_keypair(&pair).vrf_sign_n_check(transcript, |inout| check(inout, u64::MAX)).unwrap();
let pre_hash: H256 = header.hash();
let to_sign = (slot_num, pre_hash, proof.to_bytes()).encode();
let signature = pair.sign(&to_sign[..]);
let item = <generic::DigestItem<_, _, _> as CompatibleDigestItem>::babe_seal(BabeSeal {
proof,
signature: LocalizedSignature {
signature,
signer: pair.public(),
},
slot_num,
vrf_output: inout.to_output(),
});

header.digest_mut().push(item);
(header, pre_hash)
}

#[test]
fn can_serialize_block() {
drop(env_logger::try_init());
Expand Down Expand Up @@ -1103,4 +1162,39 @@ mod tests {
Keyring::Charlie.into()
]);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stray newline

#[test]
fn check_header_works_with_equivocation() {
let client = test_client::new();
let pair = sr25519::Pair::generate();
let public = pair.public();
let authorities = vec![public.clone()];

let (header1, header1_hash) = create_header(1, 1, &pair);
let (header2, header2_hash) = create_header(1, 2, &pair);
let (header3, header3_hash) = create_header(2, 2, &pair);
let (header4, header4_hash) = create_header(MAX_SLOT_CAPACITY + 2, 3, &pair);
let (header5, header5_hash) = create_header(MAX_SLOT_CAPACITY + 2, 4, &pair);

let c = Arc::new(client);
let max = u64::MAX;

type B = RawBlock<ExtrinsicWrapper<u64>>;
type P = sr25519::Pair;

// It's ok to sign same headers.
assert!(check_header::<B, _>(&c, 1, header1.clone(), header1_hash, &authorities, max).is_ok());
assert!(check_header::<B, _>(&c, 1, header1, header1_hash, &authorities, max).is_ok());

// But not two different headers at the same slot.
assert!(check_header::<B, _>(&c, 1, header2, header2_hash, &authorities, max).is_err());

// Different slot is ok.
assert!(check_header::<B, _>(&c, 2, header3.clone(), header3_hash, &authorities, max).is_ok());

// Pruning works.
assert!(check_header::<B, _>(&c, PRUNING_BOUND, header4, header4_hash, &authorities, max).is_ok());
assert!(check_header::<B, _>(&c, PRUNING_BOUND + 1, header5, header5_hash, &authorities, max).is_err());
assert!(check_header::<B, _>(&c, PRUNING_BOUND + 2, header3, header3_hash, &authorities, max).is_ok());
}
}
Loading