use crate::{ParachainInherentData, INHERENT_IDENTIFIER};
use codec::Decode;
use cumulus_primitives_core::{
relay_chain, relay_chain::UpgradeGoAhead, InboundDownwardMessage, InboundHrmpMessage, ParaId,
PersistedValidationData,
};
use cumulus_primitives_parachain_inherent::MessageQueueChain;
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
use sc_client_api::{Backend, StorageProvider};
use sp_crypto_hashing::twox_128;
use sp_inherents::{InherentData, InherentDataProvider};
use sp_runtime::traits::Block;
use std::collections::BTreeMap;
pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
#[derive(Default)]
pub struct MockValidationDataInherentDataProvider<R = ()> {
pub current_para_block: u32,
pub para_id: ParaId,
pub current_para_block_head: Option<cumulus_primitives_core::relay_chain::HeadData>,
pub relay_offset: u32,
pub relay_blocks_per_para_block: u32,
pub para_blocks_per_relay_epoch: u32,
pub relay_randomness_config: R,
pub xcm_config: MockXcmConfig,
pub raw_downward_messages: Vec<Vec<u8>>,
pub raw_horizontal_messages: Vec<(ParaId, Vec<u8>)>,
pub additional_key_values: Option<Vec<(Vec<u8>, Vec<u8>)>>,
pub upgrade_go_ahead: Option<UpgradeGoAhead>,
}
pub trait GenerateRandomness<I> {
fn generate_randomness(&self, input: I) -> relay_chain::Hash;
}
impl GenerateRandomness<u64> for () {
fn generate_randomness(&self, input: u64) -> relay_chain::Hash {
let mut mock_randomness: [u8; 32] = [0u8; 32];
mock_randomness[..8].copy_from_slice(&input.to_be_bytes());
mock_randomness.into()
}
}
#[derive(Default)]
pub struct MockXcmConfig {
pub starting_dmq_mqc_head: relay_chain::Hash,
pub starting_hrmp_mqc_heads: BTreeMap<ParaId, relay_chain::Hash>,
}
pub struct ParachainSystemName(pub Vec<u8>);
impl Default for ParachainSystemName {
fn default() -> Self {
Self(b"ParachainSystem".to_vec())
}
}
impl MockXcmConfig {
pub fn new<B: Block, BE: Backend<B>, C: StorageProvider<B, BE>>(
client: &C,
parent_block: B::Hash,
parachain_system_name: ParachainSystemName,
) -> Self {
let starting_dmq_mqc_head = client
.storage(
parent_block,
&sp_storage::StorageKey(
[twox_128(¶chain_system_name.0), twox_128(b"LastDmqMqcHead")]
.concat()
.to_vec(),
),
)
.expect("We should be able to read storage from the parent block.")
.map(|ref mut raw_data| {
Decode::decode(&mut &raw_data.0[..]).expect("Stored data should decode correctly")
})
.unwrap_or_default();
let starting_hrmp_mqc_heads = client
.storage(
parent_block,
&sp_storage::StorageKey(
[twox_128(¶chain_system_name.0), twox_128(b"LastHrmpMqcHeads")]
.concat()
.to_vec(),
),
)
.expect("We should be able to read storage from the parent block.")
.map(|ref mut raw_data| {
Decode::decode(&mut &raw_data.0[..]).expect("Stored data should decode correctly")
})
.unwrap_or_default();
Self { starting_dmq_mqc_head, starting_hrmp_mqc_heads }
}
}
#[async_trait::async_trait]
impl<R: Send + Sync + GenerateRandomness<u64>> InherentDataProvider
for MockValidationDataInherentDataProvider<R>
{
async fn provide_inherent_data(
&self,
inherent_data: &mut InherentData,
) -> Result<(), sp_inherents::Error> {
let mut sproof_builder =
RelayStateSproofBuilder { para_id: self.para_id, ..Default::default() };
let relay_parent_number =
self.relay_offset + self.relay_blocks_per_para_block * self.current_para_block;
sproof_builder.current_slot =
((relay_parent_number / RELAY_CHAIN_SLOT_DURATION_MILLIS) as u64).into();
sproof_builder.upgrade_go_ahead = self.upgrade_go_ahead;
let mut downward_messages = Vec::new();
let mut dmq_mqc = MessageQueueChain::new(self.xcm_config.starting_dmq_mqc_head);
for msg in &self.raw_downward_messages {
let wrapped = InboundDownwardMessage { sent_at: relay_parent_number, msg: msg.clone() };
dmq_mqc.extend_downward(&wrapped);
downward_messages.push(wrapped);
}
sproof_builder.dmq_mqc_head = Some(dmq_mqc.head());
let mut horizontal_messages = BTreeMap::<ParaId, Vec<InboundHrmpMessage>>::new();
for (para_id, msg) in &self.raw_horizontal_messages {
let wrapped = InboundHrmpMessage { sent_at: relay_parent_number, data: msg.clone() };
horizontal_messages.entry(*para_id).or_default().push(wrapped);
}
for (para_id, messages) in &horizontal_messages {
let mut channel_mqc = MessageQueueChain::new(
*self
.xcm_config
.starting_hrmp_mqc_heads
.get(para_id)
.unwrap_or(&relay_chain::Hash::default()),
);
for message in messages {
channel_mqc.extend_hrmp(message);
}
sproof_builder.upsert_inbound_channel(*para_id).mqc_head = Some(channel_mqc.head());
}
sproof_builder.current_epoch = if self.para_blocks_per_relay_epoch == 0 {
self.current_para_block.into()
} else {
(self.current_para_block / self.para_blocks_per_relay_epoch).into()
};
sproof_builder.randomness =
self.relay_randomness_config.generate_randomness(self.current_para_block.into());
if let Some(key_values) = &self.additional_key_values {
sproof_builder.additional_key_values = key_values.clone()
}
sproof_builder.included_para_head = self.current_para_block_head.clone();
let (relay_parent_storage_root, proof) = sproof_builder.into_state_root_and_proof();
inherent_data.put_data(
INHERENT_IDENTIFIER,
&ParachainInherentData {
validation_data: PersistedValidationData {
parent_head: Default::default(),
relay_parent_storage_root,
relay_parent_number,
max_pov_size: Default::default(),
},
downward_messages,
horizontal_messages,
relay_chain_state: proof,
},
)
}
async fn try_handle_error(
&self,
_: &sp_inherents::InherentIdentifier,
_: &[u8],
) -> Option<Result<(), sp_inherents::Error>> {
None
}
}