Skip to content

Commit

Permalink
Merge pull request #2132 from subspace/invalid_domain_runtime_upgrade
Browse files Browse the repository at this point in the history
Fraud proof: Derive set code extrinsic for Domain extrinsic root proof verification
  • Loading branch information
vedhavyas authored Oct 20, 2023
2 parents d31fe47 + 1936d85 commit 18ad5f0
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/pallet-domains/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ subspace-core-primitives = { version = "0.1.0", default-features = false, path =
subspace-runtime-primitives = { version = "0.1.0", default-features = false, path = "../subspace-runtime-primitives" }

[dev-dependencies]
domain-pallet-executive = { version = "0.1.0", default-features = false, path = "../../domains/pallets/executive" }
domain-runtime-primitives = { version = "0.1.0", default-features = false, path = "../../domains/primitives/runtime" }
pallet-balances = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "892bf8e938c6bd2b893d3827d1093cd81baa59a1" }
pallet-timestamp = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "892bf8e938c6bd2b893d3827d1093cd81baa59a1" }
Expand Down
38 changes: 17 additions & 21 deletions crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ use sp_domains_fraud_proof::verification::{
verify_invalid_domain_block_hash_fraud_proof,
verify_invalid_domain_extrinsics_root_fraud_proof, verify_invalid_state_transition_fraud_proof,
};
use sp_domains_fraud_proof::{
FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse,
};
use sp_domains_fraud_proof::FraudProofVerificationInfoRequest;
use sp_runtime::traits::{BlakeTwo256, CheckedSub, Hash, Header, One, Zero};
use sp_runtime::{RuntimeAppPublic, SaturatedConversion, Saturating};
use sp_std::boxed::Box;
Expand Down Expand Up @@ -618,10 +616,10 @@ mod pallet {
FailedToGetBlockRandomness,
/// Failed to get domain timestamp extrinsic.
FailedToGetDomainTimestampExtrinsic,
/// Received invalid Verification info from host function.
ReceivedInvalidVerificationInfo,
/// Parent receipt not found.
ParentReceiptNotFound,
/// Failed to get domain set code extrinsic.
FailedToGetDomainSetCodeExtrinsic,
}

impl<T> From<FraudProofError> for Error<T> {
Expand Down Expand Up @@ -1570,29 +1568,26 @@ impl<T: Config> Pallet<T> {
}
FraudProof::InvalidExtrinsicsRoot(proof) => {
let consensus_block_hash = bad_receipt.consensus_block_hash;
let block_randomness = match get_fraud_proof_verification_info(
let block_randomness = get_fraud_proof_verification_info(
H256::from_slice(consensus_block_hash.as_ref()),
FraudProofVerificationInfoRequest::BlockRandomness,
)
.ok_or(FraudProofError::FailedToGetBlockRandomness)?
{
FraudProofVerificationInfoResponse::BlockRandomness(randomness) => {
Ok(randomness)
}
_ => Err(FraudProofError::ReceivedInvalidVerificationInfo),
}?;
.and_then(|resp| resp.into_block_randomness())
.ok_or(FraudProofError::FailedToGetBlockRandomness)?;

let domain_timestamp_extrinsic = match get_fraud_proof_verification_info(
let domain_timestamp_extrinsic = get_fraud_proof_verification_info(
H256::from_slice(consensus_block_hash.as_ref()),
FraudProofVerificationInfoRequest::DomainTimestampExtrinsic(proof.domain_id),
)
.ok_or(FraudProofError::FailedToGetDomainTimestampExtrinsic)?
{
FraudProofVerificationInfoResponse::DomainTimestampExtrinsic(
domain_timestamp_extrinsic,
) => Ok(domain_timestamp_extrinsic),
_ => Err(FraudProofError::ReceivedInvalidVerificationInfo),
}?;
.and_then(|resp| resp.into_domain_timestamp_extrinsic())
.ok_or(FraudProofError::FailedToGetDomainTimestampExtrinsic)?;

let maybe_domain_set_code_extrinsic = get_fraud_proof_verification_info(
H256::from_slice(consensus_block_hash.as_ref()),
FraudProofVerificationInfoRequest::DomainSetCodeExtrinsic(proof.domain_id),
)
.map(|resp| resp.into_domain_set_code_extrinsic())
.ok_or(FraudProofError::FailedToGetDomainSetCodeExtrinsic)?;

verify_invalid_domain_extrinsics_root_fraud_proof::<
T::Block,
Expand All @@ -1606,6 +1601,7 @@ impl<T: Config> Pallet<T> {
proof,
block_randomness,
domain_timestamp_extrinsic,
maybe_domain_set_code_extrinsic,
)
.map_err(FraudProofError::InvalidExtrinsicRootFraudProof)?;
}
Expand Down
23 changes: 22 additions & 1 deletion crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use sp_domains::{
};
use sp_domains_fraud_proof::{
FraudProofExtension, FraudProofHostFunctions, FraudProofVerificationInfoRequest,
FraudProofVerificationInfoResponse,
FraudProofVerificationInfoResponse, SetCodeExtrinsic,
};
use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash as HashT, IdentityLookup, Zero};
use sp_runtime::{BuildStorage, Digest, OpaqueExtrinsic};
Expand Down Expand Up @@ -60,6 +60,7 @@ frame_support::construct_runtime!(
Timestamp: pallet_timestamp,
Balances: pallet_balances,
Domains: pallet_domains,
DomainExecutive: domain_pallet_executive,
}
);

Expand Down Expand Up @@ -236,6 +237,11 @@ impl pallet_domains::Config for Test {
type Randomness = MockRandomness;
}

impl domain_pallet_executive::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}

pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::<Test>::default()
.build_storage()
Expand All @@ -247,6 +253,7 @@ pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
pub(crate) struct MockDomainFraudProofExtension {
block_randomness: Randomness,
timestamp: Moment,
runtime_code: Vec<u8>,
}

impl FraudProofHostFunctions for MockDomainFraudProofExtension {
Expand All @@ -273,6 +280,19 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension {
FraudProofVerificationInfoRequest::DomainRuntimeCode(_) => {
FraudProofVerificationInfoResponse::DomainRuntimeCode(Default::default())
}
FraudProofVerificationInfoRequest::DomainSetCodeExtrinsic(_) => {
FraudProofVerificationInfoResponse::DomainSetCodeExtrinsic(
SetCodeExtrinsic::EncodedExtrinsic(
UncheckedExtrinsic::new_unsigned(
domain_pallet_executive::Call::<Test>::set_code {
code: self.runtime_code.clone(),
}
.into(),
)
.encode(),
),
)
}
};

Some(response)
Expand Down Expand Up @@ -922,6 +942,7 @@ fn test_invalid_domain_extrinsic_root_proof() {
let fraud_proof_ext = FraudProofExtension::new(Arc::new(MockDomainFraudProofExtension {
block_randomness: Randomness::from([1u8; 32]),
timestamp: 1000,
runtime_code: vec![1, 2, 3, 4],
}));
ext.register_extension(fraud_proof_ext);

Expand Down
57 changes: 48 additions & 9 deletions crates/sp-domains-fraud-proof/src/host_functions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse};
use crate::{
FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse, SetCodeExtrinsic,
};
use codec::{Decode, Encode};
use domain_block_preprocessor::runtime_api::TimestampExtrinsicConstructor;
use domain_block_preprocessor::inherents::extract_domain_runtime_upgrade_code;
use domain_block_preprocessor::runtime_api::{SetCodeConstructor, TimestampExtrinsicConstructor};
use domain_block_preprocessor::runtime_api_light::RuntimeApiLight;
use sc_executor::RuntimeVersionOf;
use sp_api::{BlockT, ProvideRuntimeApi};
Expand Down Expand Up @@ -98,11 +101,8 @@ where
domain_id: DomainId,
) -> Option<Vec<u8>> {
let runtime_api = self.consensus_client.runtime_api();
let consensus_block_hash = consensus_block_hash.into();
let runtime_code = runtime_api
.domain_runtime_code(consensus_block_hash, domain_id)
.ok()??;
let timestamp = runtime_api.timestamp(consensus_block_hash).ok()?;
let runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?;
let timestamp = runtime_api.timestamp(consensus_block_hash.into()).ok()?;

let domain_runtime_api_light =
RuntimeApiLight::new(self.executor.clone(), runtime_code.into());
Expand All @@ -118,6 +118,38 @@ where
.map(|ext| ext.encode())
}

fn derive_domain_set_code_extrinsic(
&self,
consensus_block_hash: H256,
domain_id: DomainId,
) -> Option<SetCodeExtrinsic> {
let maybe_upgraded_runtime = extract_domain_runtime_upgrade_code::<_, _, DomainBlock>(
&self.consensus_client,
consensus_block_hash.into(),
domain_id,
)
.ok()
.flatten();

if let Some(upgraded_runtime) = maybe_upgraded_runtime {
let runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?;
let domain_runtime_api_light =
RuntimeApiLight::new(self.executor.clone(), runtime_code.into());

SetCodeConstructor::<DomainBlock>::construct_set_code_extrinsic(
&domain_runtime_api_light,
// We do not care about the domain hash since this is stateless call into
// domain runtime,
Default::default(),
upgraded_runtime,
)
.ok()
.map(|ext| SetCodeExtrinsic::EncodedExtrinsic(ext.encode()))
} else {
Some(SetCodeExtrinsic::None)
}
}

fn get_domain_runtime_code(
&self,
consensus_block_hash: H256,
Expand Down Expand Up @@ -160,8 +192,8 @@ where
.map(|block_randomness| {
FraudProofVerificationInfoResponse::BlockRandomness(block_randomness)
}),
FraudProofVerificationInfoRequest::DomainTimestampExtrinsic(doman_id) => self
.derive_domain_timestamp_extrinsic(consensus_block_hash, doman_id)
FraudProofVerificationInfoRequest::DomainTimestampExtrinsic(domain_id) => self
.derive_domain_timestamp_extrinsic(consensus_block_hash, domain_id)
.map(|domain_timestamp_extrinsic| {
FraudProofVerificationInfoResponse::DomainTimestampExtrinsic(
domain_timestamp_extrinsic,
Expand All @@ -172,6 +204,13 @@ where
.map(|domain_runtime_code| {
FraudProofVerificationInfoResponse::DomainRuntimeCode(domain_runtime_code)
}),
FraudProofVerificationInfoRequest::DomainSetCodeExtrinsic(domain_id) => self
.derive_domain_set_code_extrinsic(consensus_block_hash, domain_id)
.map(|maybe_domain_set_code_extrinsic| {
FraudProofVerificationInfoResponse::DomainSetCodeExtrinsic(
maybe_domain_set_code_extrinsic,
)
}),
}
}

Expand Down
36 changes: 36 additions & 0 deletions crates/sp-domains-fraud-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,23 @@ pub enum FraudProofVerificationInfoRequest {
DomainTimestampExtrinsic(DomainId),
/// The domain runtime code
DomainRuntimeCode(DomainId),
/// Domain set_code extrinsic if there is a runtime upgrade at a given consensus block hash.
DomainSetCodeExtrinsic(DomainId),
}

impl PassBy for FraudProofVerificationInfoRequest {
type PassBy = pass_by::Codec<Self>;
}

/// Type that maybe holds an encoded set_code extrinsic with upgraded runtime
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub enum SetCodeExtrinsic {
/// No runtime upgrade.
None,
/// Holds an encoded set_code extrinsic with an upgraded runtime.
EncodedExtrinsic(Vec<u8>),
}

/// Response holds required verification information for fraud proof from Host function.
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub enum FraudProofVerificationInfoResponse {
Expand All @@ -61,13 +72,38 @@ pub enum FraudProofVerificationInfoResponse {
DomainTimestampExtrinsic(Vec<u8>),
/// The domain runtime code
DomainRuntimeCode(Vec<u8>),
/// Encoded domain set_code extrinsic if there is a runtime upgrade at given consensus block hash.
DomainSetCodeExtrinsic(SetCodeExtrinsic),
}

impl FraudProofVerificationInfoResponse {
pub fn into_block_randomness(self) -> Option<Randomness> {
match self {
Self::BlockRandomness(randomness) => Some(randomness),
_ => None,
}
}

pub fn into_domain_timestamp_extrinsic(self) -> Option<Vec<u8>> {
match self {
Self::DomainTimestampExtrinsic(timestamp_extrinsic) => Some(timestamp_extrinsic),
_ => None,
}
}

pub fn into_domain_runtime_code(self) -> Option<Vec<u8>> {
match self {
Self::DomainRuntimeCode(c) => Some(c),
_ => None,
}
}

pub fn into_domain_set_code_extrinsic(self) -> SetCodeExtrinsic {
match self {
FraudProofVerificationInfoResponse::DomainSetCodeExtrinsic(
maybe_set_code_extrinsic,
) => maybe_set_code_extrinsic,
_ => SetCodeExtrinsic::None,
}
}
}
14 changes: 11 additions & 3 deletions crates/sp-domains-fraud-proof/src/verification.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
fraud_proof_runtime_interface, FraudProofVerificationInfoRequest,
FraudProofVerificationInfoResponse,
FraudProofVerificationInfoResponse, SetCodeExtrinsic,
};
use codec::{Decode, Encode};
use hash_db::Hasher;
Expand Down Expand Up @@ -39,6 +39,7 @@ pub fn verify_invalid_domain_extrinsics_root_fraud_proof<
fraud_proof: &InvalidExtrinsicsRootProof,
block_randomness: Randomness,
domain_timestamp_extrinsic: Vec<u8>,
maybe_domain_set_code_extrinsic: SetCodeExtrinsic,
) -> Result<(), sp_domains::verification::VerificationError>
where
CBlock: BlockT,
Expand Down Expand Up @@ -73,9 +74,17 @@ where
Randomness::from(shuffling_seed.to_fixed_bytes()),
);

if let SetCodeExtrinsic::EncodedExtrinsic(domain_set_code_extrinsic) =
maybe_domain_set_code_extrinsic
{
let domain_set_code_extrinsic =
ExtrinsicDigest::new::<LayoutV1<DomainHashing>>(domain_set_code_extrinsic);
ordered_extrinsics.push_front(domain_set_code_extrinsic);
}

let timestamp_extrinsic =
ExtrinsicDigest::new::<LayoutV1<DomainHashing>>(domain_timestamp_extrinsic);
ordered_extrinsics.insert(0, timestamp_extrinsic);
ordered_extrinsics.push_front(timestamp_extrinsic);

let ordered_trie_node_values = ordered_extrinsics
.iter()
Expand All @@ -85,7 +94,6 @@ where
})
.collect();

// TODO: domain runtime upgrade extrinsic
let extrinsics_root =
valued_ordered_trie_root::<LayoutV1<BlakeTwo256>>(ordered_trie_node_values);
if bad_receipt.domain_block_extrinsic_root == extrinsics_root {
Expand Down
7 changes: 4 additions & 3 deletions domains/client/block-preprocessor/src/inherents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ where
Ok(inherent_data)
}

pub(crate) fn has_runtime_upgrade<CClient, CBlock, Block>(
pub(crate) fn is_runtime_upgraded<CClient, CBlock, Block>(
consensus_client: &Arc<CClient>,
consensus_block_hash: CBlock::Hash,
domain_id: DomainId,
Expand Down Expand Up @@ -87,7 +87,8 @@ where
.any(|upgraded_runtime_id| upgraded_runtime_id == runtime_id))
}

fn maybe_runtime_upgrade<CClient, CBlock, Block>(
/// Returns new upgraded runtime if upgraded did happen in the provided consensus block.
pub fn extract_domain_runtime_upgrade_code<CClient, CBlock, Block>(
consensus_client: &Arc<CClient>,
consensus_block_hash: CBlock::Hash,
domain_id: DomainId,
Expand Down Expand Up @@ -195,7 +196,7 @@ where
let timestamp_provider =
sp_timestamp::InherentDataProvider::new(InherentType::new(timestamp));

let maybe_runtime_upgrade_code = maybe_runtime_upgrade::<_, _, Block>(
let maybe_runtime_upgrade_code = extract_domain_runtime_upgrade_code::<_, _, Block>(
&self.consensus_client,
consensus_block_hash,
self.domain_id,
Expand Down
4 changes: 2 additions & 2 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 runtime_api_full;
pub mod runtime_api_light;
pub mod xdm_verifier;

use crate::inherents::has_runtime_upgrade;
use crate::inherents::is_runtime_upgraded;
use crate::runtime_api::{SetCodeConstructor, SignerExtractor, StateRootExtractor};
use crate::xdm_verifier::is_valid_xdm;
use codec::{Decode, Encode};
Expand Down Expand Up @@ -170,7 +170,7 @@ where
.extract_successful_bundles(consensus_block_hash, self.domain_id, primary_extrinsics)?;

if bundles.is_empty()
&& !has_runtime_upgrade::<_, _, Block>(
&& !is_runtime_upgraded::<_, _, Block>(
&self.consensus_client,
consensus_block_hash,
self.domain_id,
Expand Down

0 comments on commit 18ad5f0

Please sign in to comment.