Skip to content

Commit 00670c9

Browse files
authored
Bump ExecutorApi version and check fraud proof after version 2 (#581)
* Bump ExecutorApi version and check fraud proof after verion 2 Otherwise the latest binary will not able to sync the network from scratch. For this new added API, we can simply check if the api version is >= 2. Close #576. * Indent the code manually rustfmt failed to help here. * Use `api_version` from ApiExt Simplify the code a little bit.
1 parent 9b6081e commit 00670c9

File tree

3 files changed

+99
-56
lines changed

3 files changed

+99
-56
lines changed

crates/sc-consensus-fraud-proof/src/lib.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
1919
use codec::{Decode, Encode};
2020
use sc_consensus::block_import::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult};
21-
use sp_api::{ProvideRuntimeApi, TransactionFor};
21+
use sp_api::{ApiExt, ProvideRuntimeApi, TransactionFor};
2222
use sp_consensus::{CacheKeyId, Error as ConsensusError};
2323
use sp_executor::ExecutorApi;
2424
use sp_runtime::generic::BlockId;
@@ -88,17 +88,35 @@ where
8888
let parent_hash = *block.header.parent_hash();
8989
let parent_block_id = BlockId::Hash(parent_hash);
9090

91-
if let Some(extrinsics) = &block.body {
92-
for extrinsic in extrinsics.iter() {
93-
if let Some(fraud_proof) = self
94-
.client
95-
.runtime_api()
96-
.extract_fraud_proof(&parent_block_id, extrinsic)
97-
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
98-
{
99-
self.fraud_proof_verifier
100-
.verify_fraud_proof(&fraud_proof)
101-
.map_err(|e| ConsensusError::Other(Box::new(e)))?;
91+
let api_version = self
92+
.client
93+
.runtime_api()
94+
.api_version::<dyn ExecutorApi<Block, SecondaryHash>>(&parent_block_id)
95+
.ok()
96+
.flatten()
97+
.ok_or_else(|| {
98+
ConsensusError::ClientImport(format!(
99+
"Unable to retrieve api version of ExecutorApi at block {parent_hash}"
100+
))
101+
})?;
102+
103+
// `extract_fraud_proof` is added since ExecutorApi version 2
104+
// TODO: reset the ExecutorApi api version and remove this check when the network is reset.
105+
if api_version >= 2 {
106+
if let Some(extrinsics) = &block.body {
107+
for extrinsic in extrinsics.iter() {
108+
let api_result = self
109+
.client
110+
.runtime_api()
111+
.extract_fraud_proof(&parent_block_id, extrinsic);
112+
113+
if let Some(fraud_proof) =
114+
api_result.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
115+
{
116+
self.fraud_proof_verifier
117+
.verify_fraud_proof(&fraud_proof)
118+
.map_err(|e| ConsensusError::Other(Box::new(e)))?;
119+
}
102120
}
103121
}
104122
}

crates/sp-executor/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ pub struct InvalidTransactionProof;
419419

420420
sp_api::decl_runtime_apis! {
421421
/// API necessary for executor pallet.
422+
#[api_version(2)]
422423
pub trait ExecutorApi<SecondaryHash: Encode + Decode> {
423424
/// Submits the execution receipt via an unsigned extrinsic.
424425
fn submit_execution_receipt_unsigned(

crates/subspace-service/src/pool.rs

Lines changed: 68 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use sc_transaction_pool_api::{
1313
ReadyTransactions, TransactionFor, TransactionPool, TransactionSource,
1414
TransactionStatusStreamFor, TxHash,
1515
};
16-
use sp_api::ProvideRuntimeApi;
16+
use sp_api::{ApiExt, ProvideRuntimeApi};
1717
use sp_core::traits::{SpawnEssentialNamed, SpawnNamed};
1818
use sp_executor::ExecutorApi;
1919
use sp_runtime::generic::BlockId;
@@ -124,56 +124,80 @@ where
124124
source: TransactionSource,
125125
uxt: ExtrinsicFor<Self>,
126126
) -> Self::ValidationFuture {
127-
match self.client.runtime_api().extract_fraud_proof(at, &uxt) {
128-
Ok(maybe_fraud_proof) => {
129-
if let Some(fraud_proof) = maybe_fraud_proof {
130-
let inner = self.inner.clone();
131-
let spawner = self.spawner.clone();
132-
let fraud_proof_verifier = self.verifier.clone();
133-
let at = *at;
134-
135-
return async move {
136-
let (verified_result_sender, verified_result_receiver) = oneshot::channel();
137-
138-
// Verify the fraud proof in another blocking task as it might be pretty heavy.
139-
spawner.spawn_blocking(
140-
"txpool-fraud-proof-verification",
141-
None,
142-
async move {
143-
let verified_result =
144-
fraud_proof_verifier.verify_fraud_proof(&fraud_proof);
145-
verified_result_sender
146-
.send(verified_result)
147-
.expect("Failed to send the verified fraud proof result");
148-
}
149-
.boxed(),
150-
);
151-
152-
match verified_result_receiver.await {
153-
Ok(verified_result) => {
154-
match verified_result {
155-
Ok(_) => inner.validate_transaction(&at, source, uxt).await,
156-
Err(err) => {
157-
tracing::debug!(target: "txpool", error = ?err, "Invalid fraud proof");
158-
Err(TxPoolError::InvalidTransaction(
159-
sp_executor::InvalidTransactionCode::FraudProof.into(),
160-
)
161-
.into())
127+
let executor_api_version = match self
128+
.client
129+
.runtime_api()
130+
.api_version::<dyn ExecutorApi<Block, cirrus_primitives::Hash>>(at)
131+
.ok()
132+
.flatten()
133+
{
134+
Some(api_version) => api_version,
135+
None => {
136+
return async move {
137+
Err(sc_transaction_pool::error::Error::RuntimeApi(
138+
"Unable to retrieve ExecutorApi's api version".to_string(),
139+
))
140+
}
141+
.boxed();
142+
}
143+
};
144+
145+
// `extract_fraud_proof` is added since ExecutorApi version 2
146+
// TODO: reset the ExecutorApi api version and remove this check when the network is reset.
147+
if executor_api_version >= 2 {
148+
match self.client.runtime_api().extract_fraud_proof(at, &uxt) {
149+
Ok(maybe_fraud_proof) => {
150+
if let Some(fraud_proof) = maybe_fraud_proof {
151+
let inner = self.inner.clone();
152+
let spawner = self.spawner.clone();
153+
let fraud_proof_verifier = self.verifier.clone();
154+
let at = *at;
155+
156+
return async move {
157+
let (verified_result_sender, verified_result_receiver) = oneshot::channel();
158+
159+
// Verify the fraud proof in another blocking task as it might be pretty heavy.
160+
spawner.spawn_blocking(
161+
"txpool-fraud-proof-verification",
162+
None,
163+
async move {
164+
let verified_result =
165+
fraud_proof_verifier.verify_fraud_proof(&fraud_proof);
166+
verified_result_sender
167+
.send(verified_result)
168+
.expect("Failed to send the verified fraud proof result");
169+
}
170+
.boxed(),
171+
);
172+
173+
match verified_result_receiver.await {
174+
Ok(verified_result) => {
175+
match verified_result {
176+
Ok(_) => inner.validate_transaction(&at, source, uxt).await,
177+
Err(err) => {
178+
tracing::debug!(target: "txpool", error = ?err, "Invalid fraud proof");
179+
Err(TxPoolError::InvalidTransaction(
180+
sp_executor::InvalidTransactionCode::FraudProof.into(),
181+
)
182+
.into())
183+
}
162184
}
163185
}
164-
}
165-
Err(err) => {
166-
tracing::debug!(target: "txpool", error = ?err, "Failed to receive the fraud proof verified result");
167-
Err(TxPoolError::UnknownTransaction(UnknownTransaction::CannotLookup).into())
186+
Err(err) => {
187+
tracing::debug!(target: "txpool", error = ?err, "Failed to receive the fraud proof verified result");
188+
Err(TxPoolError::UnknownTransaction(UnknownTransaction::CannotLookup).into())
189+
}
168190
}
169191
}
192+
.boxed();
170193
}
171-
.boxed();
172194
}
173-
}
174-
Err(err) => {
175-
return async move { Err(sc_transaction_pool::error::Error::Blockchain(err.into())) }
195+
Err(err) => {
196+
return async move {
197+
Err(sc_transaction_pool::error::Error::Blockchain(err.into()))
198+
}
176199
.boxed();
200+
}
177201
}
178202
}
179203

0 commit comments

Comments
 (0)