use crate::{
author::{aura, runtime},
executor::host,
header,
verify::inherents,
};
use alloc::vec::Vec;
use core::{num::NonZero, time::Duration};
pub use runtime::{Nibble, StorageChanges, TrieEntryVersion};
pub struct Config<'a, TLocAuth> {
pub consensus: ConfigConsensus<'a, TLocAuth>,
}
pub enum ConfigConsensus<'a, TLocAuth> {
Aura {
now_from_unix_epoch: Duration,
slot_duration: NonZero<u64>,
current_authorities: header::AuraAuthoritiesIter<'a>,
local_authorities: TLocAuth,
},
}
#[must_use]
pub enum Builder {
Idle,
WaitSlot(WaitSlot),
Ready(AuthoringStart),
}
impl Builder {
pub fn new<'a>(config: Config<'a, impl Iterator<Item = &'a [u8; 32]>>) -> Self {
let (slot, ready): (WaitSlotConsensus, bool) = match config.consensus {
ConfigConsensus::Aura {
current_authorities,
local_authorities,
now_from_unix_epoch,
slot_duration,
} => {
let consensus = match aura::next_slot_claim(aura::Config {
now_from_unix_epoch,
slot_duration,
current_authorities,
local_authorities,
}) {
Some(c) => c,
None => return Builder::Idle,
};
debug_assert!(now_from_unix_epoch < consensus.slot_end_from_unix_epoch);
let ready = now_from_unix_epoch >= consensus.slot_start_from_unix_epoch;
(WaitSlotConsensus::Aura(consensus), ready)
}
};
if ready {
Builder::Ready(AuthoringStart { consensus: slot })
} else {
Builder::WaitSlot(WaitSlot { consensus: slot })
}
}
}
#[must_use]
pub enum BuilderAuthoring {
Error {
parent_runtime: host::HostVmPrototype,
error: Error,
},
ApplyExtrinsic(ApplyExtrinsic),
ApplyExtrinsicResult {
result: Result<Result<(), runtime::DispatchError>, runtime::TransactionValidityError>,
resume: ApplyExtrinsic,
},
StorageGet(StorageGet),
ClosestDescendantMerkleValue(ClosestDescendantMerkleValue),
NextKey(NextKey),
OffchainStorageSet(OffchainStorageSet),
Seal(Seal),
}
#[must_use]
#[derive(Debug)]
pub struct WaitSlot {
consensus: WaitSlotConsensus,
}
#[derive(Debug)]
enum WaitSlotConsensus {
Aura(aura::SlotClaim),
}
impl WaitSlot {
pub fn when(&self) -> Duration {
match self.consensus {
WaitSlotConsensus::Aura(claim) => claim.slot_start_from_unix_epoch,
}
}
pub fn start(self) -> AuthoringStart {
AuthoringStart {
consensus: self.consensus,
}
}
}
pub struct AuthoringStart {
consensus: WaitSlotConsensus,
}
impl AuthoringStart {
pub fn slot_start_from_unix_epoch(&self) -> Duration {
match self.consensus {
WaitSlotConsensus::Aura(claim) => claim.slot_start_from_unix_epoch,
}
}
pub fn slot_end_from_unix_epoch(&self) -> Duration {
match self.consensus {
WaitSlotConsensus::Aura(claim) => claim.slot_end_from_unix_epoch,
}
}
pub fn start(self, config: AuthoringStartConfig) -> BuilderAuthoring {
let inner_block_build = runtime::build_block(runtime::Config {
block_number_bytes: config.block_number_bytes,
parent_hash: config.parent_hash,
parent_number: config.parent_number,
parent_runtime: config.parent_runtime,
block_body_capacity: config.block_body_capacity,
consensus_digest_log_item: match self.consensus {
WaitSlotConsensus::Aura(slot) => {
runtime::ConfigPreRuntime::Aura(header::AuraPreDigest {
slot_number: slot.slot_number,
})
}
},
max_log_level: config.max_log_level,
calculate_trie_changes: config.calculate_trie_changes,
});
let inherent_data = inherents::InherentData {
timestamp: u64::try_from(config.now_from_unix_epoch.as_millis()).unwrap_or(u64::MAX),
};
(Shared {
inherent_data: Some(inherent_data),
slot_claim: self.consensus,
block_number_bytes: config.block_number_bytes,
})
.with_runtime_inner(inner_block_build)
}
}
pub struct AuthoringStartConfig<'a> {
pub block_number_bytes: usize,
pub parent_hash: &'a [u8; 32],
pub parent_number: u64,
pub now_from_unix_epoch: Duration,
pub parent_runtime: host::HostVmPrototype,
pub block_body_capacity: usize,
pub max_log_level: u32,
pub calculate_trie_changes: bool,
}
#[must_use]
pub struct ApplyExtrinsic {
inner: runtime::ApplyExtrinsic,
shared: Shared,
}
impl ApplyExtrinsic {
pub fn add_extrinsic(self, extrinsic: Vec<u8>) -> BuilderAuthoring {
self.shared
.with_runtime_inner(self.inner.add_extrinsic(extrinsic))
}
pub fn finish(self) -> BuilderAuthoring {
self.shared.with_runtime_inner(self.inner.finish())
}
}
#[must_use]
pub struct StorageGet(runtime::StorageGet, Shared);
impl StorageGet {
pub fn key(&self) -> impl AsRef<[u8]> {
self.0.key()
}
pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
self.0.child_trie()
}
pub fn inject_value(
self,
value: Option<(impl Iterator<Item = impl AsRef<[u8]>>, TrieEntryVersion)>,
) -> BuilderAuthoring {
self.1.with_runtime_inner(self.0.inject_value(value))
}
}
#[must_use]
pub struct ClosestDescendantMerkleValue(runtime::ClosestDescendantMerkleValue, Shared);
impl ClosestDescendantMerkleValue {
pub fn key(&self) -> impl Iterator<Item = Nibble> {
self.0.key()
}
pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
self.0.child_trie()
}
pub fn resume_unknown(self) -> BuilderAuthoring {
self.1.with_runtime_inner(self.0.resume_unknown())
}
pub fn inject_merkle_value(self, merkle_value: Option<&[u8]>) -> BuilderAuthoring {
self.1
.with_runtime_inner(self.0.inject_merkle_value(merkle_value))
}
}
#[must_use]
pub struct NextKey(runtime::NextKey, Shared);
impl NextKey {
pub fn key(&self) -> impl Iterator<Item = Nibble> {
self.0.key()
}
pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
self.0.child_trie()
}
pub fn or_equal(&self) -> bool {
self.0.or_equal()
}
pub fn branch_nodes(&self) -> bool {
self.0.branch_nodes()
}
pub fn prefix(&self) -> impl Iterator<Item = Nibble> {
self.0.prefix()
}
pub fn inject_key(self, key: Option<impl Iterator<Item = Nibble>>) -> BuilderAuthoring {
self.1.with_runtime_inner(self.0.inject_key(key))
}
}
#[must_use]
pub struct OffchainStorageSet(runtime::OffchainStorageSet, Shared);
impl OffchainStorageSet {
pub fn key(&self) -> impl AsRef<[u8]> {
self.0.key()
}
pub fn value(&self) -> Option<impl AsRef<[u8]>> {
self.0.value()
}
pub fn resume(self) -> BuilderAuthoring {
self.1.with_runtime_inner(self.0.resume())
}
}
#[must_use]
pub struct Seal {
shared: Shared,
block: runtime::Success,
}
impl Seal {
pub fn scale_encoded_header(&self) -> &[u8] {
&self.block.scale_encoded_header
}
pub fn to_sign(&self) -> [u8; 32] {
header::hash_from_scale_encoded_header(&self.block.scale_encoded_header)
}
pub fn authority_index(&self) -> usize {
match self.shared.slot_claim {
WaitSlotConsensus::Aura(slot) => slot.local_authorities_index,
}
}
pub fn inject_sr25519_signature(mut self, signature: [u8; 64]) -> runtime::Success {
let header = header::decode(
&self.block.scale_encoded_header,
self.shared.block_number_bytes,
)
.unwrap();
self.block.scale_encoded_header = header
.scale_encoding_with_extra_digest_item(
self.shared.block_number_bytes,
header::DigestItemRef::AuraSeal(&signature),
)
.fold(Vec::with_capacity(8192), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
self.block
}
}
#[derive(Debug, derive_more::Display, derive_more::Error, derive_more::From)]
pub enum Error {
#[display("{_0}")]
Runtime(runtime::Error),
#[from(ignore)]
InvalidHeaderGenerated,
}
#[derive(Debug)]
struct Shared {
inherent_data: Option<inherents::InherentData>,
block_number_bytes: usize,
slot_claim: WaitSlotConsensus,
}
impl Shared {
fn with_runtime_inner(mut self, mut inner: runtime::BlockBuild) -> BuilderAuthoring {
loop {
match inner {
runtime::BlockBuild::Finished(Ok(block)) => {
let decoded_header = match header::decode(
&block.scale_encoded_header,
self.block_number_bytes,
) {
Ok(h) => h,
Err(_) => {
break BuilderAuthoring::Error {
parent_runtime: block.parent_runtime,
error: Error::InvalidHeaderGenerated,
};
}
};
if decoded_header.digest.aura_seal().is_some()
|| decoded_header.digest.babe_seal().is_some()
{
break BuilderAuthoring::Error {
parent_runtime: block.parent_runtime,
error: Error::InvalidHeaderGenerated,
};
}
break BuilderAuthoring::Seal(Seal {
shared: self,
block,
});
}
runtime::BlockBuild::Finished(Err((error, parent_runtime))) => {
break BuilderAuthoring::Error {
parent_runtime,
error: Error::Runtime(error),
};
}
runtime::BlockBuild::InherentExtrinsics(a) => {
inner = a.inject_inherents(self.inherent_data.take().unwrap());
}
runtime::BlockBuild::ApplyExtrinsic(a) => {
inner = a.finish();
}
runtime::BlockBuild::ApplyExtrinsicResult { result, resume } => {
break BuilderAuthoring::ApplyExtrinsicResult {
result,
resume: ApplyExtrinsic {
inner: resume,
shared: self,
},
};
}
runtime::BlockBuild::StorageGet(inner) => {
break BuilderAuthoring::StorageGet(StorageGet(inner, self));
}
runtime::BlockBuild::ClosestDescendantMerkleValue(inner) => {
break BuilderAuthoring::ClosestDescendantMerkleValue(
ClosestDescendantMerkleValue(inner, self),
);
}
runtime::BlockBuild::NextKey(inner) => {
break BuilderAuthoring::NextKey(NextKey(inner, self));
}
runtime::BlockBuild::OffchainStorageSet(inner) => {
break BuilderAuthoring::OffchainStorageSet(OffchainStorageSet(inner, self));
}
}
}
}
}