Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 3afbfe7

Browse files
committed
add some naive equivocation defenses
1 parent a6a0f80 commit 3afbfe7

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/consensus/aura/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ async-trait = "0.1.68"
1010
codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "derive" ] }
1111
futures = "0.3.28"
1212
tracing = "0.1.37"
13+
lru = "0.10.0"
1314

1415
# Substrate
1516
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }

client/consensus/aura/src/collators/lookahead.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ pub async fn run<Block, P, BI, CIDP, Client, Backend, RClient, SO, Proposer, CS>
112112
// This is an arbitrary value which is likely guaranteed to exceed any reasonable
113113
// limit, as it would correspond to 10 non-included blocks.
114114
//
115-
// Since we only search for parent blocks which have already been included,
115+
// Since we only search for parent blocks which have already been imported,
116116
// we can guarantee that all imported blocks respect the unincluded segment
117117
// rules specified by the parachain's runtime and thus will never be too deep.
118118
const PARENT_SEARCH_DEPTH: usize = 10;
@@ -344,6 +344,6 @@ async fn max_ancestry_lookback(
344344
//
345345
// For now, just provide the conservative value of '2'.
346346
// Overestimating can cause problems, as we'd be building on forks of the
347-
// chain that never get included. Underestimating is less of an issue.
347+
// chain that can never get included. Underestimating is less of an issue.
348348
2
349349
}

client/consensus/aura/src/equivocation_import_queue.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
/// should be thrown out and which ones should be kept.
2222
2323
use codec::{Decode, Encode};
24+
use lru::LruCache;
2425
use cumulus_client_consensus_common::ParachainBlockImportMarker;
2526

2627
use sc_consensus::{
@@ -36,12 +37,41 @@ use sp_consensus_aura::{AuraApi, Slot, SlotDuration};
3637
use sp_core::crypto::Pair;
3738
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
3839
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
39-
use std::{fmt::Debug, sync::Arc};
40+
use std::{fmt::Debug, num::NonZeroUsize, sync::Arc};
41+
42+
const LRU_WINDOW: usize = 256;
43+
const EQUIVOCATION_LIMIT: usize = 16;
44+
45+
struct NaiveEquivocationDefender {
46+
cache: LruCache<u64, usize>,
47+
}
48+
49+
impl Default for NaiveEquivocationDefender {
50+
fn default() -> Self {
51+
NaiveEquivocationDefender {
52+
cache: LruCache::new(NonZeroUsize::new(LRU_WINDOW).expect("window > 0; qed")),
53+
}
54+
}
55+
}
56+
57+
impl NaiveEquivocationDefender {
58+
// return `true` if equivocation is beyond the limit.
59+
fn insert_and_check(&mut self, slot: Slot) -> bool {
60+
let val = self.cache.get_or_insert_mut(*slot, || 0);
61+
if *val == EQUIVOCATION_LIMIT {
62+
true
63+
} else {
64+
*val += 1;
65+
false
66+
}
67+
}
68+
}
4069

4170
struct Verifier<P, Client, Block, CIDP> {
4271
client: Arc<Client>,
4372
create_inherent_data_providers: CIDP,
4473
slot_duration: SlotDuration,
74+
defender: NaiveEquivocationDefender,
4575
telemetry: Option<TelemetryHandle>,
4676
_marker: std::marker::PhantomData<(Block, P)>,
4777
}
@@ -88,7 +118,7 @@ where
88118
);
89119

90120
match res {
91-
Ok((pre_header, _slot, seal_digest)) => {
121+
Ok((pre_header, slot, seal_digest)) => {
92122
telemetry!(
93123
self.telemetry;
94124
CONSENSUS_TRACE;
@@ -100,6 +130,14 @@ where
100130
block_params.post_digests.push(seal_digest);
101131
block_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
102132
block_params.post_hash = Some(post_hash);
133+
134+
// Check for and reject egregious amounts of equivocations.
135+
if self.defender.insert_and_check(slot) {
136+
return Err(format!(
137+
"Rejecting block {:?} due to excessive equivocations at slot",
138+
post_hash,
139+
));
140+
}
103141
},
104142
Err(aura_internal::SealVerificationError::Deferred(hdr, slot)) => {
105143
telemetry!(
@@ -207,6 +245,7 @@ where
207245
let verifier = Verifier::<P, _, _, _> {
208246
client,
209247
create_inherent_data_providers,
248+
defender: NaiveEquivocationDefender::default(),
210249
slot_duration,
211250
telemetry,
212251
_marker: std::marker::PhantomData,

0 commit comments

Comments
 (0)