Skip to content

Commit 0f404cb

Browse files
committed
basic babe block check in aura mode
1 parent 75fa698 commit 0f404cb

File tree

2 files changed

+94
-19
lines changed

2 files changed

+94
-19
lines changed

node/src/command.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ fn start_babe_service(arg_matches: &ArgMatches) -> Result<(), sc_cli::Error> {
273273
return start_babe_service(arg_matches);
274274
// Unknown error, return it.
275275
} else {
276+
log::error!("Failed to start Babe service: {:?}", e);
276277
Err(e.into())
277278
}
278279
}

node/src/consensus/aura_wrapped_import_queue.rs

Lines changed: 93 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use futures::future::pending;
12
use sc_client_api::AuxStore;
23
use sc_client_api::BlockOf;
34
use sc_client_api::UsageProvider;
@@ -8,17 +9,23 @@ use sc_consensus::{BasicQueue, DefaultImportQueue};
89
use sc_consensus_aura::AuraVerifier;
910
use sc_consensus_aura::CheckForEquivocation;
1011
use sc_consensus_aura::ImportQueueParams;
12+
use sc_consensus_babe::CompatibleDigestItem as _;
1113
use sc_consensus_slots::InherentDataProviderExt;
1214
use sc_telemetry::TelemetryHandle;
1315
use sp_api::ApiExt;
1416
use sp_api::ProvideRuntimeApi;
1517
use sp_block_builder::BlockBuilder as BlockBuilderApi;
1618
use sp_blockchain::HeaderBackend;
19+
use sp_blockchain::HeaderMetadata;
1720
use sp_consensus::error::Error as ConsensusError;
1821
use sp_consensus_aura::AuraApi;
19-
use sp_consensus_aura::sr25519::AuthorityId;
20-
use sp_consensus_aura::sr25519::AuthorityPair;
22+
use sp_consensus_aura::sr25519::AuthorityId as AuraAuthorityId;
23+
use sp_consensus_aura::sr25519::AuthorityPair as AuraAuthorityPair;
24+
use sp_consensus_babe::AuthorityId as BabeAuthorityId;
25+
use sp_consensus_babe::AuthorityPair as BabeAuthorityPair;
2126
use sp_consensus_babe::BABE_ENGINE_ID;
27+
use sp_core::Pair;
28+
use sp_core::crypto::Ss58Codec;
2229
use sp_inherents::CreateInherentDataProviders;
2330
use sp_runtime::Digest;
2431
use sp_runtime::DigestItem;
@@ -34,11 +41,19 @@ use std::sync::Arc;
3441
/// blacklist the offending node and refuse to connect with them until they
3542
/// are restarted
3643
struct AuraWrappedVerifier<B, C, CIDP, N> {
37-
inner: AuraVerifier<C, AuthorityPair, CIDP, N>,
44+
inner: AuraVerifier<C, AuraAuthorityPair, CIDP, N>,
45+
client: Arc<C>,
3846
_phantom: std::marker::PhantomData<B>,
3947
}
4048

41-
impl<B: BlockT, C, CIDP, N> AuraWrappedVerifier<B, C, CIDP, N> {
49+
impl<B: BlockT, C, CIDP, N> AuraWrappedVerifier<B, C, CIDP, N>
50+
where
51+
CIDP: CreateInherentDataProviders<B, ()> + Send + Sync,
52+
CIDP::InherentDataProviders: InherentDataProviderExt + Send + Sync,
53+
C: ProvideRuntimeApi<B> + Send + Sync + sc_client_api::backend::AuxStore,
54+
C::Api: BlockBuilderApi<B> + AuraApi<B, AuraAuthorityId> + ApiExt<B>,
55+
C: HeaderBackend<B> + HeaderMetadata<B>,
56+
{
4257
pub fn new(
4358
client: Arc<C>,
4459
create_inherent_data_providers: CIDP,
@@ -47,44 +62,102 @@ impl<B: BlockT, C, CIDP, N> AuraWrappedVerifier<B, C, CIDP, N> {
4762
compatibility_mode: sc_consensus_aura::CompatibilityMode<N>,
4863
) -> Self {
4964
let verifier_params = sc_consensus_aura::BuildVerifierParams::<C, CIDP, _> {
50-
client,
65+
client: client.clone(),
5166
create_inherent_data_providers,
5267
telemetry,
5368
check_for_equivocation,
5469
compatibility_mode,
5570
};
5671
let verifier =
57-
sc_consensus_aura::build_verifier::<AuthorityPair, C, CIDP, N>(verifier_params);
72+
sc_consensus_aura::build_verifier::<AuraAuthorityPair, C, CIDP, N>(verifier_params);
5873

5974
AuraWrappedVerifier {
6075
inner: verifier,
76+
client,
6177
_phantom: std::marker::PhantomData,
6278
}
6379
}
80+
81+
/// When a Babe block is encountered in Aura mode, we need to check it is legitimate
82+
/// before switching to the Babe service.
83+
///
84+
/// We can't use a full [`BabeVerifier`] because we don't have a Babe link running, however we
85+
/// can check that the block author is one of the authorities from the last verified Aura block.
86+
///
87+
/// The Babe block will be verified in full after the node spins back up as a Babe service.
88+
async fn check_babe_block(&self, block: BlockImportParams<B>) -> Result<(), String> {
89+
log::info!(
90+
"Checking Babe block {:?} is legitimate",
91+
block.post_header().hash()
92+
);
93+
let mut header = block.header.clone();
94+
let seal = header
95+
.digest_mut()
96+
.pop()
97+
.ok_or_else(|| "Header Unsealed".to_string())?;
98+
let sig = seal
99+
.as_babe_seal()
100+
.ok_or_else(|| "Header bad seal".to_string())?;
101+
102+
let authorities = self.get_last_aura_authorities(block.header)?;
103+
if let Some(a) = authorities.into_iter().find(|a| {
104+
let babe_key = BabeAuthorityId::from(a.clone().into_inner());
105+
BabeAuthorityPair::verify(&sig, header.hash(), &babe_key)
106+
}) {
107+
log::info!(
108+
"Babe block has a valid signature by author: {}",
109+
a.to_ss58check()
110+
);
111+
Ok(())
112+
} else {
113+
Err("Babe block has a bad signature. Rejecting.".to_string())
114+
}
115+
}
116+
117+
/// Given the hash of the first Babe block mined, get the Aura authorities that existed prior to
118+
/// the runtime upgrade.
119+
///
120+
/// Note: We need get the Aura authorities from grandparent rather than the parent,
121+
/// because the runtime upgrade clearing the Aura authorities occurs in the parent.
122+
fn get_last_aura_authorities(
123+
&self,
124+
first_babe_block_header: B::Header,
125+
) -> Result<Vec<AuraAuthorityId>, String> {
126+
let parent_header = self
127+
.client
128+
.header(*first_babe_block_header.parent_hash())
129+
.map_err(|e| format!("Failed to get parent header: {}", e))?
130+
.ok_or("Parent header not found".to_string())?;
131+
let grandparent_hash = parent_header.parent_hash();
132+
133+
let runtime_api = self.client.runtime_api();
134+
let authorities = runtime_api
135+
.authorities(*grandparent_hash)
136+
.map_err(|e| format!("Failed to get Aura authorities: {}", e))?;
137+
138+
Ok(authorities)
139+
}
64140
}
65141

66142
#[async_trait::async_trait]
67143
impl<B: BlockT, C, CIDP> Verifier<B> for AuraWrappedVerifier<B, C, CIDP, NumberFor<B>>
68144
where
69145
C: ProvideRuntimeApi<B> + Send + Sync + sc_client_api::backend::AuxStore,
70-
C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId> + ApiExt<B>,
146+
C::Api: BlockBuilderApi<B> + AuraApi<B, AuraAuthorityId> + ApiExt<B>,
147+
C: HeaderBackend<B> + HeaderMetadata<B>,
71148
CIDP: CreateInherentDataProviders<B, ()> + Send + Sync,
72149
CIDP::InherentDataProviders: InherentDataProviderExt + Send + Sync,
73150
{
74151
async fn verify(&self, block: BlockImportParams<B>) -> Result<BlockImportParams<B>, String> {
75152
let number: NumberFor<B> = *block.post_header().number();
76153
log::debug!("Verifying block: {:?}", number);
77154
if is_babe_digest(block.header.digest()) {
78-
// TODO: Use a BabeVerifier to verify Babe blocks. This will
79-
// prevent rapid validation failure and subsequent re-fetching
80-
// of the same block from peers, which triggers the peers to
81-
// blacklist the offending node and refuse to connect with them until they
82-
// are restarted.
83-
//
84-
// Unfortunately, BabeVerifier construction logic is NOT public outside of
85-
// its crate in vanilla Polkadot SDK, so we are unable to use it until we
86-
// migrate to our Polkadot SDK fork.
87-
self.inner.verify(block).await
155+
self.check_babe_block(block).await?;
156+
log::debug!(
157+
"Detected Babe block! Verifier cannot continue, upgrade must be triggered elsewhere..."
158+
);
159+
pending::<()>().await;
160+
unreachable!("Should not reach here, pending forever.");
88161
} else {
89162
self.inner.verify(block).await
90163
}
@@ -97,15 +170,16 @@ pub fn import_queue<B, I, C, S, CIDP>(
97170
) -> Result<DefaultImportQueue<B>, sp_consensus::Error>
98171
where
99172
B: BlockT,
100-
C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId> + ApiExt<B>,
173+
C::Api: BlockBuilderApi<B> + AuraApi<B, AuraAuthorityId> + ApiExt<B>,
101174
C: 'static
102175
+ ProvideRuntimeApi<B>
103176
+ BlockOf
104177
+ Send
105178
+ Sync
106179
+ AuxStore
107180
+ UsageProvider<B>
108-
+ HeaderBackend<B>,
181+
+ HeaderBackend<B>
182+
+ HeaderMetadata<B>,
109183
I: BlockImport<B, Error = ConsensusError> + Send + Sync + 'static,
110184
S: sp_core::traits::SpawnEssentialNamed,
111185
CIDP: CreateInherentDataProviders<B, ()> + Sync + Send + 'static,

0 commit comments

Comments
 (0)