Beefy: use VersionedFinalityProof instead of SignedCommitment (#11962)

* beefy: use VersionedFinalityProof instead of SignedCommitment.

* Change the exposed RPC API to support versioned proofs.

Co-authored-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
ZhiYong
2022-08-04 15:47:52 +08:00
committed by GitHub
parent 97b2a5ee75
commit b8ad45373f
8 changed files with 157 additions and 140 deletions
+26 -25
View File
@@ -35,7 +35,7 @@ use jsonrpsee::{
};
use log::warn;
use beefy_gadget::notification::{BeefyBestBlockStream, BeefySignedCommitmentStream};
use beefy_gadget::notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream};
mod notification;
@@ -101,7 +101,7 @@ pub trait BeefyApi<Notification, Hash> {
/// Implements the BeefyApi RPC trait for interacting with BEEFY.
pub struct Beefy<Block: BlockT> {
signed_commitment_stream: BeefySignedCommitmentStream<Block>,
finality_proof_stream: BeefyVersionedFinalityProofStream<Block>,
beefy_best_block: Arc<RwLock<Option<Block::Hash>>>,
executor: SubscriptionTaskExecutor,
}
@@ -112,7 +112,7 @@ where
{
/// Creates a new Beefy Rpc handler instance.
pub fn new(
signed_commitment_stream: BeefySignedCommitmentStream<Block>,
finality_proof_stream: BeefyVersionedFinalityProofStream<Block>,
best_block_stream: BeefyBestBlockStream<Block>,
executor: SubscriptionTaskExecutor,
) -> Result<Self, Error> {
@@ -126,20 +126,21 @@ where
});
executor.spawn("substrate-rpc-subscription", Some("rpc"), future.map(drop).boxed());
Ok(Self { signed_commitment_stream, beefy_best_block, executor })
Ok(Self { finality_proof_stream, beefy_best_block, executor })
}
}
#[async_trait]
impl<Block> BeefyApiServer<notification::EncodedSignedCommitment, Block::Hash> for Beefy<Block>
impl<Block> BeefyApiServer<notification::EncodedVersionedFinalityProof, Block::Hash>
for Beefy<Block>
where
Block: BlockT,
{
fn subscribe_justifications(&self, mut sink: SubscriptionSink) -> SubscriptionResult {
let stream = self
.signed_commitment_stream
.finality_proof_stream
.subscribe()
.map(|sc| notification::EncodedSignedCommitment::new::<Block>(sc));
.map(|vfp| notification::EncodedVersionedFinalityProof::new::<Block>(vfp));
let fut = async move {
sink.pipe_from_stream(stream).await;
@@ -164,31 +165,31 @@ mod tests {
use super::*;
use beefy_gadget::{
justification::BeefySignedCommitment,
notification::{BeefyBestBlockStream, BeefySignedCommitmentSender},
justification::BeefyVersionedFinalityProof,
notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofSender},
};
use beefy_primitives::{known_payload_ids, Payload};
use beefy_primitives::{known_payload_ids, Payload, SignedCommitment};
use codec::{Decode, Encode};
use jsonrpsee::{types::EmptyParams, RpcModule};
use sp_runtime::traits::{BlakeTwo256, Hash};
use substrate_test_runtime_client::runtime::Block;
fn setup_io_handler() -> (RpcModule<Beefy<Block>>, BeefySignedCommitmentSender<Block>) {
fn setup_io_handler() -> (RpcModule<Beefy<Block>>, BeefyVersionedFinalityProofSender<Block>) {
let (_, stream) = BeefyBestBlockStream::<Block>::channel();
setup_io_handler_with_best_block_stream(stream)
}
fn setup_io_handler_with_best_block_stream(
best_block_stream: BeefyBestBlockStream<Block>,
) -> (RpcModule<Beefy<Block>>, BeefySignedCommitmentSender<Block>) {
let (commitment_sender, commitment_stream) =
BeefySignedCommitmentStream::<Block>::channel();
) -> (RpcModule<Beefy<Block>>, BeefyVersionedFinalityProofSender<Block>) {
let (finality_proof_sender, finality_proof_stream) =
BeefyVersionedFinalityProofStream::<Block>::channel();
let handler =
Beefy::new(commitment_stream, best_block_stream, sc_rpc::testing::test_executor())
Beefy::new(finality_proof_stream, best_block_stream, sc_rpc::testing::test_executor())
.expect("Setting up the BEEFY RPC handler works");
(handler.into_rpc(), commitment_sender)
(handler.into_rpc(), finality_proof_sender)
}
#[tokio::test]
@@ -262,21 +263,21 @@ mod tests {
assert_eq!(response.result, expected);
}
fn create_commitment() -> BeefySignedCommitment<Block> {
fn create_finality_proof() -> BeefyVersionedFinalityProof<Block> {
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, "Hello World!".encode());
BeefySignedCommitment::<Block> {
BeefyVersionedFinalityProof::<Block>::V1(SignedCommitment {
commitment: beefy_primitives::Commitment {
payload,
block_number: 5,
validator_set_id: 0,
},
signatures: vec![],
}
})
}
#[tokio::test]
async fn subscribe_and_listen_to_one_justification() {
let (rpc, commitment_sender) = setup_io_handler();
let (rpc, finality_proof_sender) = setup_io_handler();
// Subscribe
let mut sub = rpc
@@ -284,16 +285,16 @@ mod tests {
.await
.unwrap();
// Notify with commitment
let commitment = create_commitment();
let r: Result<(), ()> = commitment_sender.notify(|| Ok(commitment.clone()));
// Notify with finality_proof
let finality_proof = create_finality_proof();
let r: Result<(), ()> = finality_proof_sender.notify(|| Ok(finality_proof.clone()));
r.unwrap();
// Inspect what we received
let (bytes, recv_sub_id) = sub.next::<sp_core::Bytes>().await.unwrap().unwrap();
let recv_commitment: BeefySignedCommitment<Block> =
let recv_finality_proof: BeefyVersionedFinalityProof<Block> =
Decode::decode(&mut &bytes[..]).unwrap();
assert_eq!(&recv_sub_id, sub.subscription_id());
assert_eq!(recv_commitment, commitment);
assert_eq!(recv_finality_proof, finality_proof);
}
}
@@ -21,19 +21,19 @@ use serde::{Deserialize, Serialize};
use sp_runtime::traits::Block as BlockT;
/// An encoded signed commitment proving that the given header has been finalized.
/// An encoded finality proof proving that the given header has been finalized.
/// The given bytes should be the SCALE-encoded representation of a
/// `beefy_primitives::SignedCommitment`.
/// `beefy_primitives::VersionedFinalityProof`.
#[derive(Clone, Serialize, Deserialize)]
pub struct EncodedSignedCommitment(sp_core::Bytes);
pub struct EncodedVersionedFinalityProof(sp_core::Bytes);
impl EncodedSignedCommitment {
impl EncodedVersionedFinalityProof {
pub fn new<Block>(
signed_commitment: beefy_gadget::justification::BeefySignedCommitment<Block>,
finality_proof: beefy_gadget::justification::BeefyVersionedFinalityProof<Block>,
) -> Self
where
Block: BlockT,
{
EncodedSignedCommitment(signed_commitment.encode().into())
EncodedVersionedFinalityProof(finality_proof.encode().into())
}
}
+11 -15
View File
@@ -16,7 +16,7 @@
// 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 beefy_primitives::{BeefyApi, BEEFY_ENGINE_ID};
use codec::Encode;
use log::error;
use std::{collections::HashMap, sync::Arc};
@@ -34,7 +34,8 @@ use sc_client_api::backend::Backend;
use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult};
use crate::{
justification::decode_and_verify_commitment, notification::BeefySignedCommitmentSender,
justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof},
notification::BeefyVersionedFinalityProofSender,
};
/// A block-import handler for BEEFY.
@@ -47,7 +48,7 @@ pub struct BeefyBlockImport<Block: BlockT, Backend, RuntimeApi, I> {
backend: Arc<Backend>,
runtime: Arc<RuntimeApi>,
inner: I,
justification_sender: BeefySignedCommitmentSender<Block>,
justification_sender: BeefyVersionedFinalityProofSender<Block>,
}
impl<Block: BlockT, BE, Runtime, I: Clone> Clone for BeefyBlockImport<Block, BE, Runtime, I> {
@@ -67,7 +68,7 @@ impl<Block: BlockT, BE, Runtime, I> BeefyBlockImport<Block, BE, Runtime, I> {
backend: Arc<BE>,
runtime: Arc<Runtime>,
inner: I,
justification_sender: BeefySignedCommitmentSender<Block>,
justification_sender: BeefyVersionedFinalityProofSender<Block>,
) -> BeefyBlockImport<Block, BE, Runtime, I> {
BeefyBlockImport { backend, runtime, inner, justification_sender }
}
@@ -85,7 +86,7 @@ where
encoded: &EncodedJustification,
number: NumberFor<Block>,
hash: <Block as BlockT>::Hash,
) -> Result<VersionedFinalityProof<NumberFor<Block>, Signature>, ConsensusError> {
) -> Result<BeefyVersionedFinalityProof<Block>, ConsensusError> {
let block_id = BlockId::hash(hash);
let validator_set = self
.runtime
@@ -94,7 +95,7 @@ where
.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)
decode_and_verify_finality_proof::<Block>(&encoded[..], number, &validator_set)
}
/// Import BEEFY justification: Send it to worker for processing and also append it to backend.
@@ -105,7 +106,7 @@ where
fn import_beefy_justification_unchecked(
&self,
number: NumberFor<Block>,
justification: VersionedFinalityProof<NumberFor<Block>, Signature>,
justification: BeefyVersionedFinalityProof<Block>,
) {
// Append the justification to the block in the backend.
if let Err(e) = self.backend.append_justification(
@@ -115,14 +116,9 @@ where
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."),
};
self.justification_sender
.notify(|| Ok::<_, ()>(justification))
.expect("forwards closure result; the closure always returns Ok; qed.");
}
}
+32 -21
View File
@@ -25,17 +25,17 @@ use codec::{Decode, Encode};
use sp_consensus::Error as ConsensusError;
use sp_runtime::traits::{Block as BlockT, NumberFor};
/// A commitment with matching BEEFY authorities' signatures.
pub type BeefySignedCommitment<Block> =
beefy_primitives::SignedCommitment<NumberFor<Block>, beefy_primitives::crypto::Signature>;
/// A finality proof with matching BEEFY authorities' signatures.
pub type BeefyVersionedFinalityProof<Block> =
beefy_primitives::VersionedFinalityProof<NumberFor<Block>, Signature>;
/// Decode and verify a Beefy SignedCommitment.
pub(crate) fn decode_and_verify_commitment<Block: BlockT>(
/// Decode and verify a Beefy FinalityProof.
pub(crate) fn decode_and_verify_finality_proof<Block: BlockT>(
encoded: &[u8],
target_number: NumberFor<Block>,
validator_set: &ValidatorSet<AuthorityId>,
) -> Result<VersionedFinalityProof<NumberFor<Block>, Signature>, ConsensusError> {
let proof = <VersionedFinalityProof<NumberFor<Block>, Signature>>::decode(&mut &*encoded)
) -> Result<BeefyVersionedFinalityProof<Block>, ConsensusError> {
let proof = <BeefyVersionedFinalityProof<Block>>::decode(&mut &*encoded)
.map_err(|_| ConsensusError::InvalidJustification)?;
verify_with_validator_set::<Block>(target_number, validator_set, &proof).map(|_| proof)
}
@@ -44,7 +44,7 @@ pub(crate) fn decode_and_verify_commitment<Block: BlockT>(
fn verify_with_validator_set<Block: BlockT>(
target_number: NumberFor<Block>,
validator_set: &ValidatorSet<AuthorityId>,
proof: &VersionedFinalityProof<NumberFor<Block>, Signature>,
proof: &BeefyVersionedFinalityProof<Block>,
) -> Result<(), ConsensusError> {
match proof {
VersionedFinalityProof::V1(signed_commitment) => {
@@ -80,17 +80,19 @@ fn verify_with_validator_set<Block: BlockT>(
#[cfg(test)]
pub(crate) mod tests {
use beefy_primitives::{known_payload_ids, Commitment, Payload, SignedCommitment};
use beefy_primitives::{
known_payload_ids, Commitment, Payload, SignedCommitment, VersionedFinalityProof,
};
use substrate_test_runtime_client::runtime::Block;
use super::*;
use crate::{keystore::tests::Keyring, tests::make_beefy_ids};
pub(crate) fn new_signed_commitment(
pub(crate) fn new_finality_proof(
block_num: NumberFor<Block>,
validator_set: &ValidatorSet<AuthorityId>,
keys: &[Keyring],
) -> BeefySignedCommitment<Block> {
) -> BeefyVersionedFinalityProof<Block> {
let commitment = Commitment {
payload: Payload::new(known_payload_ids::MMR_ROOT_ID, vec![]),
block_number: block_num,
@@ -98,7 +100,7 @@ pub(crate) mod tests {
};
let message = commitment.encode();
let signatures = keys.iter().map(|key| Some(key.sign(&message))).collect();
SignedCommitment { commitment, signatures }
VersionedFinalityProof::V1(SignedCommitment { commitment, signatures })
}
#[test]
@@ -108,7 +110,7 @@ pub(crate) mod tests {
// build valid justification
let block_num = 42;
let proof = new_signed_commitment(block_num, &validator_set, keys);
let proof = new_finality_proof(block_num, &validator_set, keys);
let good_proof = proof.clone().into();
// should verify successfully
@@ -132,7 +134,10 @@ pub(crate) mod tests {
// wrong signatures length -> should fail verification
let mut bad_proof = proof.clone();
// change length of signatures
bad_proof.signatures.pop().flatten().unwrap();
let bad_signed_commitment = match bad_proof {
VersionedFinalityProof::V1(ref mut sc) => sc,
};
bad_signed_commitment.signatures.pop().flatten().unwrap();
match verify_with_validator_set::<Block>(block_num + 1, &validator_set, &bad_proof.into()) {
Err(ConsensusError::InvalidJustification) => (),
_ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"),
@@ -140,8 +145,11 @@ pub(crate) mod tests {
// not enough signatures -> should fail verification
let mut bad_proof = proof.clone();
let bad_signed_commitment = match bad_proof {
VersionedFinalityProof::V1(ref mut sc) => sc,
};
// remove a signature (but same length)
*bad_proof.signatures.first_mut().unwrap() = None;
*bad_signed_commitment.signatures.first_mut().unwrap() = None;
match verify_with_validator_set::<Block>(block_num + 1, &validator_set, &bad_proof.into()) {
Err(ConsensusError::InvalidJustification) => (),
_ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"),
@@ -149,9 +157,12 @@ pub(crate) mod tests {
// not enough _correct_ signatures -> should fail verification
let mut bad_proof = proof.clone();
let bad_signed_commitment = match bad_proof {
VersionedFinalityProof::V1(ref mut sc) => sc,
};
// change a signature to a different key
*bad_proof.signatures.first_mut().unwrap() =
Some(Keyring::Dave.sign(&proof.commitment.encode()));
*bad_signed_commitment.signatures.first_mut().unwrap() =
Some(Keyring::Dave.sign(&bad_signed_commitment.commitment.encode()));
match verify_with_validator_set::<Block>(block_num + 1, &validator_set, &bad_proof.into()) {
Err(ConsensusError::InvalidJustification) => (),
_ => assert!(false, "Expected Err(ConsensusError::InvalidJustification)"),
@@ -159,19 +170,19 @@ pub(crate) mod tests {
}
#[test]
fn should_decode_and_verify_commitment() {
fn should_decode_and_verify_finality_proof() {
let keys = &[Keyring::Alice, Keyring::Bob];
let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap();
let block_num = 1;
// build valid justification
let proof = new_signed_commitment(block_num, &validator_set, keys);
let versioned_proof: VersionedFinalityProof<NumberFor<Block>, Signature> = proof.into();
let proof = new_finality_proof(block_num, &validator_set, keys);
let versioned_proof: BeefyVersionedFinalityProof<Block> = proof.into();
let encoded = versioned_proof.encode();
// should successfully decode and verify
let verified =
decode_and_verify_commitment::<Block>(&encoded, block_num, &validator_set).unwrap();
decode_and_verify_finality_proof::<Block>(&encoded, block_num, &validator_set).unwrap();
assert_eq!(verified, versioned_proof);
}
}
+7 -7
View File
@@ -46,8 +46,8 @@ mod tests;
use crate::{
import::BeefyBlockImport,
notification::{
BeefyBestBlockSender, BeefyBestBlockStream, BeefySignedCommitmentSender,
BeefySignedCommitmentStream,
BeefyBestBlockSender, BeefyBestBlockStream, BeefyVersionedFinalityProofSender,
BeefyVersionedFinalityProofStream,
},
};
@@ -121,11 +121,11 @@ where
pub struct BeefyVoterLinks<B: Block> {
// BlockImport -> Voter links
/// Stream of BEEFY signed commitments from block import to voter.
pub from_block_import_justif_stream: BeefySignedCommitmentStream<B>,
pub from_block_import_justif_stream: BeefyVersionedFinalityProofStream<B>,
// Voter -> RPC links
/// Sends BEEFY signed commitments from voter to RPC.
pub to_rpc_justif_sender: BeefySignedCommitmentSender<B>,
pub to_rpc_justif_sender: BeefyVersionedFinalityProofSender<B>,
/// Sends BEEFY best block hashes from voter to RPC.
pub to_rpc_best_block_sender: BeefyBestBlockSender<B>,
}
@@ -134,7 +134,7 @@ pub struct BeefyVoterLinks<B: Block> {
#[derive(Clone)]
pub struct BeefyRPCLinks<B: Block> {
/// Stream of signed commitments coming from the voter.
pub from_voter_justif_stream: BeefySignedCommitmentStream<B>,
pub from_voter_justif_stream: BeefyVersionedFinalityProofStream<B>,
/// Stream of BEEFY best block hashes coming from the voter.
pub from_voter_best_beefy_stream: BeefyBestBlockStream<B>,
}
@@ -156,13 +156,13 @@ where
{
// Voter -> RPC links
let (to_rpc_justif_sender, from_voter_justif_stream) =
notification::BeefySignedCommitmentStream::<B>::channel();
notification::BeefyVersionedFinalityProofStream::<B>::channel();
let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
notification::BeefyBestBlockStream::<B>::channel();
// BlockImport -> Voter links
let (to_voter_justif_sender, from_block_import_justif_stream) =
notification::BeefySignedCommitmentStream::<B>::channel();
notification::BeefyVersionedFinalityProofStream::<B>::channel();
// BlockImport
let import =
+11 -10
View File
@@ -19,7 +19,7 @@
use sc_utils::notification::{NotificationSender, NotificationStream, TracingKeyStr};
use sp_runtime::traits::Block as BlockT;
use crate::justification::BeefySignedCommitment;
use crate::justification::BeefyVersionedFinalityProof;
/// The sending half of the notifications channel(s) used to send
/// notifications about best BEEFY block from the gadget side.
@@ -31,13 +31,14 @@ pub type BeefyBestBlockStream<Block> =
NotificationStream<<Block as BlockT>::Hash, BeefyBestBlockTracingKey>;
/// The sending half of the notifications channel(s) used to send notifications
/// about signed commitments generated at the end of a BEEFY round.
pub type BeefySignedCommitmentSender<Block> = NotificationSender<BeefySignedCommitment<Block>>;
/// about versioned finality proof generated at the end of a BEEFY round.
pub type BeefyVersionedFinalityProofSender<Block> =
NotificationSender<BeefyVersionedFinalityProof<Block>>;
/// The receiving half of a notifications channel used to receive notifications
/// about signed commitments generated at the end of a BEEFY round.
pub type BeefySignedCommitmentStream<Block> =
NotificationStream<BeefySignedCommitment<Block>, BeefySignedCommitmentTracingKey>;
/// about versioned finality proof generated at the end of a BEEFY round.
pub type BeefyVersionedFinalityProofStream<Block> =
NotificationStream<BeefyVersionedFinalityProof<Block>, BeefyVersionedFinalityProofTracingKey>;
/// Provides tracing key for BEEFY best block stream.
#[derive(Clone)]
@@ -46,9 +47,9 @@ impl TracingKeyStr for BeefyBestBlockTracingKey {
const TRACING_KEY: &'static str = "mpsc_beefy_best_block_notification_stream";
}
/// Provides tracing key for BEEFY signed commitments stream.
/// Provides tracing key for BEEFY versioned finality proof stream.
#[derive(Clone)]
pub struct BeefySignedCommitmentTracingKey;
impl TracingKeyStr for BeefySignedCommitmentTracingKey {
const TRACING_KEY: &'static str = "mpsc_beefy_signed_commitments_notification_stream";
pub struct BeefyVersionedFinalityProofTracingKey;
impl TracingKeyStr for BeefyVersionedFinalityProofTracingKey {
const TRACING_KEY: &'static str = "mpsc_beefy_versioned_finality_proof_notification_stream";
}
+27 -23
View File
@@ -397,17 +397,18 @@ fn run_for(duration: Duration, net: &Arc<Mutex<BeefyTestNet>>, runtime: &mut Run
pub(crate) fn get_beefy_streams(
net: &mut BeefyTestNet,
peers: &[BeefyKeyring],
) -> (Vec<NotificationReceiver<H256>>, Vec<NotificationReceiver<BeefySignedCommitment<Block>>>) {
) -> (Vec<NotificationReceiver<H256>>, Vec<NotificationReceiver<BeefyVersionedFinalityProof<Block>>>)
{
let mut best_block_streams = Vec::new();
let mut signed_commitment_streams = Vec::new();
let mut versioned_finality_proof_streams = Vec::new();
for peer_id in 0..peers.len() {
let beefy_rpc_links = net.peer(peer_id).data.beefy_rpc_links.lock().clone().unwrap();
let BeefyRPCLinks { from_voter_justif_stream, from_voter_best_beefy_stream } =
beefy_rpc_links;
best_block_streams.push(from_voter_best_beefy_stream.subscribe());
signed_commitment_streams.push(from_voter_justif_stream.subscribe());
versioned_finality_proof_streams.push(from_voter_justif_stream.subscribe());
}
(best_block_streams, signed_commitment_streams)
(best_block_streams, versioned_finality_proof_streams)
}
fn wait_for_best_beefy_blocks(
@@ -437,7 +438,7 @@ fn wait_for_best_beefy_blocks(
}
fn wait_for_beefy_signed_commitments(
streams: Vec<NotificationReceiver<BeefySignedCommitment<Block>>>,
streams: Vec<NotificationReceiver<BeefyVersionedFinalityProof<Block>>>,
net: &Arc<Mutex<BeefyTestNet>>,
runtime: &mut Runtime,
expected_commitment_block_nums: &[u64],
@@ -446,9 +447,12 @@ fn wait_for_beefy_signed_commitments(
let len = expected_commitment_block_nums.len();
streams.into_iter().for_each(|stream| {
let mut expected = expected_commitment_block_nums.iter();
wait_for.push(Box::pin(stream.take(len).for_each(move |signed_commitment| {
wait_for.push(Box::pin(stream.take(len).for_each(move |versioned_finality_proof| {
let expected = expected.next();
async move {
let signed_commitment = match versioned_finality_proof {
beefy_primitives::VersionedFinalityProof::V1(sc) => sc,
};
let commitment_block_num = signed_commitment.commitment.block_number;
assert_eq!(expected, Some(commitment_block_num).as_ref());
// TODO: also verify commitment payload, validator set id, and signatures.
@@ -486,7 +490,7 @@ fn finalize_block_and_wait_for_beefy(
finalize_targets: &[u64],
expected_beefy: &[u64],
) {
let (best_blocks, signed_commitments) = get_beefy_streams(&mut net.lock(), peers);
let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers);
for block in finalize_targets {
let finalize = BlockId::number(*block);
@@ -499,11 +503,11 @@ fn finalize_block_and_wait_for_beefy(
// run for quarter second then verify no new best beefy block available
let timeout = Some(Duration::from_millis(250));
streams_empty_after_timeout(best_blocks, &net, runtime, timeout);
streams_empty_after_timeout(signed_commitments, &net, runtime, None);
streams_empty_after_timeout(versioned_finality_proof, &net, runtime, None);
} else {
// run until expected beefy blocks are received
wait_for_best_beefy_blocks(best_blocks, &net, runtime, expected_beefy);
wait_for_beefy_signed_commitments(signed_commitments, &net, runtime, expected_beefy);
wait_for_beefy_signed_commitments(versioned_finality_proof, &net, runtime, expected_beefy);
}
}
@@ -574,19 +578,19 @@ fn lagging_validators() {
// Alice finalizes #25, Bob lags behind
let finalize = BlockId::number(25);
let (best_blocks, signed_commitments) = get_beefy_streams(&mut net.lock(), peers);
let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers);
net.lock().peer(0).client().as_client().finalize_block(finalize, None).unwrap();
// verify nothing gets finalized by BEEFY
let timeout = Some(Duration::from_millis(250));
streams_empty_after_timeout(best_blocks, &net, &mut runtime, timeout);
streams_empty_after_timeout(signed_commitments, &net, &mut runtime, None);
streams_empty_after_timeout(versioned_finality_proof, &net, &mut runtime, None);
// Bob catches up and also finalizes #25
let (best_blocks, signed_commitments) = get_beefy_streams(&mut net.lock(), peers);
let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers);
net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap();
// expected beefy finalizes block #17 from diff-power-of-two
wait_for_best_beefy_blocks(best_blocks, &net, &mut runtime, &[23, 24, 25]);
wait_for_beefy_signed_commitments(signed_commitments, &net, &mut runtime, &[23, 24, 25]);
wait_for_beefy_signed_commitments(versioned_finality_proof, &net, &mut runtime, &[23, 24, 25]);
// Both finalize #30 (mandatory session) and #32 -> BEEFY finalize #30 (mandatory), #31, #32
finalize_block_and_wait_for_beefy(&net, peers, &mut runtime, &[30, 32], &[30, 31, 32]);
@@ -596,20 +600,20 @@ fn lagging_validators() {
// validator set).
// Alice finalizes session-boundary mandatory block #60, Bob lags behind
let (best_blocks, signed_commitments) = get_beefy_streams(&mut net.lock(), peers);
let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers);
let finalize = BlockId::number(60);
net.lock().peer(0).client().as_client().finalize_block(finalize, None).unwrap();
// verify nothing gets finalized by BEEFY
let timeout = Some(Duration::from_millis(250));
streams_empty_after_timeout(best_blocks, &net, &mut runtime, timeout);
streams_empty_after_timeout(signed_commitments, &net, &mut runtime, None);
streams_empty_after_timeout(versioned_finality_proof, &net, &mut runtime, None);
// Bob catches up and also finalizes #60 (and should have buffered Alice's vote on #60)
let (best_blocks, signed_commitments) = get_beefy_streams(&mut net.lock(), peers);
let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers);
net.lock().peer(1).client().as_client().finalize_block(finalize, None).unwrap();
// verify beefy skips intermediary votes, and successfully finalizes mandatory block #40
wait_for_best_beefy_blocks(best_blocks, &net, &mut runtime, &[60]);
wait_for_beefy_signed_commitments(signed_commitments, &net, &mut runtime, &[60]);
wait_for_beefy_signed_commitments(versioned_finality_proof, &net, &mut runtime, &[60]);
}
#[test]
@@ -647,7 +651,7 @@ fn correct_beefy_payload() {
// with 3 good voters and 1 bad one, consensus should happen and best blocks produced.
finalize_block_and_wait_for_beefy(&net, peers, &mut runtime, &[10], &[1, 9]);
let (best_blocks, signed_commitments) =
let (best_blocks, versioned_finality_proof) =
get_beefy_streams(&mut net.lock(), &[BeefyKeyring::Alice]);
// now 2 good validators and 1 bad one are voting
@@ -673,10 +677,10 @@ fn correct_beefy_payload() {
// verify consensus is _not_ reached
let timeout = Some(Duration::from_millis(250));
streams_empty_after_timeout(best_blocks, &net, &mut runtime, timeout);
streams_empty_after_timeout(signed_commitments, &net, &mut runtime, None);
streams_empty_after_timeout(versioned_finality_proof, &net, &mut runtime, None);
// 3rd good validator catches up and votes as well
let (best_blocks, signed_commitments) =
let (best_blocks, versioned_finality_proof) =
get_beefy_streams(&mut net.lock(), &[BeefyKeyring::Alice]);
net.lock()
.peer(2)
@@ -687,7 +691,7 @@ fn correct_beefy_payload() {
// verify consensus is reached
wait_for_best_beefy_blocks(best_blocks, &net, &mut runtime, &[11]);
wait_for_beefy_signed_commitments(signed_commitments, &net, &mut runtime, &[11]);
wait_for_beefy_signed_commitments(versioned_finality_proof, &net, &mut runtime, &[11]);
}
#[test]
@@ -746,7 +750,7 @@ fn beefy_importing_blocks() {
let block_num = 2;
let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob];
let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap();
let proof = crate::justification::tests::new_signed_commitment(block_num, &validator_set, keys);
let proof = crate::justification::tests::new_finality_proof(block_num, &validator_set, keys);
let versioned_proof: VersionedFinalityProof<NumberFor<Block>, Signature> = proof.into();
let encoded = versioned_proof.encode();
let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded)));
@@ -781,7 +785,7 @@ fn beefy_importing_blocks() {
let block_num = 3;
let keys = &[BeefyKeyring::Alice];
let validator_set = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap();
let proof = crate::justification::tests::new_signed_commitment(block_num, &validator_set, keys);
let proof = crate::justification::tests::new_finality_proof(block_num, &validator_set, keys);
let versioned_proof: VersionedFinalityProof<NumberFor<Block>, Signature> = proof.into();
let encoded = versioned_proof.encode();
let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded)));
+37 -33
View File
@@ -49,7 +49,7 @@ use beefy_primitives::{
use crate::{
error::Error,
gossip::{topic, GossipValidator},
justification::BeefySignedCommitment,
justification::BeefyVersionedFinalityProof,
keystore::BeefyKeystore,
metric_inc, metric_set,
metrics::Metrics,
@@ -212,7 +212,7 @@ pub(crate) struct BeefyWorker<B: Block, BE, C, R, SO> {
/// Buffer holding votes for future processing.
pending_votes: BTreeMap<NumberFor<B>, Vec<VoteMessage<NumberFor<B>, AuthorityId, Signature>>>,
/// Buffer holding justifications for future processing.
pending_justifications: BTreeMap<NumberFor<B>, Vec<BeefySignedCommitment<B>>>,
pending_justifications: BTreeMap<NumberFor<B>, Vec<BeefyVersionedFinalityProof<B>>>,
/// Chooses which incoming votes to accept and which votes to generate.
voting_oracle: VoterOracle<B>,
}
@@ -381,9 +381,12 @@ where
/// Expects `justification` to be valid.
fn triage_incoming_justif(
&mut self,
justification: BeefySignedCommitment<B>,
justification: BeefyVersionedFinalityProof<B>,
) -> Result<(), Error> {
let block_num = justification.commitment.block_number;
let signed_commitment = match justification {
VersionedFinalityProof::V1(ref sc) => sc,
};
let block_num = signed_commitment.commitment.block_number;
let best_grandpa = *self.best_grandpa_block_header.number();
match self.voting_oracle.triage_round(block_num, best_grandpa)? {
RoundAction::Process => self.finalize(justification),
@@ -417,39 +420,39 @@ where
validator_set_id: rounds.validator_set_id(),
};
let signed_commitment = SignedCommitment { commitment, signatures };
let finality_proof =
VersionedFinalityProof::V1(SignedCommitment { commitment, signatures });
metric_set!(self, beefy_round_concluded, block_num);
info!(target: "beefy", "🥩 Round #{} concluded, committed: {:?}.", round.1, signed_commitment);
info!(target: "beefy", "🥩 Round #{} concluded, finality_proof: {:?}.", round.1, finality_proof);
if let Err(e) = self.backend.append_justification(
BlockId::Number(block_num),
(
BEEFY_ENGINE_ID,
VersionedFinalityProof::V1(signed_commitment.clone()).encode(),
),
(BEEFY_ENGINE_ID, finality_proof.clone().encode()),
) {
debug!(target: "beefy", "🥩 Error {:?} on appending justification: {:?}", e, signed_commitment);
debug!(target: "beefy", "🥩 Error {:?} on appending justification: {:?}", e, finality_proof);
}
// We created the `signed_commitment` and know to be valid.
self.finalize(signed_commitment);
// We created the `finality_proof` and know to be valid.
self.finalize(finality_proof);
}
}
Ok(())
}
/// Provide BEEFY finality for block based on `signed_commitment`:
/// Provide BEEFY finality for block based on `finality_proof`:
/// 1. Prune irrelevant past sessions from the oracle,
/// 2. Set BEEFY best block,
/// 3. Send best block hash and `signed_commitment` to RPC worker.
/// 3. Send best block hash and `finality_proof` to RPC worker.
///
/// Expects `signed commitment` to be valid.
fn finalize(&mut self, signed_commitment: BeefySignedCommitment<B>) {
/// Expects `finality proof` to be valid.
fn finalize(&mut self, finality_proof: BeefyVersionedFinalityProof<B>) {
// Prune any now "finalized" sessions from queue.
self.voting_oracle.try_prune();
let signed_commitment = match finality_proof {
VersionedFinalityProof::V1(ref sc) => sc,
};
let block_num = signed_commitment.commitment.block_number;
if Some(block_num) > self.best_beefy_block {
// Set new best BEEFY block number.
@@ -465,7 +468,7 @@ where
self.links
.to_rpc_justif_sender
.notify(|| Ok::<_, ()>(signed_commitment))
.notify(|| Ok::<_, ()>(finality_proof))
.expect("forwards closure result; the closure always returns Ok; qed.");
} else {
debug!(target: "beefy", "🥩 Can't set best beefy to older: {}", block_num);
@@ -832,7 +835,7 @@ pub(crate) mod tests {
use super::*;
use crate::{
keystore::tests::Keyring,
notification::{BeefyBestBlockStream, BeefySignedCommitmentStream},
notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream},
tests::{
create_beefy_keystore, get_beefy_streams, make_beefy_ids, two_validators::TestApi,
BeefyPeer, BeefyTestNet, BEEFY_PROTOCOL_NAME,
@@ -859,10 +862,11 @@ pub(crate) mod tests {
let keystore = create_beefy_keystore(*key);
let (to_rpc_justif_sender, from_voter_justif_stream) =
BeefySignedCommitmentStream::<Block>::channel();
BeefyVersionedFinalityProofStream::<Block>::channel();
let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
BeefyBestBlockStream::<Block>::channel();
let (_, from_block_import_justif_stream) = BeefySignedCommitmentStream::<Block>::channel();
let (_, from_block_import_justif_stream) =
BeefyVersionedFinalityProofStream::<Block>::channel();
let beefy_rpc_links =
BeefyRPCLinks { from_voter_justif_stream, from_voter_best_beefy_stream };
@@ -1168,37 +1172,37 @@ pub(crate) mod tests {
let mut net = BeefyTestNet::new(1, 0);
let mut worker = create_beefy_worker(&net.peer(0), &keys[0], 1);
let (mut best_block_streams, mut signed_commitments) = get_beefy_streams(&mut net, keys);
let (mut best_block_streams, mut finality_proofs) = get_beefy_streams(&mut net, keys);
let mut best_block_stream = best_block_streams.drain(..).next().unwrap();
let mut signed_commitments = signed_commitments.drain(..).next().unwrap();
let mut finality_proof = finality_proofs.drain(..).next().unwrap();
let create_signed_commitment = |block_num: NumberFor<Block>| {
let create_finality_proof = |block_num: NumberFor<Block>| {
let commitment = Commitment {
payload: Payload::new(known_payload_ids::MMR_ROOT_ID, vec![]),
block_number: block_num,
validator_set_id: validator_set.id(),
};
SignedCommitment { commitment, signatures: vec![None] }
VersionedFinalityProof::V1(SignedCommitment { commitment, signatures: vec![None] })
};
// no 'best beefy block' or signed commitments
// no 'best beefy block' or finality proofs
assert_eq!(worker.best_beefy_block, None);
block_on(poll_fn(move |cx| {
assert_eq!(best_block_stream.poll_next_unpin(cx), Poll::Pending);
assert_eq!(signed_commitments.poll_next_unpin(cx), Poll::Pending);
assert_eq!(finality_proof.poll_next_unpin(cx), Poll::Pending);
Poll::Ready(())
}));
// unknown hash for block #1
let (mut best_block_streams, mut signed_commitments) = get_beefy_streams(&mut net, keys);
let (mut best_block_streams, mut finality_proofs) = get_beefy_streams(&mut net, keys);
let mut best_block_stream = best_block_streams.drain(..).next().unwrap();
let mut signed_commitments = signed_commitments.drain(..).next().unwrap();
let justif = create_signed_commitment(1);
let mut finality_proof = finality_proofs.drain(..).next().unwrap();
let justif = create_finality_proof(1);
worker.finalize(justif.clone());
assert_eq!(worker.best_beefy_block, Some(1));
block_on(poll_fn(move |cx| {
assert_eq!(best_block_stream.poll_next_unpin(cx), Poll::Pending);
match signed_commitments.poll_next_unpin(cx) {
match finality_proof.poll_next_unpin(cx) {
// expect justification
Poll::Ready(Some(received)) => assert_eq!(received, justif),
v => panic!("unexpected value: {:?}", v),
@@ -1211,7 +1215,7 @@ pub(crate) mod tests {
let mut best_block_stream = best_block_streams.drain(..).next().unwrap();
net.generate_blocks(2, 10, &validator_set, false);
let justif = create_signed_commitment(2);
let justif = create_finality_proof(2);
worker.finalize(justif);
assert_eq!(worker.best_beefy_block, Some(2));
block_on(poll_fn(move |cx| {