Conversation
…to retreat-slashing
…or slash calculations
ac9ff84 to
c6f1ed4
Compare
andresilva
left a comment
There was a problem hiding this comment.
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.
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.
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.
Yeah, this really feels wrong.
| client: Arc<C>, | ||
| phantom: PhantomData<P>, | ||
| inherent_data_providers: inherents::InherentDataProviders, | ||
| transaction_pool: Option<Arc<T>>, |
There was a problem hiding this comment.
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.
I'l add add a trait that implements submit_one to the transaction pool, as per offline discussion :) (as it's in my draft).
|
|
||
| /// Represents an Babe equivocation proof. | ||
| #[derive(Debug, Clone, Encode, Decode, PartialEq)] | ||
| pub struct BabeEquivocationProof<H> { |
There was a problem hiding this comment.
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.
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.
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.
I don't think that is possible unless we keep the signature inside. I think signature outside is more flexible.
| } | ||
| } | ||
|
|
||
| pub trait AuthorshipEquivocationProof { |
There was a problem hiding this comment.
IMO this is not needed, it's not abstracting over anything anyway.
There was a problem hiding this comment.
It's abstracting over Aura and Babe equivocation proof (I have the Aura implementation with low priority).
There was a problem hiding this comment.
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.
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.
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.
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.
Why not doing it in this way? Anyway the proof and the SessionIndex should be in the Equivocation, right?.
| } | ||
|
|
||
| /// Get the slot. | ||
| pub fn get_slot<H: Header>(header: &H) -> Result<SlotNumber, &str> |
There was a problem hiding this comment.
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.
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.
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") |
| 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.
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.
but the crate is consensus-common
| #[derive(Clone, PartialEq, Eq, Decode, Encode)] | ||
| pub struct EquivocationProof<H, P, S> { | ||
| pub reporter: P, | ||
| pub identity: P, |
| #[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.
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.
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.
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, |
| (message, round, set_id).encode() | ||
| } | ||
|
|
||
| // TODO [slashing] consider removing and use generic `EquivocationProof`. |
| pub second: (Message<H, N>, AuthoritySignature), | ||
| } | ||
|
|
||
| pub type GrandpaEquivocationFrom<Block> = GrandpaEquivocation< |
There was a problem hiding this comment.
docs? convention is Of, not From
| 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.
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.
why do you want to do this in the node and not in the runtime?
| impl babe::Trait for Runtime { | ||
| type EpochDuration = EpochDuration; | ||
| type ExpectedBlockTime = ExpectedBlockTime; | ||
| type IdentificationTuple = historical::IdentificationTuple<Self>; |
There was a problem hiding this comment.
this is redundant because it can be accessed by the KeyOwnerSystem. (it should be called KeyOwnerProofSystem)
|
|
||
| impl grandpa::Trait for Runtime { | ||
| type Event = Event; | ||
| type IdentificationTuple = historical::IdentificationTuple<Self>; |
| 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.
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.
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.
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 |
rphmeier
left a comment
There was a problem hiding this comment.
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.