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

Core domain bundle solution verification #923

Merged
merged 7 commits into from
Nov 17, 2022
Merged
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.
Comment on lines +164 to +165
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have an enum instead of a struct so you don't need to have optional fields?

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