-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Conversation
…to retreat-slashing
…or slash calculations
ac9ff84
to
c6f1ed4
Compare
There was a problem hiding this 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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
core/consensus/aura/src/lib.rs
Outdated
client: Arc<C>, | ||
phantom: PhantomData<P>, | ||
inherent_data_providers: inherents::InherentDataProviders, | ||
transaction_pool: Option<Arc<T>>, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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> { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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(), |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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)] |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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`. |
There was a problem hiding this comment.
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< |
There was a problem hiding this comment.
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>, |
There was a problem hiding this comment.
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(), |
There was a problem hiding this comment.
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>; |
There was a problem hiding this comment.
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>; |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah..
There was a problem hiding this 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
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. |
No description provided.