Skip to content

Commit

Permalink
Add all the necessary storage proofs that are required by fraud proof
Browse files Browse the repository at this point in the history
Signed-off-by: linning <[email protected]>
  • Loading branch information
NingLin-P committed May 7, 2024
1 parent 60c6f69 commit ceeb20b
Showing 1 changed file with 352 additions and 0 deletions.
352 changes: 352 additions & 0 deletions crates/sp-domains-fraud-proof/src/storage_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ pub enum GenerationError {

#[derive(Debug, PartialEq, Eq, Encode, Decode, PalletError, TypeInfo)]
pub enum VerificationError {
InvalidBundleStorageProof,
RuntimeCodeNotFound,
StorageProof(StorageProofVerificationError),
UnexpectedDomainRuntimeUpgrade,
}

impl From<StorageProofVerificationError> for VerificationError {
Expand Down Expand Up @@ -129,3 +132,352 @@ pub trait BasicStorageProof<Block: BlockT>:
)
}
}

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

impl_storage_proof!(SuccessfulBundlesProof);
impl<Block: BlockT> BasicStorageProof<Block> for SuccessfulBundlesProof {
type StorageValue = Vec<H256>;
type Key = DomainId;
fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::SuccessfulBundles(key)
}
}

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

impl_storage_proof!(BlockRandomnessProof);
impl<Block: BlockT> BasicStorageProof<Block> for BlockRandomnessProof {
type StorageValue = Randomness;
fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::BlockRandomness
}
}

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

impl_storage_proof!(DomainChainsAllowlistUpdateStorageProof);
impl<Block: BlockT> BasicStorageProof<Block> for DomainChainsAllowlistUpdateStorageProof {
type StorageValue = DomainAllowlistUpdates;
type Key = DomainId;
fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::DomainAllowlistUpdates(key)
}
}

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

impl_storage_proof!(TimestampStorageProof);
impl<Block: BlockT> BasicStorageProof<Block> for TimestampStorageProof {
type StorageValue = Moment;
fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::Timestamp
}
}

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

impl_storage_proof!(DynamicCostOfStorageProof);
impl<Block: BlockT> BasicStorageProof<Block> for DynamicCostOfStorageProof {
type StorageValue = bool;
fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::DynamicCostOfStorage
}
}

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

impl_storage_proof!(ConsensusTransactionByteFeeProof);
impl<Block: BlockT> BasicStorageProof<Block> for ConsensusTransactionByteFeeProof {
type StorageValue = BlockTransactionByteFee<Balance>;
fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::TransactionByteFee
}
}

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

impl_storage_proof!(BlockDigestProof);
impl<Block: BlockT> BasicStorageProof<Block> for BlockDigestProof {
type StorageValue = Digest;
fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::BlockDigest
}
}

// TODO: get the runtime id from pallet-domains since it won't change for a given domain
// The domain runtime code with storage proof
//
// NOTE: usually we should use the parent consensus block hash to `generate` or `verify` the
// domain runtime code because the domain's `set_code` extrinsic is always the last extrinsic
// to execute thus the domain runtime code will take effect in the next domain block, in other
// word the domain runtime code of the parent consensus block is the one used when construting
// the `ExecutionReceipt`.
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct DomainRuntimeCodeProof(StorageProof);

impl_storage_proof!(DomainRuntimeCodeProof);
impl<Block: BlockT> BasicStorageProof<Block> for DomainRuntimeCodeProof {
type StorageValue = RuntimeObject<NumberFor<Block>, Block::Hash>;
type Key = RuntimeId;
fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest {
FraudProofStorageKeyRequest::RuntimeRegistry(key)
}
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct OpaqueBundleWithProof<Number, Hash, DomainHeader: HeaderT, Balance> {
pub bundle: OpaqueBundle<Number, Hash, DomainHeader, Balance>,
pub bundle_index: u32,
pub bundle_storage_proof: SuccessfulBundlesProof,
}

impl<Number, Hash, DomainHeader, Balance> OpaqueBundleWithProof<Number, Hash, DomainHeader, Balance>
where
Number: Encode,
Hash: Encode,
DomainHeader: HeaderT,
Balance: Encode,
{
#[cfg(feature = "std")]
#[allow(clippy::let_and_return)]
pub fn generate<
Block: BlockT,
PP: ProofProvider<Block>,
SKP: FraudProofStorageKeyProviderInstance,
>(
storage_key_provider: &SKP,
proof_provider: &PP,
domain_id: DomainId,
block_hash: Block::Hash,
bundle: OpaqueBundle<Number, Hash, DomainHeader, Balance>,
bundle_index: u32,
) -> Result<Self, GenerationError> {
let bundle_storage_proof = SuccessfulBundlesProof::generate(
proof_provider,
block_hash,
domain_id,
storage_key_provider,
)?;

Ok(OpaqueBundleWithProof {
bundle,
bundle_index,
bundle_storage_proof,
})
}

/// Verify if the `bundle` does commit to the given `state_root`
pub fn verify<Block: BlockT, SKP: FraudProofStorageKeyProvider>(
&self,
domain_id: DomainId,
state_root: &Block::Hash,
) -> Result<(), VerificationError> {
let successful_bundles_at: Vec<H256> =
<SuccessfulBundlesProof as BasicStorageProof<Block>>::verify::<SKP>(
self.bundle_storage_proof.clone(),
domain_id,
state_root,
)?;

successful_bundles_at
.get(self.bundle_index as usize)
.filter(|b| **b == self.bundle.hash())
.ok_or(VerificationError::InvalidBundleStorageProof)?;

Ok(())
}
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct MaybeDomainRuntimeUpgradedProof {
pub block_digest: BlockDigestProof,
pub new_domain_runtime_code: Option<DomainRuntimeCodeProof>,
}

impl MaybeDomainRuntimeUpgradedProof {
/// Generate the `MaybeDomainRuntimeUpgradedProof`, it is the caller's responsibility to check
/// if the domain runtime is upgraded at `block_hash` if so the `maybe_runtime_id` should be `Some`.
#[cfg(feature = "std")]
#[allow(clippy::let_and_return)]
pub fn generate<
Block: BlockT,
PP: ProofProvider<Block>,
SKP: FraudProofStorageKeyProviderInstance,
>(
storage_key_provider: &SKP,
proof_provider: &PP,
block_hash: Block::Hash,
maybe_runtime_id: Option<RuntimeId>,
) -> Result<Self, GenerationError> {
let block_digest =
BlockDigestProof::generate(proof_provider, block_hash, (), storage_key_provider)?;
let new_domain_runtime_code = if let Some(runtime_id) = maybe_runtime_id {
Some(DomainRuntimeCodeProof::generate(
proof_provider,
block_hash,
runtime_id,
storage_key_provider,
)?)
} else {
None
};
Ok(MaybeDomainRuntimeUpgradedProof {
block_digest,
new_domain_runtime_code,
})
}

pub fn verify<Block: BlockT, SKP: FraudProofStorageKeyProvider>(
&self,
runtime_id: RuntimeId,
state_root: &Block::Hash,
) -> Result<Option<Vec<u8>>, VerificationError> {
let block_digest = <BlockDigestProof as BasicStorageProof<Block>>::verify::<SKP>(
self.block_digest.clone(),
(),
state_root,
)?;

let runtime_upgraded = block_digest
.logs
.iter()
.filter_map(|log| log.as_domain_runtime_upgrade())
.any(|upgraded_runtime_id| upgraded_runtime_id == runtime_id);

match (runtime_upgraded, self.new_domain_runtime_code.as_ref()) {
(true, None) | (false, Some(_)) => {
Err(VerificationError::UnexpectedDomainRuntimeUpgrade)
}
(false, None) => Ok(None),
(true, Some(runtime_code_proof)) => {
let mut runtime_obj = <DomainRuntimeCodeProof as BasicStorageProof<Block>>::verify::<
SKP,
>(
runtime_code_proof.clone(), runtime_id, state_root
)?;
let code = runtime_obj
.raw_genesis
.take_runtime_code()
.ok_or(VerificationError::RuntimeCodeNotFound)?;
Ok(Some(code))
}
}
}
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct DomainInherentExtrinsicDataProof {
pub timestamp_proof: TimestampStorageProof,
pub maybe_domain_runtime_upgrade_proof: MaybeDomainRuntimeUpgradedProof,
pub dynamic_cost_of_storage_proof: DynamicCostOfStorageProof,
pub consensus_chain_byte_fee_proof: ConsensusTransactionByteFeeProof,
pub domain_chain_allowlist_proof: DomainChainsAllowlistUpdateStorageProof,
}

impl DomainInherentExtrinsicDataProof {
#[cfg(feature = "std")]
#[allow(clippy::let_and_return)]
pub fn generate<
Block: BlockT,
PP: ProofProvider<Block>,
SKP: FraudProofStorageKeyProviderInstance,
>(
storage_key_provider: &SKP,
proof_provider: &PP,
domain_id: DomainId,
block_hash: Block::Hash,
maybe_runtime_id: Option<RuntimeId>,
) -> Result<Self, GenerationError> {
let timestamp_proof =
TimestampStorageProof::generate(proof_provider, block_hash, (), storage_key_provider)?;
let maybe_domain_runtime_upgrade_proof = MaybeDomainRuntimeUpgradedProof::generate(
storage_key_provider,
proof_provider,
block_hash,
maybe_runtime_id,
)?;
let dynamic_cost_of_storage_proof = DynamicCostOfStorageProof::generate(
proof_provider,
block_hash,
(),
storage_key_provider,
)?;
let consensus_chain_byte_fee_proof = ConsensusTransactionByteFeeProof::generate(
proof_provider,
block_hash,
(),
storage_key_provider,
)?;
let domain_chain_allowlist_proof = DomainChainsAllowlistUpdateStorageProof::generate(
proof_provider,
block_hash,
domain_id,
storage_key_provider,
)?;
Ok(Self {
timestamp_proof,
maybe_domain_runtime_upgrade_proof,
dynamic_cost_of_storage_proof,
consensus_chain_byte_fee_proof,
domain_chain_allowlist_proof,
})
}

pub fn verify<Block: BlockT, SKP: FraudProofStorageKeyProvider>(
&self,
domain_id: DomainId,
runtime_id: RuntimeId,
state_root: &Block::Hash,
) -> Result<DomainInherentExtrinsicData, VerificationError> {
let timestamp = <TimestampStorageProof as BasicStorageProof<Block>>::verify::<SKP>(
self.timestamp_proof.clone(),
(),
state_root,
)?;

let maybe_domain_runtime_upgrade = self
.maybe_domain_runtime_upgrade_proof
.verify::<Block, SKP>(runtime_id, state_root)?;

let dynamic_cost_of_storage =
<DynamicCostOfStorageProof as BasicStorageProof<Block>>::verify::<SKP>(
self.dynamic_cost_of_storage_proof.clone(),
(),
state_root,
)?;
let consensus_transaction_byte_fee = if dynamic_cost_of_storage {
let raw_transaction_byte_fee =
<ConsensusTransactionByteFeeProof as BasicStorageProof<Block>>::verify::<SKP>(
self.consensus_chain_byte_fee_proof.clone(),
(),
state_root,
)?;

sp_domains::DOMAIN_STORAGE_FEE_MULTIPLIER * raw_transaction_byte_fee.next
} else {
Balance::from(1u32)
};

let domain_chain_allowlist =
<DomainChainsAllowlistUpdateStorageProof as BasicStorageProof<Block>>::verify::<SKP>(
self.domain_chain_allowlist_proof.clone(),
domain_id,
state_root,
)?;

Ok(DomainInherentExtrinsicData {
timestamp,
maybe_domain_runtime_upgrade,
consensus_transaction_byte_fee,
domain_chain_allowlist,
})
}
}

0 comments on commit ceeb20b

Please sign in to comment.