Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c4efa5f
client/beefy: don't accept vote for older rounds
acatangiu Jul 8, 2022
934f14c
client/beefy: clean up and reorg the worker struct
acatangiu Jul 7, 2022
2f1f221
client/beefy: first step towards Full BEEFY
acatangiu Jul 11, 2022
d732f82
client/beefy: sketch idea for block import and sync
acatangiu Jul 12, 2022
b70b9be
client/beefy: add BEEFY block import
acatangiu Jul 12, 2022
1d0298c
client/beefy: process justifications from block import
acatangiu Jul 13, 2022
6d381db
client/beefy: add TODOs for sync protocol
acatangiu Jul 13, 2022
739898a
client/beefy: add more docs and comments
acatangiu Jul 13, 2022
abfd074
client/beefy-rpc: fix RPC error
acatangiu Jul 13, 2022
46de909
client/beefy: verify justification validity on block import
acatangiu Jul 14, 2022
efe0157
client/beefy: more tests
acatangiu Jul 14, 2022
e129702
client/beefy: small fixes
acatangiu Jul 15, 2022
0540091
client/beefy: remove invalid justifications at block import
acatangiu Jul 15, 2022
2c16427
todo: beefy block import tests
acatangiu Jul 18, 2022
8256fb0
RFC: ideas for multiple justifications per block
acatangiu Jul 18, 2022
d5b53ae
Revert "RFC: ideas for multiple justifications per block"
acatangiu Jul 19, 2022
bc6a187
client/beefy: append justif to backend on block import
acatangiu Jul 19, 2022
73355d0
client/beefy: groundwork for block import test
acatangiu Jul 18, 2022
d5f9f1a
client/beefy: groundwork2 for block import test
acatangiu Jul 19, 2022
ff02b1a
client/beefy: groundwork3 for block import test
acatangiu Jul 19, 2022
1c49f49
client/beefy: add block import test
acatangiu Jul 19, 2022
9761f89
Merge branch 'master' of github.com:paritytech/substrate into beefy-i…
acatangiu Jul 20, 2022
ecf625b
client/beefy: add required trait bounds to block import builder
acatangiu Jul 20, 2022
0d843e0
remove client from beefy block import, backend gets the job done
acatangiu Jul 29, 2022
581b5ab
Merge branch 'master' of github.com:paritytech/substrate into beefy-i…
acatangiu Jul 29, 2022
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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion client/beefy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ description = "BEEFY Client gadget for substrate"
homepage = "https://substrate.io"

[dependencies]
async-trait = "0.1.50"
codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] }
fnv = "1.0.6"
futures = "0.3"
Expand All @@ -22,6 +23,7 @@ beefy-primitives = { version = "4.0.0-dev", path = "../../primitives/beefy" }
prometheus = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" }
sc-chain-spec = { version = "4.0.0-dev", path = "../../client/chain-spec" }
sc-client-api = { version = "4.0.0-dev", path = "../api" }
sc-consensus = { version = "0.10.0-dev", path = "../consensus/common" }
sc-finality-grandpa = { version = "0.10.0-dev", path = "../../client/finality-grandpa" }
sc-keystore = { version = "4.0.0-dev", path = "../keystore" }
sc-network = { version = "0.10.0-dev", path = "../network" }
Expand All @@ -42,7 +44,7 @@ serde = "1.0.136"
strum = { version = "0.24.1", features = ["derive"] }
tempfile = "3.1.0"
tokio = "1.17.0"
sc-consensus = { version = "0.10.0-dev", path = "../consensus/common" }
sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" }
sc-network-test = { version = "0.8.0", path = "../network/test" }
sp-finality-grandpa = { version = "4.0.0-dev", path = "../../primitives/finality-grandpa" }
sp-keyring = { version = "6.0.0", path = "../../primitives/keyring" }
Expand Down
5 changes: 3 additions & 2 deletions client/beefy/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ where
mod tests {
use super::*;

use beefy_gadget::notification::{
BeefyBestBlockStream, BeefySignedCommitment, BeefySignedCommitmentSender,
use beefy_gadget::{
justification::BeefySignedCommitment,
notification::{BeefyBestBlockStream, BeefySignedCommitmentSender},
};
use beefy_primitives::{known_payload_ids, Payload};
use codec::{Decode, Encode};
Expand Down
2 changes: 1 addition & 1 deletion client/beefy/rpc/src/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub struct EncodedSignedCommitment(sp_core::Bytes);

impl EncodedSignedCommitment {
pub fn new<Block>(
signed_commitment: beefy_gadget::notification::BeefySignedCommitment<Block>,
signed_commitment: beefy_gadget::justification::BeefySignedCommitment<Block>,
) -> Self
where
Block: BlockT,
Expand Down
4 changes: 4 additions & 0 deletions client/beefy/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ use std::fmt::Debug;

#[derive(Debug, thiserror::Error, PartialEq)]
pub enum Error {
#[error("Backend: {0}")]
Backend(String),
#[error("Keystore error: {0}")]
Keystore(String),
#[error("Signature error: {0}")]
Signature(String),
#[error("Session uninitialized")]
UninitSession,
}
205 changes: 205 additions & 0 deletions client/beefy/src/import.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// This file is part of Substrate.

// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use beefy_primitives::{crypto::Signature, BeefyApi, VersionedFinalityProof, BEEFY_ENGINE_ID};
use codec::Encode;
use log::error;
use std::{collections::HashMap, sync::Arc};

use sp_api::{ProvideRuntimeApi, TransactionFor};
use sp_blockchain::{well_known_cache_keys, HeaderBackend};
use sp_consensus::Error as ConsensusError;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT, NumberFor},
EncodedJustification,
};

use sc_client_api::backend::Backend;
use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult};

use crate::{
justification::decode_and_verify_commitment, notification::BeefySignedCommitmentSender,
};

/// A block-import handler for BEEFY.
///
/// This scans each imported block for BEEFY justifications and verifies them.
/// Wraps a `inner: BlockImport` and ultimately defers to it.
///
/// When using BEEFY, the block import worker should be using this block import object.
pub struct BeefyBlockImport<Block: BlockT, Backend, RuntimeApi, I> {
backend: Arc<Backend>,
runtime: Arc<RuntimeApi>,
inner: I,
justification_sender: BeefySignedCommitmentSender<Block>,
}

impl<Block: BlockT, BE, Runtime, I: Clone> Clone for BeefyBlockImport<Block, BE, Runtime, I> {
fn clone(&self) -> Self {
BeefyBlockImport {
backend: self.backend.clone(),
runtime: self.runtime.clone(),
inner: self.inner.clone(),
justification_sender: self.justification_sender.clone(),
}
}
}

impl<Block: BlockT, BE, Runtime, I> BeefyBlockImport<Block, BE, Runtime, I> {
/// Create a new BeefyBlockImport.
pub fn new(
backend: Arc<BE>,
runtime: Arc<Runtime>,
inner: I,
justification_sender: BeefySignedCommitmentSender<Block>,
) -> BeefyBlockImport<Block, BE, Runtime, I> {
BeefyBlockImport { backend, runtime, inner, justification_sender }
}
}

impl<Block, BE, Runtime, I> BeefyBlockImport<Block, BE, Runtime, I>
where
Block: BlockT,
BE: Backend<Block>,
Runtime: ProvideRuntimeApi<Block>,
Runtime::Api: BeefyApi<Block> + Send + Sync,
{
fn decode_and_verify(
&self,
encoded: &EncodedJustification,
number: NumberFor<Block>,
hash: <Block as BlockT>::Hash,
) -> Result<VersionedFinalityProof<NumberFor<Block>, Signature>, ConsensusError> {
let block_id = BlockId::hash(hash);
let validator_set = self
.runtime
.runtime_api()
.validator_set(&block_id)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
.ok_or_else(|| ConsensusError::ClientImport("Unknown validator set".to_string()))?;

decode_and_verify_commitment::<Block>(&encoded[..], number, &validator_set)
}

/// Import BEEFY justification: Send it to worker for processing and also append it to backend.
///
/// This function assumes:
/// - `justification` is verified and valid,
/// - the block referred by `justification` has been imported _and_ finalized.
fn import_beefy_justification_unchecked(
&self,
number: NumberFor<Block>,
justification: VersionedFinalityProof<NumberFor<Block>, Signature>,
) {
// Append the justification to the block in the backend.
if let Err(e) = self.backend.append_justification(
BlockId::Number(number),
(BEEFY_ENGINE_ID, justification.encode()),
) {
error!(target: "beefy", "🥩 Error {:?} on appending justification: {:?}", e, justification);
}
// Send the justification to the BEEFY voter for processing.
match justification {
// TODO #11838: Should not unpack, these channels should also use
// `VersionedFinalityProof`.
VersionedFinalityProof::V1(signed_commitment) => self
.justification_sender
.notify(|| Ok::<_, ()>(signed_commitment))
.expect("forwards closure result; the closure always returns Ok; qed."),
};
}
}

#[async_trait::async_trait]
impl<Block, BE, Runtime, I> BlockImport<Block> for BeefyBlockImport<Block, BE, Runtime, I>
where
Block: BlockT,
BE: Backend<Block>,
I: BlockImport<
Block,
Error = ConsensusError,
Transaction = sp_api::TransactionFor<Runtime, Block>,
> + Send
+ Sync,
Runtime: ProvideRuntimeApi<Block> + Send + Sync,
Runtime::Api: BeefyApi<Block>,
{
type Error = ConsensusError;
type Transaction = TransactionFor<Runtime, Block>;

async fn import_block(
&mut self,
mut block: BlockImportParams<Block, Self::Transaction>,
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
let hash = block.post_hash();
let number = *block.header.number();

let beefy_proof = block
.justifications
.as_mut()
.and_then(|just| {
let decoded = just
.get(BEEFY_ENGINE_ID)
.map(|encoded| self.decode_and_verify(encoded, number, hash));
// Remove BEEFY justification from the list before giving to `inner`;
// we will append it to backend ourselves at the end if all goes well.
just.remove(BEEFY_ENGINE_ID);
decoded
})
.transpose()
.unwrap_or(None);

// Run inner block import.
let inner_import_result = self.inner.import_block(block, new_cache).await?;

match (beefy_proof, &inner_import_result) {
(Some(proof), ImportResult::Imported(_)) => {
let status = self.backend.blockchain().info();
if number <= status.finalized_number &&
Some(hash) ==
self.backend
.blockchain()
.hash(number)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
{
// The proof is valid and the block is imported and final, we can import.
self.import_beefy_justification_unchecked(number, proof);
} else {
error!(
target: "beefy",
"🥩 Cannot import justification: {:?} for, not yet final, block number {:?}",
proof,
number,
);
}
},
_ => (),
}

Ok(inner_import_result)
}

async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
self.inner.check_block(block).await
}
}
Loading