Skip to content

Commit 71a8173

Browse files
acatangiunathanwhit
authored andcommitted
client/beefy: drop justification on import if pallet not enabled (paritytech#13422)
BEEFY pallet allows setting on-chain BEEFY genesis to some future block. Disregard any BEEFY justifications attached to imported blocks that predate configured BEEFY genesis. Signed-off-by: acatangiu <[email protected]>
1 parent a01533e commit 71a8173

File tree

2 files changed

+89
-65
lines changed

2 files changed

+89
-65
lines changed

client/beefy/src/import.rs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,23 @@ where
9292
number: NumberFor<Block>,
9393
hash: <Block as BlockT>::Hash,
9494
) -> Result<BeefyVersionedFinalityProof<Block>, ConsensusError> {
95+
use ConsensusError::ClientImport as ImportError;
9596
let block_id = BlockId::hash(hash);
97+
let beefy_genesis = self
98+
.runtime
99+
.runtime_api()
100+
.beefy_genesis(&block_id)
101+
.map_err(|e| ImportError(e.to_string()))?
102+
.ok_or_else(|| ImportError("Unknown BEEFY genesis".to_string()))?;
103+
if number < beefy_genesis {
104+
return Err(ImportError("BEEFY genesis is set for future block".to_string()))
105+
}
96106
let validator_set = self
97107
.runtime
98108
.runtime_api()
99109
.validator_set(&block_id)
100-
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
101-
.ok_or_else(|| ConsensusError::ClientImport("Unknown validator set".to_string()))?;
110+
.map_err(|e| ImportError(e.to_string()))?
111+
.ok_or_else(|| ImportError("Unknown validator set".to_string()))?;
102112

103113
decode_and_verify_finality_proof::<Block>(&encoded[..], number, &validator_set)
104114
}
@@ -142,26 +152,28 @@ where
142152

143153
match (beefy_encoded, &inner_import_result) {
144154
(Some(encoded), ImportResult::Imported(_)) => {
145-
if let Ok(proof) = self.decode_and_verify(&encoded, number, hash) {
146-
// The proof is valid and the block is imported and final, we can import.
147-
debug!(
148-
target: LOG_TARGET,
149-
"🥩 import justif {:?} for block number {:?}.", proof, number
150-
);
151-
// Send the justification to the BEEFY voter for processing.
152-
self.justification_sender
153-
.notify(|| Ok::<_, ()>(proof))
154-
.expect("forwards closure result; the closure always returns Ok; qed.");
155-
156-
metric_inc!(self, beefy_good_justification_imports);
157-
} else {
158-
debug!(
159-
target: LOG_TARGET,
160-
"🥩 error decoding justification: {:?} for imported block {:?}",
161-
encoded,
162-
number,
163-
);
164-
metric_inc!(self, beefy_bad_justification_imports);
155+
match self.decode_and_verify(&encoded, number, hash) {
156+
Ok(proof) => {
157+
// The proof is valid and the block is imported and final, we can import.
158+
debug!(
159+
target: LOG_TARGET,
160+
"🥩 import justif {:?} for block number {:?}.", proof, number
161+
);
162+
// Send the justification to the BEEFY voter for processing.
163+
self.justification_sender
164+
.notify(|| Ok::<_, ()>(proof))
165+
.expect("the closure always returns Ok; qed.");
166+
metric_inc!(self, beefy_good_justification_imports);
167+
},
168+
Err(err) => {
169+
debug!(
170+
target: LOG_TARGET,
171+
"🥩 error importing BEEFY justification for block {:?}: {:?}",
172+
number,
173+
err,
174+
);
175+
metric_inc!(self, beefy_bad_justification_imports);
176+
},
165177
}
166178
},
167179
_ => (),

client/beefy/src/tests.rs

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use sp_runtime::{
6060
codec::Encode,
6161
generic::BlockId,
6262
traits::{Header as HeaderT, NumberFor},
63-
BuildStorage, DigestItem, Justifications, Storage,
63+
BuildStorage, DigestItem, EncodedJustification, Justifications, Storage,
6464
};
6565
use std::{collections::HashMap, marker::PhantomData, sync::Arc, task::Poll};
6666
use substrate_test_runtime_client::{runtime::Header, ClientExt};
@@ -107,11 +107,13 @@ pub(crate) struct PeerData {
107107
#[derive(Default)]
108108
pub(crate) struct BeefyTestNet {
109109
peers: Vec<BeefyPeer>,
110+
pub beefy_genesis: NumberFor<Block>,
110111
}
111112

112113
impl BeefyTestNet {
113114
pub(crate) fn new(n_authority: usize) -> Self {
114-
let mut net = BeefyTestNet { peers: Vec::with_capacity(n_authority) };
115+
let beefy_genesis = 1;
116+
let mut net = BeefyTestNet { peers: Vec::with_capacity(n_authority), beefy_genesis };
115117

116118
for i in 0..n_authority {
117119
let (rx, cfg) = on_demand_justifications_protocol_config(GENESIS_HASH, None);
@@ -202,7 +204,7 @@ impl TestNetFactory for BeefyTestNet {
202204
) {
203205
let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob];
204206
let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap();
205-
let api = Arc::new(TestApi::with_validator_set(&validator_set));
207+
let api = Arc::new(TestApi::new(self.beefy_genesis, &validator_set, GOOD_MMR_ROOT));
206208
let inner = BlockImportAdapter::new(client.clone());
207209
let (block_import, voter_links, rpc_links) =
208210
beefy_block_import_and_links(inner, client.as_backend(), api, None);
@@ -716,7 +718,7 @@ async fn correct_beefy_payload() {
716718
}
717719

718720
#[tokio::test]
719-
async fn beefy_importing_blocks() {
721+
async fn beefy_importing_justifications() {
720722
use futures::{future::poll_fn, task::Poll};
721723
use sc_block_builder::BlockBuilderProvider;
722724
use sc_client_api::BlockBackend;
@@ -726,11 +728,15 @@ async fn beefy_importing_blocks() {
726728
let mut net = BeefyTestNet::new(2);
727729
let keys = &[BeefyKeyring::Alice, BeefyKeyring::Bob];
728730
let good_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap();
731+
// Set BEEFY genesis to block 3.
732+
net.beefy_genesis = 3;
729733

730734
let client = net.peer(0).client().clone();
735+
let full_client = client.as_client();
731736
let (mut block_import, _, peer_data) = net.make_block_import(client.clone());
732737
let PeerData { beefy_voter_links, .. } = peer_data;
733738
let justif_stream = beefy_voter_links.lock().take().unwrap().from_block_import_justif_stream;
739+
let mut justif_recv = justif_stream.subscribe(100_000);
734740

735741
let params = |block: Block, justifications: Option<Justifications>| {
736742
let mut import = BlockImportParams::new(BlockOrigin::File, block.header);
@@ -740,15 +746,19 @@ async fn beefy_importing_blocks() {
740746
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
741747
import
742748
};
749+
let backend_justif_for = |block_hash: H256| -> Option<EncodedJustification> {
750+
full_client
751+
.justifications(block_hash)
752+
.unwrap()
753+
.and_then(|j| j.get(BEEFY_ENGINE_ID).cloned())
754+
};
743755

744-
let full_client = client.as_client();
745756
let parent_id = BlockId::Number(0);
746757
let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap();
747758
let block = builder.build().unwrap().block;
748759
let hashof1 = block.header.hash();
749760

750-
// Import without justifications.
751-
let mut justif_recv = justif_stream.subscribe(100_000);
761+
// Import block 1 without justifications.
752762
assert_eq!(
753763
block_import
754764
.import_block(params(block.clone(), None), HashMap::new())
@@ -760,16 +770,32 @@ async fn beefy_importing_blocks() {
760770
block_import.import_block(params(block, None), HashMap::new()).await.unwrap(),
761771
ImportResult::AlreadyInChain,
762772
);
763-
// Verify no BEEFY justifications present:
773+
774+
// Import block 2 with "valid" justification (beefy pallet genesis block not yet reached).
775+
let parent_id = BlockId::Number(1);
776+
let block_num = 2;
777+
let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap();
778+
let block = builder.build().unwrap().block;
779+
let hashof2 = block.header.hash();
780+
781+
let proof = crate::justification::tests::new_finality_proof(block_num, &good_set, keys);
782+
let versioned_proof: VersionedFinalityProof<NumberFor<Block>, Signature> = proof.into();
783+
let encoded = versioned_proof.encode();
784+
let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded)));
785+
assert_eq!(
786+
block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(),
787+
ImportResult::Imported(ImportedAux {
788+
bad_justification: false,
789+
is_new_best: true,
790+
..Default::default()
791+
}),
792+
);
793+
794+
// Verify no BEEFY justifications present (for either block 1 or 2):
764795
{
765796
// none in backend,
766-
assert_eq!(
767-
full_client
768-
.justifications(hashof1)
769-
.unwrap()
770-
.and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()),
771-
None
772-
);
797+
assert_eq!(backend_justif_for(hashof1), None);
798+
assert_eq!(backend_justif_for(hashof2), None);
773799
// and none sent to BEEFY worker.
774800
poll_fn(move |cx| {
775801
assert_eq!(justif_recv.poll_next_unpin(cx), Poll::Pending);
@@ -778,17 +804,16 @@ async fn beefy_importing_blocks() {
778804
.await;
779805
}
780806

781-
// Import with valid justification.
782-
let parent_id = BlockId::Number(1);
783-
let block_num = 2;
807+
// Import block 3 with valid justification.
808+
let parent_id = BlockId::Number(2);
809+
let block_num = 3;
810+
let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap();
811+
let block = builder.build().unwrap().block;
812+
let hashof3 = block.header.hash();
784813
let proof = crate::justification::tests::new_finality_proof(block_num, &good_set, keys);
785814
let versioned_proof: VersionedFinalityProof<NumberFor<Block>, Signature> = proof.into();
786815
let encoded = versioned_proof.encode();
787816
let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded)));
788-
789-
let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap();
790-
let block = builder.build().unwrap().block;
791-
let hashof2 = block.header.hash();
792817
let mut justif_recv = justif_stream.subscribe(100_000);
793818
assert_eq!(
794819
block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(),
@@ -801,13 +826,7 @@ async fn beefy_importing_blocks() {
801826
// Verify BEEFY justification successfully imported:
802827
{
803828
// still not in backend (worker is responsible for appending to backend),
804-
assert_eq!(
805-
full_client
806-
.justifications(hashof2)
807-
.unwrap()
808-
.and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()),
809-
None
810-
);
829+
assert_eq!(backend_justif_for(hashof3), None);
811830
// but sent to BEEFY worker
812831
// (worker will append it to backend when all previous mandatory justifs are there as well).
813832
poll_fn(move |cx| {
@@ -820,19 +839,18 @@ async fn beefy_importing_blocks() {
820839
.await;
821840
}
822841

823-
// Import with invalid justification (incorrect validator set).
824-
let parent_id = BlockId::Number(2);
825-
let block_num = 3;
842+
// Import block 4 with invalid justification (incorrect validator set).
843+
let parent_id = BlockId::Number(3);
844+
let block_num = 4;
845+
let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap();
846+
let block = builder.build().unwrap().block;
847+
let hashof4 = block.header.hash();
826848
let keys = &[BeefyKeyring::Alice];
827849
let bad_set = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap();
828850
let proof = crate::justification::tests::new_finality_proof(block_num, &bad_set, keys);
829851
let versioned_proof: VersionedFinalityProof<NumberFor<Block>, Signature> = proof.into();
830852
let encoded = versioned_proof.encode();
831853
let justif = Some(Justifications::from((BEEFY_ENGINE_ID, encoded)));
832-
833-
let builder = full_client.new_block_at(&parent_id, Default::default(), false).unwrap();
834-
let block = builder.build().unwrap().block;
835-
let hashof3 = block.header.hash();
836854
let mut justif_recv = justif_stream.subscribe(100_000);
837855
assert_eq!(
838856
block_import.import_block(params(block, justif), HashMap::new()).await.unwrap(),
@@ -846,13 +864,7 @@ async fn beefy_importing_blocks() {
846864
// Verify bad BEEFY justifications was not imported:
847865
{
848866
// none in backend,
849-
assert_eq!(
850-
full_client
851-
.justifications(hashof3)
852-
.unwrap()
853-
.and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()),
854-
None
855-
);
867+
assert_eq!(backend_justif_for(hashof4), None);
856868
// and none sent to BEEFY worker.
857869
poll_fn(move |cx| {
858870
assert_eq!(justif_recv.poll_next_unpin(cx), Poll::Pending);

0 commit comments

Comments
 (0)