Skip to content

Commit

Permalink
Verify the state root in the core domain bundle
Browse files Browse the repository at this point in the history
`core_block_hash` and `core_state_root` are added to the struct
`ProofOfElection` as we need to the block hash and the corresponding
state root of a core domain. They are optional for the sytem domain
bundle.

Also added a TODO to rename `secondary_hash` to `domain_hash` in
the Receipt struct since now the core domain is possible as well.
  • Loading branch information
liuchengxu committed Nov 16, 2022
1 parent 51e8219 commit 4cb2373
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
7 changes: 7 additions & 0 deletions crates/sp-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ pub struct ProofOfElection<SecondaryHash> {
pub block_number: BlockNumber,
/// Block hash corresponding to the `block_number` above.
pub block_hash: SecondaryHash,
/// Block hash of the core domain block at which the proof of election was created.
pub core_block_hash: Option<SecondaryHash>,
/// Core domain state root corresponding to the `core_block_hash` above.
pub core_state_root: Option<SecondaryHash>,
}

impl<SecondaryHash: Default> ProofOfElection<SecondaryHash> {
Expand All @@ -236,6 +240,8 @@ impl<SecondaryHash: Default> ProofOfElection<SecondaryHash> {
storage_proof: StorageProof::empty(),
block_number: Default::default(),
block_hash: Default::default(),
core_block_hash: None,
core_state_root: None,
}
}
}
Expand Down Expand Up @@ -338,6 +344,7 @@ pub struct ExecutionReceipt<Number, Hash, SecondaryHash> {
pub primary_number: Number,
/// Primary block hash.
pub primary_hash: Hash,
// TODO: rename to `domain_hash`.
/// Secondary block hash.
pub secondary_hash: SecondaryHash,
/// List of storage roots collected during the block execution.
Expand Down
14 changes: 6 additions & 8 deletions domains/client/domain-executor/src/bundle_election_solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,8 @@ where
// electioned executor storage instead of the whole authority set.
let storage_proof = if domain_id.is_system() {
let storage_keys = well_known_keys::bundle_election_storage_keys(domain_id);
self.client.read_proof(
&best_block_id,
&mut storage_keys.iter().map(|s| s.as_slice()),
)?
self.client
.read_proof(best_hash, &mut storage_keys.iter().map(|s| s.as_slice()))?
} else if domain_id.is_core() {
let storage_keys = self
.client
Expand All @@ -128,10 +126,8 @@ where
"Empty core bundle election storage keys".to_string(),
)
})?;
self.client.read_proof(
&best_block_id,
&mut storage_keys.iter().map(|s| s.as_slice()),
)?
self.client
.read_proof(best_hash, &mut storage_keys.iter().map(|s| s.as_slice()))?
} else {
return Err(sp_blockchain::Error::Application(Box::from(
"Only system and core domain are supported".to_string(),
Expand All @@ -158,6 +154,8 @@ where
storage_proof,
block_number: best_number,
block_hash: best_hash,
core_block_hash: None,
core_state_root: None,
};

return Ok(Some(proof_of_election));
Expand Down
15 changes: 14 additions & 1 deletion domains/client/domain-executor/src/core_bundle_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use sp_domains::{
};
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Zero};
use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as HeaderT, Zero};
use sp_runtime::RuntimeAppPublic;
use std::marker::PhantomData;
use std::sync::Arc;
Expand Down Expand Up @@ -143,9 +143,12 @@ where
to_sign.as_ref(),
) {
Ok(Some(signature)) => {
let best_hash = self.client.info().best_hash;

let as_core_block_hash = |system_block_hash: SBlock::Hash| {
Block::Hash::decode(&mut system_block_hash.encode().as_slice()).unwrap()
};

let signed_bundle = SignedBundle {
bundle,
proof_of_election: ProofOfElection {
Expand All @@ -158,6 +161,16 @@ where
storage_proof: proof_of_election.storage_proof,
block_number: proof_of_election.block_number,
block_hash: as_core_block_hash(proof_of_election.block_hash),
// TODO: override the core block info, see if there is a nicer way
// later.
core_block_hash: Some(best_hash),
core_state_root: Some(
*self
.client
.header(BlockId::Hash(best_hash))?
.expect("Best block header must exist; qed")
.state_root(),
),
},
signature: ExecutorSignature::decode(&mut signature.as_slice()).map_err(
|err| {
Expand Down
51 changes: 50 additions & 1 deletion domains/pallets/domain-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,9 +521,20 @@ mod pallet {
/// Domain stake allocation exceeds the maximum available value.
StakeAllocationTooLarge,

/// An error occurred while reading the state needed for verifying the bundle solution.
FailedToReadBundleElectionParams,

/// Invalid core domain bundle solution.
BadBundleElectionSolution,

/// Either `core_block_hash` or `core_state_root` is missing in the proof of election.
CoreBlockInfoNotFound,

/// Cannot verify the core domain state root.
StateRootUnverifiable,

/// Invalid core domain state root.
BadStateRoot,
}

#[pallet::event]
Expand Down Expand Up @@ -752,10 +763,48 @@ impl<T: Config> Pallet<T> {
state_root,
executor_public_key,
global_challenge,
block_number,
block_hash,
core_block_hash,
core_state_root,
..
} = &signed_opaque_bundle.proof_of_election;

// TODO: verify state root
if !block_number.is_zero() {
let block_number = T::BlockNumber::from(*block_number);

let core_block_hash = core_block_hash.ok_or(Error::<T>::CoreBlockInfoNotFound)?;
let core_state_root = core_state_root.ok_or(Error::<T>::CoreBlockInfoNotFound)?;

let maybe_state_root =
signed_opaque_bundle
.bundle
.receipts
.iter()
.find_map(|receipt| {
receipt.trace.last().and_then(|state_root| {
if (receipt.primary_number, receipt.secondary_hash)
== (block_number, *block_hash)
|| (receipt.primary_number, receipt.secondary_hash)
== (block_number, core_block_hash)
{
Some(*state_root)
} else {
None
}
})
});

let expected_state_root = match maybe_state_root {
Some(v) => v,
None => StateRoots::<T>::get((domain_id, block_number, core_block_hash))
.ok_or(Error::<T>::StateRootUnverifiable)?,
};

if expected_state_root != core_state_root {
return Err(Error::<T>::BadStateRoot);
}
}

let db = storage_proof.clone().into_memory_db::<BlakeTwo256>();

Expand Down

0 comments on commit 4cb2373

Please sign in to comment.