Skip to content

Commit

Permalink
Merge pull request #2016 from subspace/fraud_proof/timestamp_and_doma…
Browse files Browse the repository at this point in the history
…in_runtime_upgrade

Fraud Proof: Timestamp
  • Loading branch information
nazar-pc authored Sep 29, 2023
2 parents 3cc49e3 + cfac10c commit 1572145
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 52 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.

42 changes: 22 additions & 20 deletions crates/pallet-domains/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,39 @@ sp-runtime = { version = "24.0.0", default-features = false, git = "https://gith
sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" }
sp-version = { version = "22.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7", features = ["serde"] }
subspace-core-primitives = { version = "0.1.0", default-features = false, path = "../subspace-core-primitives" }
subspace-runtime-primitives = { version = "0.1.0", default-features = false, path = "../subspace-runtime-primitives" }

[dev-dependencies]
pallet-balances = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" }
pallet-timestamp = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" }
sp-state-machine = { version = "0.28.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" }
sp-trie = { version = "22.0.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" }
sp-externalities = { version = "0.19.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" }
subspace-runtime-primitives = { version = "0.1.0", path = "../subspace-runtime-primitives" }

[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"sp-core/std",
"sp-domains/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"sp-version/std",
"subspace-core-primitives/std",
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"sp-core/std",
"sp-domains/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"sp-version/std",
"subspace-core-primitives/std",
"subspace-runtime-primitives/std",
]
try-runtime = ["frame-support/try-runtime"]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"frame-benchmarking",
"frame-benchmarking/runtime-benchmarks",
"sp-domains/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"frame-benchmarking",
"frame-benchmarking/runtime-benchmarks",
"sp-domains/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
8 changes: 7 additions & 1 deletion crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ mod pallet {
use frame_support::{Identity, PalletError};
use frame_system::pallet_prelude::*;
use sp_core::H256;
use sp_domains::fraud_proof::{FraudProof, StorageKeys};
use sp_domains::fraud_proof::{DeriveExtrinsics, FraudProof, StorageKeys};
use sp_domains::inherents::{InherentError, InherentType, INHERENT_IDENTIFIER};
use sp_domains::transaction::InvalidTransactionCode;
use sp_domains::{
Expand All @@ -157,6 +157,7 @@ mod pallet {
use sp_std::vec;
use sp_std::vec::Vec;
use subspace_core_primitives::U256;
use subspace_runtime_primitives::Moment;

#[pallet::config]
pub trait Config: frame_system::Config {
Expand Down Expand Up @@ -287,6 +288,9 @@ mod pallet {

/// Trait impl to fetch storage keys.
type StorageKeys: StorageKeys;

/// Derive extrinsics trait impl.
type DeriveExtrinsics: DeriveExtrinsics<Moment>;
}

#[pallet::pallet]
Expand Down Expand Up @@ -1595,13 +1599,15 @@ impl<T: Config> Pallet<T> {
)
.ok_or(FraudProofError::MissingConsensusStateRoot)?
.1;

verify_invalid_domain_extrinsics_root_fraud_proof::<
T::Block,
T::DomainNumber,
T::DomainHash,
BalanceOf<T>,
T::Hashing,
T::StorageKeys,
T::DeriveExtrinsics,
>(consensus_state_root, bad_receipt, proof)
.map_err(FraudProofError::InvalidExtrinsicRootFraudProof)?;
}
Expand Down
49 changes: 42 additions & 7 deletions crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::dispatch::RawOrigin;
use frame_support::storage::generator::StorageValue;
use frame_support::traits::{ConstU16, ConstU32, ConstU64, Currency, Hooks};
use frame_support::weights::Weight;
use frame_support::{assert_err, assert_ok, parameter_types, PalletId};
Expand Down Expand Up @@ -35,7 +36,7 @@ use sp_version::RuntimeVersion;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use subspace_core_primitives::{Randomness, U256 as P256};
use subspace_runtime_primitives::SSC;
use subspace_runtime_primitives::{Moment, SSC};

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
Expand All @@ -50,6 +51,7 @@ const OPERATOR_ID: OperatorId = 0u64;
frame_support::construct_runtime!(
pub struct Test {
System: frame_system,
Timestamp: pallet_timestamp,
Balances: pallet_balances,
Domains: pallet_domains,
}
Expand Down Expand Up @@ -193,12 +195,33 @@ impl frame_support::traits::Randomness<Hash, BlockNumber> for MockRandomness {

pub struct StorageKeys;
impl sp_domains::fraud_proof::StorageKeys for StorageKeys {
fn block_randomness_key() -> StorageKey {
fn block_randomness_storage_key() -> StorageKey {
StorageKey(
frame_support::storage::storage_prefix("Subspace".as_ref(), "BlockRandomness".as_ref())
.to_vec(),
)
}

fn timestamp_storage_key() -> StorageKey {
StorageKey(pallet_timestamp::pallet::Now::<Test>::storage_value_final_key().to_vec())
}
}

const SLOT_DURATION: u64 = 1000;
impl pallet_timestamp::Config for Test {
/// A timestamp: milliseconds since the unix epoch.
type Moment = Moment;
type OnTimestampSet = ();
type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
type WeightInfo = ();
}

pub struct DeriveExtrinsics;
impl sp_domains::fraud_proof::DeriveExtrinsics<Moment> for DeriveExtrinsics {
fn derive_timestamp_extrinsic(now: Moment) -> Vec<u8> {
UncheckedExtrinsic::new_unsigned(pallet_timestamp::Call::<Test>::set { now }.into())
.encode()
}
}

impl pallet_domains::Config for Test {
Expand Down Expand Up @@ -227,6 +250,7 @@ impl pallet_domains::Config for Test {
type SudoId = ();
type Randomness = MockRandomness;
type StorageKeys = StorageKeys;
type DeriveExtrinsics = DeriveExtrinsics;
}

pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
Expand Down Expand Up @@ -856,6 +880,7 @@ fn test_invalid_domain_extrinsic_root_proof() {
domain_id,
bad_receipt_hash,
Randomness::from([1u8; 32]),
1000,
);
let (consensus_block_number, consensus_block_hash) = (
domain_block.execution_receipt.consensus_block_number,
Expand All @@ -871,23 +896,32 @@ fn test_invalid_domain_extrinsic_root_proof() {
});
}

fn generate_invalid_domain_extrinsic_root_fraud_proof<T: Config>(
fn generate_invalid_domain_extrinsic_root_fraud_proof<T: Config + pallet_timestamp::Config>(
domain_id: DomainId,
bad_receipt_hash: ReceiptHash,
randomness: Randomness,
moment: Moment,
) -> (FraudProof<BlockNumberFor<T>, T::Hash>, T::Hash) {
let storage_key =
let randomness_storage_key =
frame_support::storage::storage_prefix("Subspace".as_ref(), "BlockRandomness".as_ref())
.to_vec();
let timestamp_storage_key =
pallet_timestamp::pallet::Now::<T>::storage_value_final_key().to_vec();
let mut root = T::Hash::default();
let mut mdb = PrefixedMemoryDB::<T::Hashing>::default();
{
let mut trie = TrieDBMutBuilderV1::new(&mut mdb, &mut root).build();
trie.insert(&storage_key, &randomness.encode()).unwrap();
trie.insert(&randomness_storage_key, &randomness.encode())
.unwrap();
trie.insert(&timestamp_storage_key, &moment.encode())
.unwrap();
};

let backend = TrieBackendBuilder::new(mdb, root).build();
let (root, randomness_proof) = storage_proof_for_key::<T, _>(backend, StorageKey(storage_key));
let (_, randomness_storage_proof) =
storage_proof_for_key::<T, _>(backend.clone(), StorageKey(randomness_storage_key));
let (root, timestamp_storage_proof) =
storage_proof_for_key::<T, _>(backend, StorageKey(timestamp_storage_key));
let valid_bundle_digests = vec![ValidBundleDigest {
bundle_index: 0,
bundle_digest: vec![(Some(vec![1, 2, 3]), ExtrinsicDigest::Data(vec![4, 5, 6]))],
Expand All @@ -896,8 +930,9 @@ fn generate_invalid_domain_extrinsic_root_fraud_proof<T: Config>(
FraudProof::InvalidExtrinsicsRoot(InvalidExtrinsicsRootProof {
domain_id,
bad_receipt_hash,
randomness_proof,
randomness_storage_proof,
valid_bundle_digests,
timestamp_storage_proof,
}),
root,
)
Expand Down
13 changes: 11 additions & 2 deletions crates/sp-domains/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ impl ExecutionPhase {
}
}

/// Trait to derive domain extrinsics such as timestamp on Consensus chain.
pub trait DeriveExtrinsics<Moment> {
/// Derives pallet_timestamp::set extrinsic.
fn derive_timestamp_extrinsic(moment: Moment) -> Vec<u8>;
}

/// Error type of fraud proof verification on consensus node.
#[derive(Debug)]
#[cfg_attr(feature = "thiserror", derive(thiserror::Error))]
Expand Down Expand Up @@ -466,7 +472,9 @@ pub struct InvalidExtrinsicsRootProof {
/// Valid Bundle digests
pub valid_bundle_digests: Vec<ValidBundleDigest>,
/// Randomness Storage proof
pub randomness_proof: StorageProof,
pub randomness_storage_proof: StorageProof,
/// Timestamp Storage proof
pub timestamp_storage_proof: StorageProof,
}

impl InvalidTotalRewardsProof {
Expand All @@ -481,7 +489,8 @@ impl InvalidTotalRewardsProof {

/// Trait to get Storage keys.
pub trait StorageKeys {
fn block_randomness_key() -> StorageKey;
fn block_randomness_storage_key() -> StorageKey;
fn timestamp_storage_key() -> StorageKey;
}

/// This is a representation of actual Block Rewards storage in pallet-operator-rewards.
Expand Down
7 changes: 5 additions & 2 deletions crates/sp-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,8 +801,11 @@ sp_api::decl_runtime_apis! {
/// Returns the chain state root at the given block.
fn domain_state_root(domain_id: DomainId, number: DomainNumber, hash: DomainHash) -> Option<DomainHash>;

/// Block randomness key.
fn block_randomness_key() -> Vec<u8>;
/// Returns the storage key for block randomness.
fn block_randomness_storage_key() -> Vec<u8>;

/// Returns the storage key for timestamp;
fn timestamp_storage_key() -> Vec<u8>;
}

pub trait BundleProducerElectionApi<Balance: Encode + Decode> {
Expand Down
40 changes: 29 additions & 11 deletions crates/sp-domains/src/verification.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::fraud_proof::{ExtrinsicDigest, InvalidExtrinsicsRootProof, StorageKeys};
use crate::fraud_proof::{
DeriveExtrinsics, ExtrinsicDigest, InvalidExtrinsicsRootProof, StorageKeys,
};
use crate::valued_trie_root::valued_ordered_trie_root;
use crate::{ExecutionReceipt, DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT};
use domain_runtime_primitives::opaque::AccountId;
Expand All @@ -20,6 +22,7 @@ use sp_std::marker::PhantomData;
use sp_std::vec::Vec;
use sp_trie::{read_trie_value, LayoutV1, StorageProof};
use subspace_core_primitives::Randomness;
use subspace_runtime_primitives::Moment;
use trie_db::node::Value;

/// Verification error.
Expand Down Expand Up @@ -104,6 +107,7 @@ pub fn verify_invalid_domain_extrinsics_root_fraud_proof<
Balance,
Hashing,
SK,
DE,
>(
consensus_state_root: CBlock::Hash,
bad_receipt: ExecutionReceipt<
Expand All @@ -119,10 +123,12 @@ where
CBlock: Block,
Hashing: Hasher<Out = CBlock::Hash>,
SK: StorageKeys,
DE: DeriveExtrinsics<Moment>,
{
let InvalidExtrinsicsRootProof {
valid_bundle_digests,
randomness_proof,
randomness_storage_proof,
timestamp_storage_proof,
..
} = fraud_proof;

Expand All @@ -140,30 +146,42 @@ where
bundle_extrinsics_digests.extend(bundle_digest.bundle_digest.clone());
}

let storage_key = SK::block_randomness_key();
let storage_proof = randomness_proof.clone();
let block_randomness = StorageProofVerifier::<Hashing>::verify_and_get_value::<Randomness>(
&consensus_state_root,
storage_proof,
storage_key,
randomness_storage_proof.clone(),
SK::block_randomness_storage_key(),
)
.map_err(|_| VerificationError::InvalidProof)?;

let timestamp = StorageProofVerifier::<Hashing>::verify_and_get_value::<Moment>(
&consensus_state_root,
timestamp_storage_proof.clone(),
SK::timestamp_storage_key(),
)
.map_err(|_| VerificationError::InvalidProof)?;

let shuffling_seed =
H256::decode(&mut extrinsics_shuffling_seed::<Hashing>(block_randomness).as_ref())
.map_err(|_| VerificationError::FailedToDecode)?;
let ordered_extrinsics = deduplicate_and_shuffle_extrinsics(

let mut ordered_extrinsics = deduplicate_and_shuffle_extrinsics(
bundle_extrinsics_digests,
Randomness::from(shuffling_seed.to_fixed_bytes()),
);

let timestamp_extrinsic =
ExtrinsicDigest::new::<LayoutV1<BlakeTwo256>>(DE::derive_timestamp_extrinsic(timestamp));
ordered_extrinsics.insert(0, timestamp_extrinsic);

let ordered_trie_node_values = ordered_extrinsics
.iter()
.map(|ext_digest| match ext_digest {
ExtrinsicDigest::Data(data) => Value::Inline(data),
ExtrinsicDigest::Hash(hash) => Value::Node(hash.0.as_slice()),
})
.collect();
// TODO: include timestamp and domain runtime upgrade extrinsic

// 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 All @@ -185,7 +203,7 @@ where
pub fn deduplicate_and_shuffle_extrinsics<Extrinsic>(
mut extrinsics: Vec<(Option<AccountId>, Extrinsic)>,
shuffling_seed: Randomness,
) -> Vec<Extrinsic>
) -> VecDeque<Extrinsic>
where
Extrinsic: Debug + PartialEq + Clone,
{
Expand Down Expand Up @@ -213,7 +231,7 @@ where
pub fn shuffle_extrinsics<Extrinsic: Debug, AccountId: Ord + Clone>(
extrinsics: Vec<(Option<AccountId>, Extrinsic)>,
shuffling_seed: Randomness,
) -> Vec<Extrinsic> {
) -> VecDeque<Extrinsic> {
let mut rng = ChaCha8Rng::from_seed(*shuffling_seed);

let mut positions = extrinsics
Expand Down Expand Up @@ -245,7 +263,7 @@ pub fn shuffle_extrinsics<Extrinsic: Debug, AccountId: Ord + Clone>(
.pop_front()
.expect("Extrinsic definitely exists as it's correctly grouped above; qed")
})
.collect::<Vec<_>>();
.collect::<VecDeque<_>>();

trace!(?shuffled_extrinsics, "Shuffled extrinsics");

Expand Down
Loading

0 comments on commit 1572145

Please sign in to comment.