Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the consensus chain storage proof in FP by gathering necessary storage into one place #3281

Open
NingLin-P opened this issue Dec 3, 2024 · 6 comments
Assignees
Labels
execution Subspace execution

Comments

@NingLin-P
Copy link
Member

Atm, there are many consensus chain storage proofs used by different fraud proof, see:

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub enum FraudProofStorageKeyRequest<Number> {
BlockRandomness,
Timestamp,
SuccessfulBundles(DomainId),
TransactionByteFee,
DomainAllowlistUpdates(DomainId),
BlockDigest,
RuntimeRegistry(RuntimeId),
DynamicCostOfStorage,
DomainSudoCall(DomainId),
MmrRoot(Number),
}

And we need to generate and verify each of them during fraud proof generation & verification, to simplify that we can gather all the small storage into one place which does introduce duplicated storage but will simplify things a lot (and even remove some of the proof data) as we only need to generate/verify one storage proof to cover many proof data.

For example:

struct FraudProofData {
    timestamp: Moment,
    block_randomness: Randomness,
    // Replace `TransactionByteFee` and `DynamicCostOfStorage`
    TransactionByteFee: BlockTransactionByteFee<Balance>,
    // Replace `BlockDigest`
    RuntimeUpgrade: Option<RuntimeId>,
    ...
}

// In pallet-domains:
fn on_finalize(_: BlockNumberFor<T>) {
    ...

    // if this consensus block will derive any domain block, gather the necessary storage for potential FP usage
    if !SuccessfulBundles::is_empty() || !DomainRuntimeUpgrades::is_empty() {
        let fraud_proof_data = ...;
        FraudProofData::set(Some(fraud_proof_data));
    }
}

Then the storage proof of FraudProofData can replace many storage proofs usage in https://github.com/autonomys/subspace/blob/main/crates/sp-domains-fraud-proof/src/fraud_proof.rs

cc @teor2345

@NingLin-P NingLin-P added the execution Subspace execution label Dec 3, 2024
@teor2345 teor2345 self-assigned this Dec 4, 2024
@teor2345
Copy link
Contributor

teor2345 commented Dec 4, 2024

let fraud_proof_data = ...;

I think I'm a bit lost here, is the code here something like:

let fraud_proof_data = FraudProofData {
    block_randomness: StorageKeyProvider::storage_key(FraudProofStorageKeyRequest::BlockRandomness),
	...
};

Then the storage proof of FraudProofData can replace many storage proofs usage in main/crates/sp-domains-fraud-proof/src/fraud_proof.rs

In FraudProofVariant, FraudProof, or somewhere else?

#[derive(Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct FraudProof<Number, Hash, DomainHeader: HeaderT, MmrHash> {
pub domain_id: DomainId,
/// Hash of the bad receipt this fraud proof targeted
pub bad_receipt_hash: HeaderHashFor<DomainHeader>,
/// The MMR proof for the consensus state root that used to verify the storage proof
///
/// It is set `None` if the specific fraud proof variant doesn't contains storage proof
pub maybe_mmr_proof: Option<ConsensusChainMmrLeafProof<Number, Hash, MmrHash>>,
/// The domain runtime code storage proof
///
/// It is set `None` if the specific fraud proof variant doesn't required domain runtime code
/// or the required domain runtime code is available from the current runtime state.
pub maybe_domain_runtime_code_proof: Option<DomainRuntimeCodeAt<Number, Hash, MmrHash>>,
/// The specific fraud proof variant
pub proof: FraudProofVariant<Number, Hash, MmrHash, DomainHeader>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub enum FraudProofVariant<Number, Hash, MmrHash, DomainHeader: HeaderT> {
InvalidStateTransition(InvalidStateTransitionProof),
ValidBundle(ValidBundleProof<Number, Hash, DomainHeader>),
InvalidExtrinsicsRoot(InvalidExtrinsicsRootProof),
InvalidBundles(InvalidBundlesProof<Number, Hash, MmrHash, DomainHeader>),
InvalidDomainBlockHash(InvalidDomainBlockHashProof),
InvalidBlockFees(InvalidBlockFeesProof),
InvalidTransfers(InvalidTransfersProof),
// Dummy fraud proof only used in test and benchmark
//
// NOTE: the `Dummy` must be the last variant, because the `#[cfg(..)]` will apply to
// all the variants after it.
#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
Dummy,
}

@NingLin-P
Copy link
Member Author

I think I'm a bit lost here, is the code here something like:

let fraud_proof_data = FraudProofData {
block_randomness: StorageKeyProvider::storage_key(FraudProofStorageKeyRequest::BlockRandomness),
...
};

What I mean is something like:

pub struct FraudfProofDataStorageProof(StorageProof);

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

and FraudfProofDataStorageProof can be used to replace the storage proof in different fraud proof variants (mostly InvalidExtrinsicsRootProof but I didn't look closer at other fraud proofs).

@teor2345
Copy link
Contributor

teor2345 commented Dec 9, 2024

This is looking a bit complicated, I might start with one of the simpler proofs first.

@teor2345
Copy link
Contributor

I've done some analysis here, and I think the InvalidExtrinsicsRoot is the only proof that will benefit from this change.

It's also possible to merge the storage for SuccessfulBundlesProof and DomainChainsAllowlistUpdateStorageProof, but their size is variable, and not guaranteed to be small (particularly in a fraud situation). And since they're in different fraud proof variants, we'd be duplicating the storage without actually reducing the number of proofs (I think?).

The other proofs are either:

  • large extrinsics or execution (with included extrinsics): state transition, invalid bundles, DomainSudoCallStorageProof
  • already stored (and as a single storage proof): domain block hash, block fees, transfers

They're also stored using different keys, which might complicate things.

@teor2345
Copy link
Contributor

Here's a copy of my notes for the successful bundles proof. We should consider merging this fraud proof data, if the savings on fraud proof generation outweigh the data duplication.

ValidBundle
replaces SuccessfulBundlesProof
pub bundle_storage_proof: DomainId -> Vec<H256>,

InvalidExtrinsicsRoot
replaces DomainChainsAllowlistUpdateStorageProof
pub domain_chain_allowlist: DomainId -> DomainAllowlistUpdates(BTreeSet<ChainId> x 2),

@NingLin-P
Copy link
Member Author

Let's leave SuccessfulBundlesProof and DomainChainsAllowlistUpdateStorageProof as it is, SuccessfulBundlesProof is used in many FPs, merging them may even make things complicated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
execution Subspace execution
Projects
None yet
Development

No branches or pull requests

2 participants