diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index e971830c95cb..cdef39d5bdf1 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -252,12 +252,37 @@ sp_api::impl_runtime_apis! { unimplemented!() } + fn submit_report_fork_voting_unsigned_extrinsic( + _: sp_consensus_beefy::ForkVotingProof< + ::Header, + BeefyId, + sp_runtime::OpaqueValue + >, + _: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + unimplemented!() + } + + fn submit_report_future_block_voting_unsigned_extrinsic( + _: sp_consensus_beefy::FutureBlockVotingProof, + _: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + unimplemented!() + } + fn generate_key_ownership_proof( _: sp_consensus_beefy::ValidatorSetId, _: BeefyId, ) -> Option { unimplemented!() } + + fn generate_ancestry_proof( + _: BlockNumber, + _: Option, + ) -> Option { + unimplemented!() + } } impl sp_mmr_primitives::MmrApi for Runtime { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 058b9c279f2a..2a1cb58fc17f 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -2054,7 +2054,7 @@ sp_api::impl_runtime_apis! { } } - #[api_version(4)] + #[api_version(5)] impl sp_consensus_beefy::BeefyApi for Runtime { fn beefy_genesis() -> Option { pallet_beefy::GenesisBlock::::get() @@ -2080,6 +2080,31 @@ sp_api::impl_runtime_apis! { ) } + fn submit_report_fork_voting_unsigned_extrinsic( + equivocation_proof: + sp_consensus_beefy::ForkVotingProof< + ::Header, + BeefyId, + sp_runtime::OpaqueValue + >, + key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + Beefy::submit_unsigned_fork_voting_report( + equivocation_proof.try_into()?, + key_owner_proof.decode()?, + ) + } + + fn submit_report_future_block_voting_unsigned_extrinsic( + equivocation_proof: sp_consensus_beefy::FutureBlockVotingProof, + key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + Beefy::submit_unsigned_future_block_voting_report( + equivocation_proof, + key_owner_proof.decode()?, + ) + } + fn generate_key_ownership_proof( _set_id: sp_consensus_beefy::ValidatorSetId, authority_id: BeefyId, @@ -2090,6 +2115,17 @@ sp_api::impl_runtime_apis! { .map(|p| p.encode()) .map(sp_consensus_beefy::OpaqueKeyOwnershipProof::new) } + + fn generate_ancestry_proof( + prev_block_number: BlockNumber, + best_known_block_number: Option, + ) -> Option { + use sp_consensus_beefy::AncestryHelper; + + MmrLeaf::generate_proof(prev_block_number, best_known_block_number) + .map(|p| p.encode()) + .map(sp_runtime::OpaqueValue::new) + } } #[api_version(2)] diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index a8a369a68e66..8e34320d38f2 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -1029,12 +1029,38 @@ sp_api::impl_runtime_apis! { None } + fn submit_report_fork_voting_unsigned_extrinsic( + _equivocation_proof: + sp_consensus_beefy::ForkVotingProof< + ::Header, + BeefyId, + sp_runtime::OpaqueValue + >, + _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } + + fn submit_report_future_block_voting_unsigned_extrinsic( + _equivocation_proof: sp_consensus_beefy::FutureBlockVotingProof, + _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } + fn generate_key_ownership_proof( _set_id: sp_consensus_beefy::ValidatorSetId, _authority_id: BeefyId, ) -> Option { None } + + fn generate_ancestry_proof( + _prev_block_number: BlockNumber, + _best_known_block_number: Option, + ) -> Option { + None + } } impl mmr::MmrApi for Runtime { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 13a69050c342..093476106518 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2109,7 +2109,7 @@ sp_api::impl_runtime_apis! { } } - #[api_version(4)] + #[api_version(5)] impl sp_consensus_beefy::BeefyApi for Runtime { fn beefy_genesis() -> Option { pallet_beefy::GenesisBlock::::get() @@ -2135,6 +2135,31 @@ sp_api::impl_runtime_apis! { ) } + fn submit_report_fork_voting_unsigned_extrinsic( + equivocation_proof: + sp_consensus_beefy::ForkVotingProof< + ::Header, + BeefyId, + sp_runtime::OpaqueValue + >, + key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + Beefy::submit_unsigned_fork_voting_report( + equivocation_proof.try_into()?, + key_owner_proof.decode()?, + ) + } + + fn submit_report_future_block_voting_unsigned_extrinsic( + equivocation_proof: sp_consensus_beefy::FutureBlockVotingProof, + key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + Beefy::submit_unsigned_future_block_voting_report( + equivocation_proof, + key_owner_proof.decode()?, + ) + } + fn generate_key_ownership_proof( _set_id: sp_consensus_beefy::ValidatorSetId, authority_id: BeefyId, @@ -2145,6 +2170,17 @@ sp_api::impl_runtime_apis! { .map(|p| p.encode()) .map(sp_consensus_beefy::OpaqueKeyOwnershipProof::new) } + + fn generate_ancestry_proof( + prev_block_number: BlockNumber, + best_known_block_number: Option, + ) -> Option { + use sp_consensus_beefy::AncestryHelper; + + BeefyMmrLeaf::generate_proof(prev_block_number, best_known_block_number) + .map(|p| p.encode()) + .map(sp_runtime::OpaqueValue::new) + } } impl mmr::MmrApi for Runtime { diff --git a/prdoc/pr_4993.prdoc b/prdoc/pr_4993.prdoc new file mode 100644 index 000000000000..d822d5cd6c76 --- /dev/null +++ b/prdoc/pr_4993.prdoc @@ -0,0 +1,27 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Added BEEFY equivocation-related methods to BeefyApi + +doc: + - audience: Node Dev + description: | + This PR adds the `generate_ancestry_proof`, `submit_report_fork_voting_unsigned_extrinsic` and + `submit_report_future_block_voting_unsigned_extrinsic` to `BeefyApi` and bumps the `BeefyApi` version + from 4 to 5. + +crates: + - name: pallet-beefy + bump: minor + - name: pallet-beefy-mmr + bump: minor + - name: kitchensink-runtime + bump: major + - name: rococo-runtime + bump: major + - name: westend-runtime + bump: major + - name: sp-consensus-beefy + bump: minor + - name: polkadot-service + bump: patch diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 997213447262..cad2cc119f0d 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -3038,7 +3038,7 @@ impl_runtime_apis! { } } - #[api_version(4)] + #[api_version(5)] impl sp_consensus_beefy::BeefyApi for Runtime { fn beefy_genesis() -> Option { pallet_beefy::GenesisBlock::::get() @@ -3064,6 +3064,31 @@ impl_runtime_apis! { ) } + fn submit_report_fork_voting_unsigned_extrinsic( + equivocation_proof: + sp_consensus_beefy::ForkVotingProof< + ::Header, + BeefyId, + sp_runtime::OpaqueValue + >, + key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + Beefy::submit_unsigned_fork_voting_report( + equivocation_proof.try_into()?, + key_owner_proof.decode()?, + ) + } + + fn submit_report_future_block_voting_unsigned_extrinsic( + equivocation_proof: sp_consensus_beefy::FutureBlockVotingProof, + key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { + Beefy::submit_unsigned_future_block_voting_report( + equivocation_proof, + key_owner_proof.decode()?, + ) + } + fn generate_key_ownership_proof( _set_id: sp_consensus_beefy::ValidatorSetId, authority_id: BeefyId, @@ -3072,6 +3097,17 @@ impl_runtime_apis! { .map(|p| p.encode()) .map(sp_consensus_beefy::OpaqueKeyOwnershipProof::new) } + + fn generate_ancestry_proof( + prev_block_number: BlockNumber, + best_known_block_number: Option, + ) -> Option { + use sp_consensus_beefy::AncestryHelper; + + MmrLeaf::generate_proof(prev_block_number, best_known_block_number) + .map(|p| p.encode()) + .map(sp_runtime::OpaqueValue::new) + } } impl pallet_mmr::primitives::MmrApi< diff --git a/substrate/frame/beefy-mmr/src/lib.rs b/substrate/frame/beefy-mmr/src/lib.rs index ec341cad2084..195bbfbf2f29 100644 --- a/substrate/frame/beefy-mmr/src/lib.rs +++ b/substrate/frame/beefy-mmr/src/lib.rs @@ -183,6 +183,24 @@ where type Proof = AncestryProof>; type ValidationContext = MerkleRootOf; + fn generate_proof( + prev_block_number: BlockNumberFor, + best_known_block_number: Option>, + ) -> Option { + pallet_mmr::Pallet::::generate_ancestry_proof(prev_block_number, best_known_block_number) + .map_err(|e| { + log::error!( + target: "runtime::beefy", + "Failed to generate ancestry proof for block {:?} at {:?}: {:?}", + prev_block_number, + best_known_block_number, + e + ); + e + }) + .ok() + } + fn extract_validation_context(header: HeaderFor) -> Option { // Check if the provided header is canonical. let expected_hash = frame_system::Pallet::::block_hash(header.number()); diff --git a/substrate/frame/beefy/src/lib.rs b/substrate/frame/beefy/src/lib.rs index fd9a0027c6fc..353ba876c7ed 100644 --- a/substrate/frame/beefy/src/lib.rs +++ b/substrate/frame/beefy/src/lib.rs @@ -538,8 +538,8 @@ impl Pallet { ValidatorSet::::new(validators, id) } - /// Submits an extrinsic to report an equivocation. This method will create - /// an unsigned extrinsic with a call to `report_equivocation_unsigned` and + /// Submits an extrinsic to report a double voting equivocation. This method will create + /// an unsigned extrinsic with a call to `report_double_voting_unsigned` and /// will push the transaction to the pool. Only useful in an offchain context. pub fn submit_unsigned_double_voting_report( equivocation_proof: DoubleVotingProof< @@ -556,6 +556,37 @@ impl Pallet { .ok() } + /// Submits an extrinsic to report a fork voting equivocation. This method will create + /// an unsigned extrinsic with a call to `report_fork_voting_unsigned` and + /// will push the transaction to the pool. Only useful in an offchain context. + pub fn submit_unsigned_fork_voting_report( + equivocation_proof: ForkVotingProof< + HeaderFor, + T::BeefyId, + >>::Proof, + >, + key_owner_proof: T::KeyOwnerProof, + ) -> Option<()> { + T::EquivocationReportSystem::publish_evidence(EquivocationEvidenceFor::ForkVotingProof( + equivocation_proof, + key_owner_proof, + )) + .ok() + } + + /// Submits an extrinsic to report a future block voting equivocation. This method will create + /// an unsigned extrinsic with a call to `report_future_block_voting_unsigned` and + /// will push the transaction to the pool. Only useful in an offchain context. + pub fn submit_unsigned_future_block_voting_report( + equivocation_proof: FutureBlockVotingProof, T::BeefyId>, + key_owner_proof: T::KeyOwnerProof, + ) -> Option<()> { + T::EquivocationReportSystem::publish_evidence( + EquivocationEvidenceFor::FutureBlockVotingProof(equivocation_proof, key_owner_proof), + ) + .ok() + } + fn change_authorities( new: BoundedVec, queued: BoundedVec, diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index 03efccff7643..a0880660d051 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -111,6 +111,13 @@ impl AncestryHelper
for MockAncestryHelper { type Proof = MockAncestryProof; type ValidationContext = MockAncestryProofContext; + fn generate_proof( + _prev_block_number: Header::Number, + _best_known_block_number: Option, + ) -> Option { + unimplemented!() + } + fn extract_validation_context(_header: Header) -> Option { AncestryProofContext::get() } diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs index 7f6f733d0e39..020e1667cbc7 100644 --- a/substrate/primitives/consensus/beefy/src/lib.rs +++ b/substrate/primitives/consensus/beefy/src/lib.rs @@ -349,6 +349,19 @@ pub struct ForkVotingProof pub header: Header, } +impl ForkVotingProof { + /// Try to decode the `AncestryProof`. + pub fn try_into( + self, + ) -> Option> { + Some(ForkVotingProof:: { + vote: self.vote, + ancestry_proof: self.ancestry_proof.decode()?, + header: self.header, + }) + } +} + /// Proof showing that an authority voted for a future block. #[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] pub struct FutureBlockVotingProof { @@ -428,6 +441,13 @@ pub trait AncestryHelper { /// The data needed for validating the proof. type ValidationContext; + /// Generates a proof that the `prev_block_number` is part of the canonical chain at + /// `best_known_block_number`. + fn generate_proof( + prev_block_number: Header::Number, + best_known_block_number: Option, + ) -> Option; + /// Extract the validation context from the provided header. fn extract_validation_context(header: Header) -> Option; @@ -450,7 +470,7 @@ pub type OpaqueKeyOwnershipProof = OpaqueValue; sp_api::decl_runtime_apis! { /// API necessary for BEEFY voters. - #[api_version(4)] + #[api_version(5)] pub trait BeefyApi where AuthorityId : Codec + RuntimeAppPublic, { @@ -474,6 +494,34 @@ sp_api::decl_runtime_apis! { key_owner_proof: OpaqueKeyOwnershipProof, ) -> Option<()>; + /// Submits an unsigned extrinsic to report a fork voting equivocation. The caller + /// must provide the fork voting proof (the ancestry proof should be obtained using + /// `generate_ancestry_proof`) and a key ownership proof (should be obtained using + /// `generate_key_ownership_proof`). The extrinsic will be unsigned and should only + /// be accepted for local authorship (not to be broadcast to the network). This method + /// returns `None` when creation of the extrinsic fails, e.g. if equivocation + /// reporting is disabled for the given runtime (i.e. this method is + /// hardcoded to return `None`). Only useful in an offchain context. + fn submit_report_fork_voting_unsigned_extrinsic( + equivocation_proof: + ForkVotingProof, + key_owner_proof: OpaqueKeyOwnershipProof, + ) -> Option<()>; + + /// Submits an unsigned extrinsic to report a future block voting equivocation. The caller + /// must provide the future block voting proof and a key ownership proof + /// (should be obtained using `generate_key_ownership_proof`). + /// The extrinsic will be unsigned and should only be accepted for local + /// authorship (not to be broadcast to the network). This method returns + /// `None` when creation of the extrinsic fails, e.g. if equivocation + /// reporting is disabled for the given runtime (i.e. this method is + /// hardcoded to return `None`). Only useful in an offchain context. + fn submit_report_future_block_voting_unsigned_extrinsic( + equivocation_proof: + FutureBlockVotingProof, AuthorityId>, + key_owner_proof: OpaqueKeyOwnershipProof, + ) -> Option<()>; + /// Generates a proof of key ownership for the given authority in the /// given set. An example usage of this module is coupled with the /// session historical module to prove that a given authority key is @@ -489,6 +537,13 @@ sp_api::decl_runtime_apis! { set_id: ValidatorSetId, authority_id: AuthorityId, ) -> Option; + + /// Generates a proof that the `prev_block_number` is part of the canonical chain at + /// `best_known_block_number`. + fn generate_ancestry_proof( + prev_block_number: NumberFor, + best_known_block_number: Option>, + ) -> Option; } }