From 6021f3916fa2ded29fd048c49e34ec3f7fc86997 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Thu, 22 Aug 2024 12:02:25 +0200 Subject: [PATCH] Implement solochain state proofs --- .../authorities-noting/src/lib.rs | 42 +++++++++++++----- .../src/client_side.rs | 44 +++++++++++++++++++ primitives/core/src/lib.rs | 14 +++++- test-sproof-builder/src/lib.rs | 2 +- 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/container-chain-pallets/authorities-noting/src/lib.rs b/container-chain-pallets/authorities-noting/src/lib.rs index 867b954..e24ac21 100644 --- a/container-chain-pallets/authorities-noting/src/lib.rs +++ b/container-chain-pallets/authorities-noting/src/lib.rs @@ -176,14 +176,26 @@ pub mod pallet { let relay_storage_root = T::RelayChainStateProvider::current_relay_chain_state().state_root; - - let para_id = OrchestratorParaId::::get(); let relay_chain_state_proof = GenericStateProof::new(relay_storage_root, relay_chain_state_proof) .expect("Invalid relay chain state proof"); - // Fetch authorities - let authorities = { + // This pallet needs to support both solochains like starlight and parachains like + // dancebox without any config changes because we want the templates to work on both. + // To detect whether we should fetch authorities from the orchestrator state proof or + // from the relay state proof, we use an empty orchestrator_chain_state_proof as a + // sentinel value to indicate that this container chain is running under a solochain. + let authorities = if orchestrator_chain_state_proof.is_empty() { + // starlight: need to fetch authorities from relay state proof + Self::fetch_authorities_from_proof( + &relay_chain_state_proof, + T::SelfParaId::get(), + true, + ) + } else { + // dancebox: need to fetch orchestrator state root from paras->heads, and then fetch + // authorities from the orchestrator state proof + let para_id = OrchestratorParaId::::get(); let orchestrator_root = Self::fetch_orchestrator_header_from_relay_proof( &relay_chain_state_proof, para_id, @@ -193,9 +205,10 @@ pub mod pallet { GenericStateProof::new(orchestrator_root, orchestrator_chain_state_proof) .expect("Invalid orchestrator chain state proof"); - Self::fetch_authorities_from_orchestrator_proof( + Self::fetch_authorities_from_proof( &orchestrator_chain_state_proof, T::SelfParaId::get(), + false, ) }; @@ -325,22 +338,31 @@ impl Pallet { } /// Fetch author slot from a proof of header - fn fetch_authorities_from_orchestrator_proof( - orchestrator_state_proof: &GenericStateProof, + fn fetch_authorities_from_proof( + state_proof: &GenericStateProof, para_id: ParaId, + solochain: bool, ) -> Result, Error> { // Read orchestrator session index - let session_index = orchestrator_state_proof + let session_index = state_proof .read_entry::(well_known_keys::SESSION_INDEX, None) .map_err(|e| match e { ReadEntryErr::Proof => panic!("Invalid proof: cannot read session index"), _ => Error::::FailedReading, })?; + let pallet_authorities_prefix = if solochain { + Some(well_known_keys::SOLOCHAIN_AUTHORITY_ASSIGNMENT_PREFIX) + } else { + None + }; // Read the assignment from the orchestrator - let assignment = orchestrator_state_proof + let assignment = state_proof .read_entry::>( - &well_known_keys::authority_assignment_for_session(session_index), + &well_known_keys::authority_assignment_for_session( + session_index, + pallet_authorities_prefix, + ), None, ) .map_err(|e| match e { diff --git a/container-chain-primitives/authorities-noting-inherent/src/client_side.rs b/container-chain-primitives/authorities-noting-inherent/src/client_side.rs index ee299dc..0148c76 100644 --- a/container-chain-primitives/authorities-noting-inherent/src/client_side.rs +++ b/container-chain-primitives/authorities-noting-inherent/src/client_side.rs @@ -59,6 +59,34 @@ async fn collect_orchestrator_storage_proof( relevant_keys.push(well_known_keys::SESSION_INDEX.to_vec()); relevant_keys.push(well_known_keys::authority_assignment_for_session( session_index, + None, + )); + + orchestrator_chain_interface + .prove_read(orchestrator_parent, &relevant_keys) + .await + .ok() +} + +/// Collect the relevant solochain chain state in form of a proof +/// for putting it into the authorities noting inherent +async fn collect_solochain_storage_proof( + orchestrator_chain_interface: &impl RelayChainInterface, + orchestrator_parent: PHash, +) -> Option { + // We need to fetch the actual session index to build the key for the + // authorities. + let session_index = orchestrator_chain_interface + .get_storage_by_key(orchestrator_parent, well_known_keys::SESSION_INDEX) + .await + .ok()??; + let session_index = u32::decode(&mut session_index.as_slice()).ok()?; + + let mut relevant_keys = Vec::new(); + relevant_keys.push(well_known_keys::SESSION_INDEX.to_vec()); + relevant_keys.push(well_known_keys::authority_assignment_for_session( + session_index, + Some(well_known_keys::SOLOCHAIN_AUTHORITY_ASSIGNMENT_PREFIX), )); orchestrator_chain_interface @@ -134,6 +162,22 @@ impl ContainerChainAuthoritiesInherentData { }) } + /// Create the [`ContainerChainAuthoritiesInherentData`] at the given `relay_parent`. + /// + /// Returns `None` if the creation failed. + pub async fn create_at_solochain( + relay_parent: PHash, + relay_chain_interface: &impl RelayChainInterface, + ) -> Option { + let relay_chain_state = + collect_solochain_storage_proof(relay_chain_interface, relay_parent).await?; + + Some(ContainerChainAuthoritiesInherentData { + relay_chain_state, + orchestrator_chain_state: sp_trie::StorageProof::empty(), + }) + } + pub async fn get_latest_orchestrator_head_info( relay_parent: PHash, relay_chain_interface: &impl RelayChainInterface, diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index e7f6157..d17cf13 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -96,12 +96,22 @@ pub mod well_known_keys { }) } + /// authorityAssignment pallet prefix pub const AUTHORITY_ASSIGNMENT_PREFIX: &[u8] = &hex_literal::hex!["ebe78423c7e3ed25234f80d54547285a170f16afec7d161bc6acec3964492a0c"]; - pub fn authority_assignment_for_session(session_index: u32) -> Vec { + /// tanssiAuthorityAssignment instead of authorityAssignment for solochain + pub const SOLOCHAIN_AUTHORITY_ASSIGNMENT_PREFIX: &[u8] = + &hex_literal::hex!["7a201242ca61564279dc11734e3f8772170f16afec7d161bc6acec3964492a0c"]; + + pub fn authority_assignment_for_session( + session_index: u32, + custom_prefix: Option<&[u8]>, + ) -> Vec { session_index.using_encoded(|index| { - AUTHORITY_ASSIGNMENT_PREFIX + let prefix = custom_prefix.unwrap_or(AUTHORITY_ASSIGNMENT_PREFIX); + + prefix .iter() .chain(twox_64(index).iter()) .chain(index.iter()) diff --git a/test-sproof-builder/src/lib.rs b/test-sproof-builder/src/lib.rs index cccba8d..e624685 100644 --- a/test-sproof-builder/src/lib.rs +++ b/test-sproof-builder/src/lib.rs @@ -200,7 +200,7 @@ impl AuthorityAssignmentSproofBuilder { self.session_index.encode(), ); insert( - well_known_keys::authority_assignment_for_session(self.session_index).to_vec(), + well_known_keys::authority_assignment_for_session(self.session_index, None).to_vec(), self.authority_assignment.encode(), );