use crate::Client;
use codec::{Decode, Encode};
use polkadot_primitives::{vstaging::InherentData as ParachainsInherentData, Block};
use polkadot_test_runtime::UncheckedExtrinsic;
use polkadot_test_service::GetLastTimestamp;
use sc_block_builder::{BlockBuilder, BlockBuilderBuilder};
use sp_api::ProvideRuntimeApi;
use sp_consensus_babe::{
digests::{PreDigest, SecondaryPlainPreDigest},
BABE_ENGINE_ID,
};
use sp_runtime::{traits::Block as BlockT, Digest, DigestItem};
use sp_state_machine::BasicExternalities;
pub trait InitPolkadotBlockBuilder {
fn init_polkadot_block_builder(&self) -> sc_block_builder::BlockBuilder<Block, Client>;
fn init_polkadot_block_builder_at(
&self,
hash: <Block as BlockT>::Hash,
) -> sc_block_builder::BlockBuilder<Block, Client>;
}
impl InitPolkadotBlockBuilder for Client {
fn init_polkadot_block_builder(&self) -> BlockBuilder<Block, Client> {
let chain_info = self.chain_info();
self.init_polkadot_block_builder_at(chain_info.best_hash)
}
fn init_polkadot_block_builder_at(
&self,
hash: <Block as BlockT>::Hash,
) -> BlockBuilder<Block, Client> {
let last_timestamp =
self.runtime_api().get_last_timestamp(hash).expect("Get last timestamp");
let minimum_period = BasicExternalities::new_empty()
.execute_with(|| polkadot_test_runtime::MinimumPeriod::get());
let timestamp = if last_timestamp == 0 {
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("Time is always after UNIX_EPOCH; qed")
.as_millis() as u64
} else {
last_timestamp + minimum_period
};
let slot_duration = BasicExternalities::new_empty()
.execute_with(|| polkadot_test_runtime::SlotDuration::get());
let slot = (timestamp / slot_duration).into();
let digest = Digest {
logs: vec![DigestItem::PreRuntime(
BABE_ENGINE_ID,
PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 42 })
.encode(),
)],
};
let mut block_builder = BlockBuilderBuilder::new(self)
.on_parent_block(hash)
.fetch_parent_block_number(&self)
.expect("Fetches parent block number")
.with_inherent_digests(digest)
.build()
.expect("Creates new block builder for test runtime");
let mut inherent_data = sp_inherents::InherentData::new();
inherent_data
.put_data(sp_timestamp::INHERENT_IDENTIFIER, ×tamp)
.expect("Put timestamp inherent data");
let parent_header = self
.header(hash)
.expect("Get the parent block header")
.expect("The target block header must exist");
let parachains_inherent_data = ParachainsInherentData {
bitfields: Vec::new(),
backed_candidates: Vec::new(),
disputes: Vec::new(),
parent_header,
};
inherent_data
.put_data(
polkadot_primitives::PARACHAINS_INHERENT_IDENTIFIER,
¶chains_inherent_data,
)
.expect("Put parachains inherent data");
let inherents = block_builder.create_inherents(inherent_data).expect("Creates inherents");
inherents
.into_iter()
.for_each(|ext| block_builder.push(ext).expect("Pushes inherent"));
block_builder
}
}
pub trait BlockBuilderExt {
fn push_polkadot_extrinsic(
&mut self,
ext: UncheckedExtrinsic,
) -> Result<(), sp_blockchain::Error>;
}
impl BlockBuilderExt for BlockBuilder<'_, Block, Client> {
fn push_polkadot_extrinsic(
&mut self,
ext: UncheckedExtrinsic,
) -> Result<(), sp_blockchain::Error> {
let encoded = ext.encode();
self.push(
Decode::decode(&mut &encoded[..]).expect(
"The runtime specific extrinsic always decodes to an opaque extrinsic; qed",
),
)
}
}