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

Equivocations slashing #3314

Closed
wants to merge 211 commits into from
Closed

Equivocations slashing #3314

wants to merge 211 commits into from

Conversation

marcio-diaz
Copy link
Contributor

No description provided.

@marcio-diaz marcio-diaz force-pushed the marcio/all-equivocation-0 branch from ac9ff84 to c6f1ed4 Compare August 7, 2019 07:47
Copy link
Contributor

@andresilva andresilva left a comment

Choose a reason for hiding this comment

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

I think some of the stuff can be simplified. But overall the structure that we're going to use to detect and construct equivocation proofs, using the runtime to construct the report and eventually signing and submitting the tx is mostly fleshed out now.

pub trait CompatibleDigestItem: Sized {
/// Construct a digest item which contains a BABE pre-digest.
fn babe_pre_digest(seal: BabePreDigest) -> Self;
fn babe_pre_digest<D: Codec>(seal: D) -> Self;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this change needed? The runtime should be using RawBabePreDigest anyway. This function signature doesn't make a lot of sense, it looks like you can give it anything and it will make a BABE seal which is not the case at all, since you need to give it some specific struct to make a proper BABE seal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it should be a better way to do it. But it also using a non raw pre digest.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, this really feels wrong.

client: Arc<C>,
phantom: PhantomData<P>,
inherent_data_providers: inherents::InherentDataProviders,
transaction_pool: Option<Arc<T>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be replaced with a F: Fn(Block::Extrinsic) which will just push the extrinsic to the transaction pool As it stands you can't do anything with this T, and it's probably easier to not drag any complicated TransactionPool types here.

Copy link
Contributor Author

@marcio-diaz marcio-diaz Aug 7, 2019

Choose a reason for hiding this comment

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

I'l add add a trait that implements submit_one to the transaction pool, as per offline discussion :) (as it's in my draft).

@@ -133,6 +138,90 @@ impl slots::SlotData for BabeConfiguration {
const SLOT_KEY: &'static [u8] = b"babe_bootstrap_data";
}

/// Represents an Babe equivocation proof.
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub struct BabeEquivocationProof<H> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we had a type for EquivocationProof already (in slots). Why add another? I also don't know why you need the authority signatures here, if you pass the headers you can extract the author from it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we are taking the signatures out of the header (I think we talk about this yesterday). In your case I will need to push the signature again, and pop it again when checking...

Copy link
Contributor

Choose a reason for hiding this comment

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

But current struct doesn't really enforce consistency, how do you prevent creating it with first_signature not matching first_header.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think that is possible unless we keep the signature inside. I think signature outside is more flexible.

@@ -29,3 +31,45 @@ decl_runtime_apis! {
fn authorities() -> Vec<AuthorityId>;
}
}

pub trait AuthorshipEquivocationProof {
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO this is not needed, it's not abstracting over anything anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's abstracting over Aura and Babe equivocation proof (I have the Aura implementation with low priority).

Copy link
Contributor

Choose a reason for hiding this comment

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

https://github.com/paritytech/substrate/pull/3314/files/1a88f9016ae3b16937afce5e3fb189749dd8e521#diff-1144b2e008f250f4ca22726b35ee22c7R65

How will you implement this for GRANDPA? Same goes for some other methods which are specific to block proposal equivocations. My point is there's too little in common between BABE and GRANDPA equivocations to abstract anything, so there's no point in abstracting. They'll be reported to different modules anyway so I don't know why you need a common type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's interfacing Aura and Babe with the Slots module. If I remove this I need to duplicate code in Slots module, or there is a better way?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I misread your comment earlier and thought this was supposed to abstract over GRANDPA and BABE.

fst_header: prev_header.clone(),
snd_header: header.clone(),
}));
return Ok(Some(AuthorshipEquivocationProof::new(
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just use the existing struct and just pass the remaining data as arguments to the construct report call?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why not doing it in this way? Anyway the proof and the SessionIndex should be in the Equivocation, right?.

@@ -93,6 +88,30 @@ impl BabePreDigest {
}
}

/// Get the slot.
pub fn get_slot<H: Header>(header: &H) -> Result<SlotNumber, &str>
Copy link
Contributor

Choose a reason for hiding this comment

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

The documentation and function naming needs real improvement


/// Extract the BABE pre digest from the given header. Pre-runtime digests are
/// mandatory, the function will return `Err` if none is found.
pub fn find_pre_digest<H: Header, D: Codec>(header: &H) -> Result<D, &str>
Copy link
Contributor

Choose a reason for hiding this comment

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

it's strange to return a str with the lifetime of the given header as opposed to a &'static str.

let block_id = BlockId::number(client.info().best_number);
let maybe_report_transaction = client
.runtime_api()
.construct_equivocation_transaction_with_context(
Copy link
Contributor

Choose a reason for hiding this comment

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

This is IMO not the right level of abstraction.

It's better to think of things in terms of making code testable: rather than a SubmitExtrinsic trait (which is itself better than passing a raw transaction queue), you should have a SubmitEquivocationProof trait, which deals with extrinsics internally.

This means that tests don't have to deal with extrinsics, which is way more nasty.

});
info!(target: "afg", "Babe equivocation report has been submitted")
} else {
error!(target: "afg", "Error constructing Babe equivocation report")
Copy link
Contributor

Choose a reason for hiding this comment

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

why afg?

@@ -151,7 +165,8 @@ impl TestNetFactory for BabeTestNet {
inherent_data_providers,
config,
time_source: Default::default(),
transaction_pool : Default::default(),
// TODO [slashing] get the transaction pool from somewhere.
transaction_pool: Default::default(),
Copy link
Contributor

Choose a reason for hiding this comment

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

see -- you have trouble writing the test because you've tried to push too much onto the BABE logic.

use client::decl_runtime_apis;
use rstd::vec::Vec;

/// Represents an Babe equivocation proof.
Copy link
Contributor

Choose a reason for hiding this comment

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

but the crate is consensus-common

#[derive(Clone, PartialEq, Eq, Decode, Encode)]
pub struct EquivocationProof<H, P, S> {
pub reporter: P,
pub identity: P,
Copy link
Contributor

Choose a reason for hiding this comment

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

no docs.

#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Clone, PartialEq, Eq, Decode, Encode)]
pub struct EquivocationProof<H, P, S> {
pub reporter: P,
Copy link
Contributor

@rphmeier rphmeier Aug 18, 2019

Choose a reason for hiding this comment

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

why should the proof care? runtime should just infer this from the transaction author.

pub struct EquivocationProof<H, P, S> {
pub reporter: P,
pub identity: P,
pub slot: u64,
Copy link
Contributor

Choose a reason for hiding this comment

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

should use a strong type alias as in the one from babe primitives.

I'm not sure why consensus-common needs to define this type. Trying to unify things like equivocation proofs across all consensus engines is going to fail.

#![deny(warnings)]
#![forbid(unsafe_code, missing_docs)]
// #![deny(warnings)]
// #![forbid(unsafe_code, missing_docs)]
Copy link
Contributor

Choose a reason for hiding this comment

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

don't comment out code

Copy link
Contributor

Choose a reason for hiding this comment

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

it should be either removed or left there

#[derive(Clone, PartialEq, Eq, Encode, Decode)]
pub struct GrandpaEquivocation<H, N> {
/// Reporter of the equivocation.
pub reporter: AuthorityId,
Copy link
Contributor

Choose a reason for hiding this comment

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

why does it matter here?

(message, round, set_id).encode()
}

// TODO [slashing] consider removing and use generic `EquivocationProof`.
Copy link
Contributor

Choose a reason for hiding this comment

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

"generic"

pub second: (Message<H, N>, AuthoritySignature),
}

pub type GrandpaEquivocationFrom<Block> = GrandpaEquivocation<
Copy link
Contributor

Choose a reason for hiding this comment

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

docs? convention is Of, not From

@@ -377,9 +379,10 @@ pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA, SC> {
pub(crate) network: crate::communication::NetworkBridge<Block, N>,
pub(crate) set_id: u64,
pub(crate) voter_set_state: SharedVoterSetState<Block>,
pub(crate) transaction_pool: Arc<T>,
Copy link
Contributor

@rphmeier rphmeier Aug 18, 2019

Choose a reason for hiding this comment

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

Again: generation of the extrinsic is beyond the scope of this structure.

reporter,
round_number: equivocation.round_number,
// TODO [slashing] get proper session index.
session_index: SessionIndex::default(),
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you want to do this in the node and not in the runtime?

@@ -136,6 +141,10 @@ parameter_types! {
impl babe::Trait for Runtime {
type EpochDuration = EpochDuration;
type ExpectedBlockTime = ExpectedBlockTime;
type IdentificationTuple = historical::IdentificationTuple<Self>;
Copy link
Contributor

Choose a reason for hiding this comment

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

this is redundant because it can be accessed by the KeyOwnerSystem. (it should be called KeyOwnerProofSystem)

@@ -407,6 +416,9 @@ impl offences::Trait for Runtime {

impl grandpa::Trait for Runtime {
type Event = Event;
type IdentificationTuple = historical::IdentificationTuple<Self>;
Copy link
Contributor

Choose a reason for hiding this comment

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

also redundant

if equivocation.slot == first_slot && first_slot == second_slot {
let author = &equivocation.identity;

if !author.verify(&first_header.hash(), &equivocation.first_signature) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is a serious misunderstanding of BABE - the hash includes the seal includes the signature, so it will always fail. I'm not sure why the entire header needs to go into the proof at all.

return Err("invalid equivocation")
}

let to_punish = <T as Trait>::KeyOwnerSystem::check_proof(
Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer if the proof was checked first as opposed to afterwards

let raw_payload = (function, extra.clone(), genesis_hash, genesis_hash);

let maybe_signature: Option<app::Signature> = raw_payload.using_encoded(|payload| if payload.len() > 256 {
reporter.sign(&sr_io::blake2_256(payload))
Copy link
Contributor

@rphmeier rphmeier Aug 18, 2019

Choose a reason for hiding this comment

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

have you tested that this actually works? I think you're assuming that authority key is an account-crypto key.


let identity = &equivocation.identity;

if first_vote == second_vote { // TODO: fix equality before merging
Copy link
Contributor

Choose a reason for hiding this comment

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

yeah..

Copy link
Contributor

@rphmeier rphmeier left a comment

Choose a reason for hiding this comment

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

The APIs could be cleaner and consider relationships and responsibilities of types more carefully.

Needs tests (as I'm not convinced this will work as-is), and there are a number of un-done TODOs

@tomusdrw
Copy link
Contributor

The commit history of this PR grew too much and there is still a lot of work that needs changing. This PR is going to be superseded by a new branch where we will copy over the correct parts.

Closing it now to save the CI and reviewers cycles.

@tomusdrw tomusdrw closed this Aug 21, 2019
@marcio-diaz marcio-diaz deleted the marcio/all-equivocation-0 branch September 16, 2019 01:19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A3-in_progress Pull request is in progress. No review needed at this stage.
Projects
None yet
Development

Successfully merging this pull request may close these issues.