Expand description
Crate implementing a mechanism for Partner Chain blocks to reference a stable Cardano main chain block.
§Purpose of this crate
This crate provides a base for all other Partner Chain Toolkit’s features that require Cardano observability. Whenever some part of Cardano state is observed and included in a Partner Chain’s own state, all Partner Chain nodes need to agree on what data was eligible for inclusion when a PC block was produced. This is best achieved by first agreeing on a stable Cardano block which then serves as the upper boundary of all observed Cardano data. This main chain reference block is selected by the block producing node and must be accepted by other nodes as being stable at the time of block production.
This crate implements the [InherentDataProvider] and InherentDigest mechanism for selecting a reference main chain block as part of inherent data creation step and saving it as a main chain reference hash in the block header.
§Prerequisites
This feature uses the InherentDigest mechanism from sp_partner_chains_consensus_aura crate for storing inherent data in the block header. Your node must use the PartnerChainsProposer defined by that crate for this feature to work.
§Adding to the node
To add the feature to your Partner Chain node, follow the instructions below. Refer to the demo node implementation for reference.
§Data source
A data source implementing the McHashDataSource interface trait should be added to your node. A Db-Sync
implementation can be found in the partner-chains-db-sync-data-sources
crate. Refer to its documentation on
how to configure and use it.
§Adding the inherent data provider
McHashInherentDataProvider should be added to your inherent data provider stack for both block proposal and verification, using dedicated constructor for each.
The main chain reference hash provided by McHashInherentDataProvider is meant to be used by other inherent data providers from the Partner Chains Toolkit which require Cardano observability. As such, it should be created first in your node’s IDP stack, so that McHashInherentDataProvider::mc_hash and McHashInherentDataProvider::previous_mc_hash can be used to pass the reference hashes to other IDPs.
As an example, creation for proposal would look like the following:
use sp_consensus_slots::{Slot, SlotDuration};
use sp_runtime::traits::Block as BlockT;
struct ProposalCIDP {
// the data source should be created as part of your node's setup
mc_hash_data_source: Arc<dyn McHashDataSource + Send + Sync>,
// slot duration should either be a part of your node's configuration or be
// retrieved using `parent_hash` and your consensus mechanism's runtime API
slot_duration: SlotDuration,
}
#[async_trait::async_trait]
impl<Block> sp_inherents::CreateInherentDataProviders<Block, ()> for ProposalCIDP
where
Block: BlockT + Send + Sync
{
type InherentDataProviders = McHashInherentDataProvider;
async fn create_inherent_data_providers(
&self,
parent_hash: <Block as BlockT>::Hash,
_extra_args: (),
) -> Result<Self::InherentDataProviders, Box<dyn std::error::Error + Send + Sync>> {
let parent_header: <Block as BlockT>::Header = unimplemented!("Retrieved from block store");
let slot: Slot = unimplemented!("Provided by the consensus IDP");
let mc_hash = McHashInherentDataProvider::new_proposal(
parent_header,
self.mc_hash_data_source.as_ref(),
slot,
self.slot_duration,
).await?;
// Other providers can now use mc_hash.mc_hash() and mc_hash.previous_mc_hash()
Ok((mc_hash /* other inherent data providers */))
}
}
For block validation, creation of the IDP would look like this:
use sidechain_domain::McBlockHash;
struct VerificationIDP {
// the data source should be created as part of your node's setup
mc_hash_data_source: Arc<dyn McHashDataSource + Send + Sync>,
// slot duration should either be a part of your node's configuration or be
// retrieved using `parent_hash` and your consensus mechanism's runtime API
slot_duration: SlotDuration,
}
#[async_trait::async_trait]
impl<Block> sp_inherents::CreateInherentDataProviders<Block, (Slot, McBlockHash)> for VerificationIDP
where
Block: BlockT + Send + Sync
{
type InherentDataProviders = McHashInherentDataProvider;
async fn create_inherent_data_providers(
&self,
parent_hash: <Block as BlockT>::Hash,
(block_slot_from_header, mc_hash_from_header): (Slot, McBlockHash),
) -> Result<Self::InherentDataProviders, Box<dyn std::error::Error + Send + Sync>> {
let parent_header: <Block as BlockT>::Header = unimplemented!("Retrieved from block store");
let slot: Slot = unimplemented!("Provided by the consensus IDP");
let parent_slot: Option<Slot> = unimplemented!("Read from previous block using runtime API");
let mc_hash = McHashInherentDataProvider::new_verification(
parent_header,
parent_slot,
block_slot_from_header,
mc_hash_from_header,
self.slot_duration,
self.mc_hash_data_source.as_ref(),
)
.await?;
// Other providers can now use mc_hash.mc_hash() and mc_hash.previous_mc_hash()
Ok((mc_hash /* other inherent data providers */))
}
}
Note that for verification the implementation now accepts additional arguments of types [Slot] and McBlockHash which are provided by the import queue based on the header. In this case, the McBlockHash value comes from the InherentDigest defined in next section.
§Import queue configuration
To be able to save and retrieve inherent data in the block header, your node should use the PartnerChainsProposer instead of the stock proposer provided by Substrate. Refer to its documentation for more information.
For this feature to work, the only modification needed is to set McHashInherentDigest as the inherent digest type of the proposer. This will cause it to save the inherent data provided by McHashInherentDataProvider to be also saved in the block header and available for inspection and verification. Assuming your node uses PartnerChainsProposerFactory, this should look like the following:
use sidechain_mc_hash::McHashInherentDigest;
use sp_consensus::Environment;
use sp_partner_chains_consensus_aura::block_proposal::PartnerChainsProposerFactory;
use sp_runtime::traits::Block as BlockT;
fn new_full<Block: BlockT, ProposerFactory: Environment<Block>>(
base_proposer_factory: ProposerFactory,
) {
// .. other node setup logic
let proposer_factory: PartnerChainsProposerFactory<
Block,
ProposerFactory,
McHashInherentDigest,
> = PartnerChainsProposerFactory::new(base_proposer_factory);
// ..
}
Modules§
- mock
- Mock implementations of components in this crate for use in tests
Structs§
- McHash
Inherent Data Provider - Inherent data provider that provides the hash of the latest stable Cardano block (main chain reference block) observed by the block producer to be included in the header of the currently produced PC block.
- McHash
Inherent Digest - InherentDigest implementation for the main chain reference hash
Enums§
- McHash
Inherent Error - Error type returned by constructors of McHashInherentDataProvider
Constants§
- INHERENT_
IDENTIFIER - Inherent identifier under which the main chain block reference is provided
- MC_
HASH_ DIGEST_ ID - Digest ID of the main chain reference block hash
Traits§
- McHash
Data Source - Data source API used by McHashInherentDataProvider