Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/sp-domains/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ pub enum VerificationError {
#[cfg(feature = "std")]
#[cfg_attr(feature = "thiserror", error("Failed to get runtime code: {0}"))]
RuntimeCode(String),
#[cfg(feature = "std")]
#[cfg_attr(
feature = "thiserror",
error("Oneshot error when verifying fraud proof in tx pool: {0}")
)]
Oneshot(String),
}

/// Fraud proof.
Expand Down
2 changes: 1 addition & 1 deletion crates/subspace-fraud-proof/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.4.0", features = ["derive"] }
domain-runtime-primitives = { version = "0.1.0", path = "../../domains/primitives/runtime" }
futures = "0.3.26"
hash-db = "0.15.2"
sc-client-api = { version = "4.0.0-dev", git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232" }
sp-api = { version = "4.0.0-dev", git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232" }
Expand All @@ -30,7 +31,6 @@ tracing = "0.1.37"
[dev-dependencies]
domain-block-builder = { version = "0.1.0", path = "../../domains/client/block-builder" }
domain-test-service = { version = "0.1.0", path = "../../domains/test/service" }
futures = "0.3.26"
pallet-balances = { version = "4.0.0-dev", git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232" }
sc-cli = { version = "0.10.0-dev", git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232", default-features = false }
sc-service = { version = "0.10.0-dev", git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232", default-features = false }
Expand Down
33 changes: 33 additions & 0 deletions crates/subspace-fraud-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ mod invalid_state_transition_proof;
mod tests;

use codec::{Decode, Encode};
use futures::channel::oneshot;
use futures::FutureExt;
use invalid_state_transition_proof::InvalidStateTransitionProofVerifier;
pub use invalid_state_transition_proof::{
ExecutionProver, PrePostStateRootVerifier, VerifyPrePostStateRoot,
Expand Down Expand Up @@ -117,3 +119,34 @@ where
self.verify(proof)
}
}

/// Verifies the fraud proof extracted from extrinsic in the transaction pool.
pub async fn validate_fraud_proof_in_tx_pool<Block, Verifier>(
spawner: &dyn SpawnNamed,
fraud_proof_verifier: Verifier,
fraud_proof: FraudProof<NumberFor<Block>, Block::Hash>,
) -> Result<(), VerificationError>
where
Block: BlockT,
Verifier: VerifyFraudProof<Block> + Send + 'static,
{
let (verified_result_sender, verified_result_receiver) = oneshot::channel();

// Verify the fraud proof in another blocking task as it might be pretty heavy.
spawner.spawn_blocking(
"txpool-fraud-proof-verification",
None,
async move {
let verified_result = fraud_proof_verifier.verify_fraud_proof(&fraud_proof);
verified_result_sender
.send(verified_result)
.expect("Failed to send the verified fraud proof result");
}
.boxed(),
);

match verified_result_receiver.await {
Ok(verified_result) => verified_result,
Err(err) => Err(VerificationError::Oneshot(err.to_string())),
}
}
48 changes: 32 additions & 16 deletions crates/subspace-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ pub mod dsn;
pub mod piece_cache;
pub mod rpc;
pub mod segment_headers;
pub mod tx_pre_validator;

use crate::dsn::import_blocks::import_blocks as import_blocks_from_dsn;
use crate::dsn::{create_dsn_instance, DsnConfigurationError};
use crate::piece_cache::PieceCache;
use crate::segment_headers::{start_segment_header_archiver, SegmentHeaderCache};
use crate::tx_pre_validator::PrimaryChainTxPreValidator;
use derive_more::{Deref, DerefMut, Into};
use domain_runtime_primitives::Hash as DomainHash;
use dsn::start_dsn_archiver;
Expand Down Expand Up @@ -74,14 +76,13 @@ use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
use std::num::NonZeroUsize;
use std::sync::{Arc, Mutex};
use subspace_core_primitives::crypto::kzg::{embedded_kzg_settings, Kzg};
use subspace_fraud_proof::VerifyFraudProof;
use subspace_networking::libp2p::multiaddr::Protocol;
use subspace_networking::libp2p::Multiaddr;
use subspace_networking::{peer_id, Node};
use subspace_runtime_primitives::opaque::Block;
use subspace_runtime_primitives::{AccountId, Balance, Hash, Index as Nonce};
use subspace_transaction_pool::bundle_validator::{BundleValidator, ValidateBundle};
use subspace_transaction_pool::FullPool;
use subspace_transaction_pool::bundle_validator::BundleValidator;
use subspace_transaction_pool::{FullPool, PreValidateTransaction};
use tracing::{debug, error, info, Instrument};

/// Error type for Subspace service.
Expand Down Expand Up @@ -209,8 +210,12 @@ pub fn new_partial<RuntimeApi, ExecutorDispatch>(
FullPool<
Block,
FullClient<RuntimeApi, ExecutorDispatch>,
FraudProofVerifier<RuntimeApi, ExecutorDispatch>,
BundleValidator<Block, FullClient<RuntimeApi, ExecutorDispatch>>,
PrimaryChainTxPreValidator<
Block,
FullClient<RuntimeApi, ExecutorDispatch>,
FraudProofVerifier<RuntimeApi, ExecutorDispatch>,
BundleValidator<Block, FullClient<RuntimeApi, ExecutorDispatch>>,
>,
>,
(
impl BlockImport<
Expand Down Expand Up @@ -293,12 +298,17 @@ where
task_manager.spawn_handle(),
subspace_fraud_proof::PrePostStateRootVerifier::new(client.clone()),
);
let tx_pre_validator = PrimaryChainTxPreValidator::new(
client.clone(),
Box::new(task_manager.spawn_handle()),
proof_verifier.clone(),
bundle_validator.clone(),
);
let transaction_pool = subspace_transaction_pool::new_full(
config,
&task_manager,
client.clone(),
proof_verifier.clone(),
bundle_validator.clone(),
tx_pre_validator,
);

let fraud_proof_block_import =
Expand Down Expand Up @@ -367,7 +377,7 @@ where
}

/// Full node along with some other components.
pub struct NewFull<Client, Validator, Verifier>
pub struct NewFull<Client, TxPreValidator>
where
Client: ProvideRuntimeApi<Block>
+ BlockBackend<Block>
Expand All @@ -378,9 +388,7 @@ where
Client::Api: TaggedTransactionQueue<Block>
+ ExecutorApi<Block, DomainHash>
+ PreValidationObjectApi<Block, domain_runtime_primitives::Hash>,
Validator:
ValidateBundle<Block, domain_runtime_primitives::Hash> + Clone + Send + Sync + 'static,
Verifier: VerifyFraudProof<Block> + Clone + Send + Sync + 'static,
TxPreValidator: PreValidateTransaction<Block = Block> + Send + Sync + Clone + 'static,
{
/// Task manager.
pub task_manager: TaskManager,
Expand All @@ -407,13 +415,17 @@ where
/// Network starter.
pub network_starter: NetworkStarter,
/// Transaction pool.
pub transaction_pool: Arc<FullPool<Block, Client, Verifier, Validator>>,
pub transaction_pool: Arc<FullPool<Block, Client, TxPreValidator>>,
}

type FullNode<RuntimeApi, ExecutorDispatch> = NewFull<
FullClient<RuntimeApi, ExecutorDispatch>,
BundleValidator<Block, FullClient<RuntimeApi, ExecutorDispatch>>,
FraudProofVerifier<RuntimeApi, ExecutorDispatch>,
PrimaryChainTxPreValidator<
Block,
FullClient<RuntimeApi, ExecutorDispatch>,
FraudProofVerifier<RuntimeApi, ExecutorDispatch>,
BundleValidator<Block, FullClient<RuntimeApi, ExecutorDispatch>>,
>,
>;

/// Builds a new service for a full client.
Expand All @@ -428,8 +440,12 @@ pub async fn new_full<RuntimeApi, ExecutorDispatch, I>(
FullPool<
Block,
FullClient<RuntimeApi, ExecutorDispatch>,
FraudProofVerifier<RuntimeApi, ExecutorDispatch>,
BundleValidator<Block, FullClient<RuntimeApi, ExecutorDispatch>>,
PrimaryChainTxPreValidator<
Block,
FullClient<RuntimeApi, ExecutorDispatch>,
FraudProofVerifier<RuntimeApi, ExecutorDispatch>,
BundleValidator<Block, FullClient<RuntimeApi, ExecutorDispatch>>,
>,
>,
(
I,
Expand Down
114 changes: 114 additions & 0 deletions crates/subspace-service/src/tx_pre_validator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use sc_transaction_pool::error::Result as TxPoolResult;
use sc_transaction_pool_api::error::Error as TxPoolError;
use sc_transaction_pool_api::TransactionSource;
use sp_api::ProvideRuntimeApi;
use sp_core::traits::SpawnNamed;
use sp_domains::transaction::{
InvalidTransactionCode, PreValidationObject, PreValidationObjectApi,
};
use sp_runtime::generic::BlockId;
use sp_runtime::traits::Block as BlockT;
use std::marker::PhantomData;
use std::sync::Arc;
use subspace_fraud_proof::VerifyFraudProof;
use subspace_transaction_pool::bundle_validator::ValidateBundle;
use subspace_transaction_pool::PreValidateTransaction;

pub struct PrimaryChainTxPreValidator<Block, Client, Verifier, BundleValidator> {
client: Arc<Client>,
spawner: Box<dyn SpawnNamed>,
fraud_proof_verifier: Verifier,
bundle_validator: BundleValidator,
_phantom_data: PhantomData<Block>,
}

impl<Block, Client, Verifier, BundleValidator> Clone
for PrimaryChainTxPreValidator<Block, Client, Verifier, BundleValidator>
where
Verifier: Clone,
BundleValidator: Clone,
{
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
spawner: self.spawner.clone(),
fraud_proof_verifier: self.fraud_proof_verifier.clone(),
bundle_validator: self.bundle_validator.clone(),
_phantom_data: self._phantom_data,
}
}
}

impl<Block, Client, Verifier, BundleValidator>
PrimaryChainTxPreValidator<Block, Client, Verifier, BundleValidator>
{
pub fn new(
client: Arc<Client>,
spawner: Box<dyn SpawnNamed>,
fraud_proof_verifier: Verifier,
bundle_validator: BundleValidator,
) -> Self {
Self {
client,
spawner,
fraud_proof_verifier,
bundle_validator,
_phantom_data: Default::default(),
}
}
}

#[async_trait::async_trait]
impl<Block, Client, Verifier, BundleValidator> PreValidateTransaction
for PrimaryChainTxPreValidator<Block, Client, Verifier, BundleValidator>
where
Block: BlockT,
Client: ProvideRuntimeApi<Block> + Send + Sync,
Client::Api: PreValidationObjectApi<Block, domain_runtime_primitives::Hash>,
Verifier: VerifyFraudProof<Block> + Clone + Send + Sync + 'static,
BundleValidator:
ValidateBundle<Block, domain_runtime_primitives::Hash> + Clone + Send + Sync + 'static,
{
type Block = Block;
async fn pre_validate_transaction(
&self,
at: Block::Hash,
_source: TransactionSource,
uxt: Block::Extrinsic,
) -> TxPoolResult<()> {
let pre_validation_object = self
.client
.runtime_api()
.extract_pre_validation_object(at, uxt.clone())
.map_err(|err| sc_transaction_pool::error::Error::Blockchain(err.into()))?;

match pre_validation_object {
PreValidationObject::Null => {
// No pre-validation is required.
}
PreValidationObject::Bundle(bundle) => {
if let Err(err) = self
.bundle_validator
.validate_bundle(&BlockId::Hash(at), &bundle)
{
tracing::trace!(target: "txpool", error = ?err, "Dropped `submit_bundle` extrinsic");
return Err(TxPoolError::ImmediatelyDropped.into());
}
}
PreValidationObject::FraudProof(fraud_proof) => {
subspace_fraud_proof::validate_fraud_proof_in_tx_pool(
&self.spawner,
self.fraud_proof_verifier.clone(),
fraud_proof,
)
.await
.map_err(|err| {
tracing::debug!(target: "txpool", error = ?err, "Invalid fraud proof");
TxPoolError::InvalidTransaction(InvalidTransactionCode::FraudProof.into())
})?;
}
}

Ok(())
}
}
2 changes: 1 addition & 1 deletion crates/subspace-transaction-pool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ repository = "https://github.com/subspace/subspace"
description = "Subspace transaction pool"

[dependencies]
async-trait = "0.1.64"
codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false, features = ["derive"] }
domain-runtime-primitives = { version = "0.1.0", path = "../../domains/primitives/runtime" }
futures = "0.3.26"
Expand All @@ -26,6 +27,5 @@ sp-consensus-subspace = { version = "0.1.0", path = "../sp-consensus-subspace" }
sp-domains = { version = "0.1.0", path = "../sp-domains" }
sp-runtime = { version = "7.0.0", git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232" }
sp-transaction-pool = { version = "4.0.0-dev", git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232" }
subspace-fraud-proof = { version = "0.1.0", path = "../subspace-fraud-proof" }
substrate-prometheus-endpoint = { git = "https://github.com/subspace/substrate", rev = "456cfad45a178617f6886ec400c312f2fea59232" }
tracing = "0.1.37"
13 changes: 0 additions & 13 deletions crates/subspace-transaction-pool/src/bundle_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,19 +307,6 @@ pub trait ValidateBundle<Block: BlockT, DomainHash: Encode> {
) -> Result<(), BundleError>;
}

#[derive(Clone)]
pub struct SkipBundleValidation;

impl<Block: BlockT, DomainHash: Encode> ValidateBundle<Block, DomainHash> for SkipBundleValidation {
fn validate_bundle(
&self,
_at: &BlockId<Block>,
_signed_opaque_bundle: &SignedOpaqueBundle<NumberFor<Block>, Block::Hash, DomainHash>,
) -> Result<(), BundleError> {
Ok(())
}
}

impl<Block, DomainHash, Client> ValidateBundle<Block, DomainHash> for BundleValidator<Block, Client>
where
Block: BlockT,
Expand Down
Loading