Skip to content

Commit

Permalink
Merge pull request #2363 from subspace/rsub/2322
Browse files Browse the repository at this point in the history
Add extrinsic decodable check
  • Loading branch information
rahulksnv authored Jan 5, 2024
2 parents 612b754 + 2866534 commit 626fb15
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 15 deletions.
7 changes: 7 additions & 0 deletions crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ pub(crate) struct MockDomainFraudProofExtension {
runtime_code: Vec<u8>,
tx_range: bool,
is_inherent: bool,
is_decodable: bool,
domain_total_stake: Balance,
bundle_slot_probability: (u64, u64),
operator_stake: Balance,
Expand Down Expand Up @@ -331,6 +332,9 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension {
FraudProofVerificationInfoRequest::InherentExtrinsicCheck { .. } => {
FraudProofVerificationInfoResponse::InherentExtrinsicCheck(self.is_inherent)
}
FraudProofVerificationInfoRequest::ExtrinsicDecodableCheck { .. } => {
FraudProofVerificationInfoResponse::ExtrinsicDecodableCheck(self.is_decodable)
}
FraudProofVerificationInfoRequest::DomainElectionParams { .. } => {
FraudProofVerificationInfoResponse::DomainElectionParams {
domain_total_stake: self.domain_total_stake,
Expand Down Expand Up @@ -1032,6 +1036,7 @@ fn test_invalid_domain_extrinsic_root_proof() {
runtime_code: vec![1, 2, 3, 4],
tx_range: true,
is_inherent: true,
is_decodable: true,
domain_total_stake: 100 * SSC,
operator_stake: 10 * SSC,
bundle_slot_probability: (0, 0),
Expand Down Expand Up @@ -1112,6 +1117,7 @@ fn test_true_invalid_bundles_inherent_extrinsic_proof() {
tx_range: true,
// return `true` indicating this is an inherent extrinsic
is_inherent: true,
is_decodable: true,
domain_total_stake: 100 * SSC,
operator_stake: 10 * SSC,
bundle_slot_probability: (0, 0),
Expand Down Expand Up @@ -1178,6 +1184,7 @@ fn test_false_invalid_bundles_inherent_extrinsic_proof() {
tx_range: true,
// return `false` indicating this is not an inherent extrinsic
is_inherent: false,
is_decodable: true,
domain_total_stake: 100 * SSC,
operator_stake: 10 * SSC,
bundle_slot_probability: (0, 0),
Expand Down
6 changes: 6 additions & 0 deletions crates/sp-domains-fraud-proof/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ pub enum VerificationError<DomainHash> {
error("Failed to check if a given extrinsic is inherent or not")
)]
FailedToCheckInherentExtrinsic,
/// Failed to check if a given extrinsic is decodable or not.
#[cfg_attr(
feature = "thiserror",
error("Failed to check if a given extrinsic is decodable or not")
)]
FailedToCheckExtrinsicDecodable,
/// Invalid bundle equivocation fraud proof.
#[cfg_attr(
feature = "thiserror",
Expand Down
24 changes: 24 additions & 0 deletions crates/sp-domains-fraud-proof/src/host_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,22 @@ where
.ok()
}

fn is_decodable_extrinsic(
&self,
consensus_block_hash: H256,
domain_id: DomainId,
opaque_extrinsic: OpaqueExtrinsic,
) -> Option<bool> {
let runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?;
let domain_stateless_runtime =
StatelessRuntime::<DomainBlock, _>::new(self.executor.clone(), runtime_code.into());

Some(matches!(
domain_stateless_runtime.decode_extrinsic(opaque_extrinsic),
Ok(Ok(_))
))
}

fn get_domain_election_params(
&self,
consensus_block_hash: H256,
Expand Down Expand Up @@ -390,6 +406,14 @@ where
.map(|is_inherent| {
FraudProofVerificationInfoResponse::InherentExtrinsicCheck(is_inherent)
}),
FraudProofVerificationInfoRequest::ExtrinsicDecodableCheck {
domain_id,
opaque_extrinsic,
} => self
.is_decodable_extrinsic(consensus_block_hash, domain_id, opaque_extrinsic)
.map(|is_decodable| {
FraudProofVerificationInfoResponse::ExtrinsicDecodableCheck(is_decodable)
}),
FraudProofVerificationInfoRequest::DomainElectionParams { domain_id } => self
.get_domain_election_params(consensus_block_hash, domain_id)
.map(|domain_election_params| {
Expand Down
17 changes: 17 additions & 0 deletions crates/sp-domains-fraud-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ pub enum FraudProofVerificationInfoRequest {
/// Extrinsic for which we need to if it is inherent or not.
opaque_extrinsic: OpaqueExtrinsic,
},
/// Request to check if the domain extrinsic is decodable or not.
ExtrinsicDecodableCheck {
domain_id: DomainId,
/// Extrinsic for which we need to if it is decodable or not.
opaque_extrinsic: OpaqueExtrinsic,
},
/// Request to get Domain election params.
DomainElectionParams { domain_id: DomainId },
/// Request to get Operator stake.
Expand Down Expand Up @@ -160,6 +166,8 @@ pub enum FraudProofVerificationInfoResponse {
TxRangeCheck(bool),
/// If the particular extrinsic provided is either inherent or not.
InherentExtrinsicCheck(bool),
/// If the domain extrinsic is decodable or not.
ExtrinsicDecodableCheck(bool),
/// Domain's total stake at a given Consensus hash.
DomainElectionParams {
domain_total_stake: Balance,
Expand Down Expand Up @@ -227,6 +235,15 @@ impl FraudProofVerificationInfoResponse {
}
}

pub fn into_extrinsic_decodable_check(self) -> Option<bool> {
match self {
FraudProofVerificationInfoResponse::ExtrinsicDecodableCheck(is_decodable) => {
Some(is_decodable)
}
_ => None,
}
}

pub fn into_domain_election_params(self) -> Option<(Balance, (u64, u64))> {
match self {
FraudProofVerificationInfoResponse::DomainElectionParams {
Expand Down
21 changes: 21 additions & 0 deletions crates/sp-domains-fraud-proof/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,27 @@ where
Err(VerificationError::InvalidProof)
}
}
InvalidBundleType::UndecodableTx(extrinsic_index) => {
let extrinsic = get_extrinsic_from_proof::<DomainHeader>(
*extrinsic_index,
invalid_bundle_entry.extrinsics_root,
invalid_bundles_fraud_proof.proof_data.clone(),
)?;
let is_decodable = get_fraud_proof_verification_info(
H256::from_slice(bad_receipt.consensus_block_hash.as_ref()),
FraudProofVerificationInfoRequest::ExtrinsicDecodableCheck {
domain_id: invalid_bundles_fraud_proof.domain_id,
opaque_extrinsic: extrinsic,
},
)
.and_then(FraudProofVerificationInfoResponse::into_extrinsic_decodable_check)
.ok_or(VerificationError::FailedToCheckExtrinsicDecodable)?;

if is_decodable == invalid_bundles_fraud_proof.is_true_invalid_fraud_proof {
return Err(VerificationError::InvalidProof);
}
Ok(())
}

// TODO: implement the other invalid bundle types
_ => Err(VerificationError::InvalidProof),
Expand Down
27 changes: 15 additions & 12 deletions domains/client/block-preprocessor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub mod xdm_verifier;

use crate::inherents::is_runtime_upgraded;
use crate::xdm_verifier::is_valid_xdm;
use codec::{Decode, Encode};
use codec::Encode;
use domain_runtime_primitives::opaque::AccountId;
use domain_runtime_primitives::DomainCoreApi;
use sc_client_api::BlockBackend;
Expand Down Expand Up @@ -263,17 +263,20 @@ where
// NOTE: for each extrinsic the checking order must follow `InvalidBundleType::checking_order`
let runtime_api = self.client.runtime_api();
for (index, opaque_extrinsic) in bundle.extrinsics.iter().enumerate() {
let Ok(extrinsic) =
<<Block as BlockT>::Extrinsic>::decode(&mut opaque_extrinsic.encode().as_slice())
else {
tracing::error!(
?opaque_extrinsic,
"Undecodable extrinsic in bundle({})",
bundle.hash()
);
return Ok(BundleValidity::Invalid(InvalidBundleType::UndecodableTx(
index as u32,
)));
let decode_result = runtime_api.decode_extrinsic(at, opaque_extrinsic.clone())?;
let extrinsic = match decode_result {
Ok(extrinsic) => extrinsic,
Err(err) => {
tracing::error!(
?opaque_extrinsic,
?err,
"Undecodable extrinsic in bundle({})",
bundle.hash()
);
return Ok(BundleValidity::Invalid(InvalidBundleType::UndecodableTx(
index as u32,
)));
}
};

let is_within_tx_range =
Expand Down
11 changes: 10 additions & 1 deletion domains/client/block-preprocessor/src/stateless_runtime.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use codec::{Codec, Encode};
use domain_runtime_primitives::opaque::AccountId;
use domain_runtime_primitives::{CheckExtrinsicsValidityError, DomainCoreApi};
use domain_runtime_primitives::{
CheckExtrinsicsValidityError, DecodeExtrinsicError, DomainCoreApi,
};
use sc_executor::RuntimeVersionOf;
use sp_api::{ApiError, BlockT, Core, Hasher, RuntimeVersion};
use sp_core::traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode};
Expand Down Expand Up @@ -197,6 +199,13 @@ where
<Self as DomainCoreApi<Block>>::is_inherent_extrinsic(self, Default::default(), extrinsic)
}

pub fn decode_extrinsic(
&self,
opaque_extrinsic: sp_runtime::OpaqueExtrinsic,
) -> Result<Result<<Block as BlockT>::Extrinsic, DecodeExtrinsicError>, ApiError> {
<Self as DomainCoreApi<Block>>::decode_extrinsic(self, Default::default(), opaque_extrinsic)
}

pub fn is_within_tx_range(
&self,
extrinsic: &<Block as BlockT>::Extrinsic,
Expand Down
11 changes: 11 additions & 0 deletions domains/primitives/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;

use alloc::string::String;
use frame_support::dispatch::{DispatchClass, PerDispatchClass};
use frame_support::weights::constants::{BlockExecutionWeight, ExtrinsicBaseWeight};
use frame_system::limits::{BlockLength, BlockWeights};
Expand Down Expand Up @@ -168,6 +171,9 @@ pub struct CheckExtrinsicsValidityError {
pub transaction_validity_error: TransactionValidityError,
}

#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct DecodeExtrinsicError(pub String);

/// fullu qualified method name of check_extrinsics_and_do_pre_dispatch runtime api.
/// Used to call state machine.
/// Change it when the runtime api's name is changed in the interface.
Expand Down Expand Up @@ -233,6 +239,11 @@ sp_api::decl_runtime_apis! {
fn check_extrinsics_and_do_pre_dispatch(uxts: Vec<<Block as BlockT>::Extrinsic>, block_number: NumberFor<Block>,
block_hash: <Block as BlockT>::Hash) -> Result<(), CheckExtrinsicsValidityError>;

/// Decodes the domain specific extrinsic from the opaque extrinsic.
fn decode_extrinsic(
opaque_extrinsic: sp_runtime::OpaqueExtrinsic,
) -> Result<<Block as BlockT>::Extrinsic, DecodeExtrinsicError>;

/// Returns extrinsic Era if present
fn extrinsic_era(
extrinsic: &<Block as BlockT>::Extrinsic
Expand Down
13 changes: 12 additions & 1 deletion domains/runtime/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ mod precompiles;
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

extern crate alloc;

use codec::{Decode, Encode};
use domain_runtime_primitives::opaque::Header;
pub use domain_runtime_primitives::{
block_weights, maximum_block_length, opaque, Balance, BlockNumber, Hash, Nonce,
EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT,
};
use domain_runtime_primitives::{
CheckExtrinsicsValidityError, MultiAccountId, TryConvertBack, SLOT_DURATION,
CheckExtrinsicsValidityError, DecodeExtrinsicError, MultiAccountId, TryConvertBack,
SLOT_DURATION,
};
use fp_account::EthereumSignature;
use fp_self_contained::{CheckedSignature, SelfContainedCall};
Expand Down Expand Up @@ -951,6 +954,14 @@ impl_runtime_apis! {
Ok(())
}

fn decode_extrinsic(
opaque_extrinsic: sp_runtime::OpaqueExtrinsic,
) -> Result<<Block as BlockT>::Extrinsic, DecodeExtrinsicError> {
let encoded = opaque_extrinsic.encode();
UncheckedExtrinsic::decode(&mut encoded.as_slice())
.map_err(|err| DecodeExtrinsicError(alloc::format!("{}", err)))
}

fn extrinsic_era(
extrinsic: &<Block as BlockT>::Extrinsic
) -> Option<Era> {
Expand Down
12 changes: 11 additions & 1 deletion domains/test/runtime/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ mod precompiles;
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

extern crate alloc;

use codec::{Decode, Encode};
pub use domain_runtime_primitives::opaque::Header;
use domain_runtime_primitives::{
block_weights, maximum_block_length, MultiAccountId, TryConvertBack, EXISTENTIAL_DEPOSIT,
MAXIMUM_BLOCK_WEIGHT, SLOT_DURATION,
};
pub use domain_runtime_primitives::{
opaque, Balance, BlockNumber, CheckExtrinsicsValidityError, Hash, Nonce,
opaque, Balance, BlockNumber, CheckExtrinsicsValidityError, DecodeExtrinsicError, Hash, Nonce,
};
use fp_account::EthereumSignature;
use fp_self_contained::{CheckedSignature, SelfContainedCall};
Expand Down Expand Up @@ -916,6 +918,14 @@ impl_runtime_apis! {
Ok(())
}

fn decode_extrinsic(
opaque_extrinsic: sp_runtime::OpaqueExtrinsic,
) -> Result<<Block as BlockT>::Extrinsic, DecodeExtrinsicError> {
let encoded = opaque_extrinsic.encode();
UncheckedExtrinsic::decode(&mut encoded.as_slice())
.map_err(|err| DecodeExtrinsicError(alloc::format!("{}", err)))
}

fn extrinsic_era(
extrinsic: &<Block as BlockT>::Extrinsic
) -> Option<Era> {
Expand Down

0 comments on commit 626fb15

Please sign in to comment.