use crate::{trie, util};
use alloc::{vec, vec::Vec};
use core::{fmt, iter, slice};
mod aura;
mod babe;
mod grandpa;
mod tests;
pub use aura::*;
pub use babe::*;
pub use grandpa::*;
pub fn hash_from_scale_encoded_header(header: impl AsRef<[u8]>) -> [u8; 32] {
hash_from_scale_encoded_header_vectored(iter::once(header))
}
pub fn hash_from_scale_encoded_header_vectored(
header: impl Iterator<Item = impl AsRef<[u8]>>,
) -> [u8; 32] {
let mut hasher = blake2_rfc::blake2b::Blake2b::with_key(32, &[]);
for buf in header {
hasher.update(buf.as_ref());
}
let result = hasher.finalize();
debug_assert_eq!(result.as_bytes().len(), 32);
let mut out = [0; 32];
out.copy_from_slice(result.as_bytes());
out
}
pub fn extrinsics_root(transactions: &[impl AsRef<[u8]>]) -> [u8; 32] {
trie::ordered_root(
trie::TrieEntryVersion::V0,
trie::HashFunction::Blake2,
transactions,
)
}
pub fn decode(scale_encoded: &'_ [u8], block_number_bytes: usize) -> Result<HeaderRef<'_>, Error> {
let (header, remainder) = decode_partial(scale_encoded, block_number_bytes)?;
if !remainder.is_empty() {
return Err(Error::TooLong);
}
Ok(header)
}
pub fn decode_partial(
mut scale_encoded: &'_ [u8],
block_number_bytes: usize,
) -> Result<(HeaderRef<'_>, &'_ [u8]), Error> {
if scale_encoded.len() < 32 + 1 {
return Err(Error::TooShort);
}
let parent_hash: &[u8; 32] = TryFrom::try_from(&scale_encoded[0..32]).unwrap();
scale_encoded = &scale_encoded[32..];
let (mut scale_encoded, number) =
crate::util::nom_scale_compact_u64::<nom::error::Error<&[u8]>>(scale_encoded)
.map_err(|_| Error::BlockNumberDecodeError)?;
if scale_encoded.len() < 32 + 32 + 1 {
return Err(Error::TooShort);
}
let state_root: &[u8; 32] = TryFrom::try_from(&scale_encoded[0..32]).unwrap();
scale_encoded = &scale_encoded[32..];
let extrinsics_root: &[u8; 32] = TryFrom::try_from(&scale_encoded[0..32]).unwrap();
scale_encoded = &scale_encoded[32..];
let (digest, remainder) = DigestRef::from_scale_bytes(scale_encoded, block_number_bytes)?;
let header = HeaderRef {
parent_hash,
number,
state_root,
extrinsics_root,
digest,
};
Ok((header, remainder))
}
#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
pub enum Error {
TooShort,
TooLong,
BlockNumberDecodeError,
DigestLenDecodeError,
DigestItemLenDecodeError,
DigestItemDecodeError,
#[display("Digest log with an unrecognized type {unknown_type}")]
UnknownDigestLogType {
unknown_type: u8,
},
SealIsntLastItem,
BadAuraSealLength,
BadAuraConsensusRefType,
BadAuraAuthoritiesListLen,
MultipleAuraPreRuntimeDigests,
BadBabeSealLength,
BadBabePreDigestRefType,
BadBabeConsensusRefType,
BadBabeNextConfigVersion,
MultipleBabePreRuntimeDigests,
MultipleBabeEpochDescriptors,
MultipleBabeConfigDescriptors,
MutipleRuntimeEnvironmentUpdated,
UnexpectedBabeConfigDescriptor,
GrandpaConsensusLogDecodeError,
PowIdeologicallyNotSupported,
}
#[derive(Debug, Clone)]
pub struct HeaderRef<'a> {
pub parent_hash: &'a [u8; 32],
pub number: u64,
pub state_root: &'a [u8; 32],
pub extrinsics_root: &'a [u8; 32],
pub digest: DigestRef<'a>,
}
impl<'a> HeaderRef<'a> {
pub fn scale_encoding(
&self,
block_number_bytes: usize,
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + use<'a>> + Clone + use<'a> {
self.scale_encoding_before_digest().map(either::Left).chain(
self.digest
.scale_encoding(block_number_bytes)
.map(either::Right),
)
}
pub fn scale_encoding_with_extra_digest_item(
&self,
block_number_bytes: usize,
extra_item: DigestItemRef<'a>,
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + use<'a>> + Clone + use<'a> {
self.scale_encoding_before_digest().map(either::Left).chain(
self.digest
.scale_encoding_with_extra_item(block_number_bytes, extra_item)
.map(either::Right),
)
}
fn scale_encoding_before_digest(
&self,
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + use<'a>> + Clone + use<'a> {
iter::once(either::Left(&self.parent_hash[..]))
.chain(iter::once(either::Right(util::encode_scale_compact_u64(
self.number,
))))
.chain(iter::once(either::Left(&self.state_root[..])))
.chain(iter::once(either::Left(&self.extrinsics_root[..])))
}
pub fn scale_encoding_vec(&self, block_number_bytes: usize) -> Vec<u8> {
const CAP: usize = 1024;
self.scale_encoding(block_number_bytes)
.fold(Vec::with_capacity(CAP), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
})
}
pub fn hash(&self, block_number_bytes: usize) -> [u8; 32] {
hash_from_scale_encoded_header_vectored(self.scale_encoding(block_number_bytes))
}
}
impl<'a> From<&'a Header> for HeaderRef<'a> {
fn from(a: &'a Header) -> HeaderRef<'a> {
HeaderRef {
parent_hash: &a.parent_hash,
number: a.number,
state_root: &a.state_root,
extrinsics_root: &a.extrinsics_root,
digest: (&a.digest).into(),
}
}
}
#[derive(Debug, Clone)]
pub struct Header {
pub parent_hash: [u8; 32],
pub number: u64,
pub state_root: [u8; 32],
pub extrinsics_root: [u8; 32],
pub digest: Digest,
}
impl Header {
pub fn scale_encoding(
&self,
block_number_bytes: usize,
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone> + Clone {
HeaderRef::from(self).scale_encoding(block_number_bytes)
}
pub fn scale_encoding_vec(&self, block_number_bytes: usize) -> Vec<u8> {
HeaderRef::from(self).scale_encoding_vec(block_number_bytes)
}
pub fn hash(&self, block_number_bytes: usize) -> [u8; 32] {
HeaderRef::from(self).hash(block_number_bytes)
}
}
impl<'a> From<HeaderRef<'a>> for Header {
fn from(a: HeaderRef<'a>) -> Header {
Header {
parent_hash: *a.parent_hash,
number: a.number,
state_root: *a.state_root,
extrinsics_root: *a.extrinsics_root,
digest: a.digest.into(),
}
}
}
#[derive(Clone)]
pub struct DigestRef<'a> {
inner: DigestRefInner<'a>,
aura_seal_index: Option<usize>,
aura_predigest_index: Option<usize>,
babe_seal_index: Option<usize>,
babe_predigest_index: Option<usize>,
babe_next_epoch_data_index: Option<usize>,
babe_next_config_data_index: Option<usize>,
has_runtime_environment_updated: bool,
}
#[derive(Clone)]
enum DigestRefInner<'a> {
Undecoded {
digest_logs_len: usize,
digest: &'a [u8],
block_number_bytes: usize,
},
Parsed(&'a [DigestItem]),
}
impl<'a> DigestRef<'a> {
pub fn empty() -> DigestRef<'a> {
DigestRef {
inner: DigestRefInner::Parsed(&[]),
aura_seal_index: None,
aura_predigest_index: None,
babe_seal_index: None,
babe_predigest_index: None,
babe_next_epoch_data_index: None,
babe_next_config_data_index: None,
has_runtime_environment_updated: false,
}
}
pub fn has_any_aura(&self) -> bool {
self.logs().any(|l| l.is_aura())
}
pub fn has_any_babe(&self) -> bool {
self.logs().any(|l| l.is_babe())
}
pub fn has_any_grandpa(&self) -> bool {
self.logs().any(|l| l.is_grandpa())
}
pub fn aura_seal(&self) -> Option<&'a [u8; 64]> {
if let Some(aura_seal_index) = self.aura_seal_index {
if let DigestItemRef::AuraSeal(seal) = self.logs().nth(aura_seal_index).unwrap() {
Some(seal)
} else {
unreachable!()
}
} else {
None
}
}
pub fn aura_pre_runtime(&self) -> Option<AuraPreDigest> {
if let Some(aura_predigest_index) = self.aura_predigest_index {
if let DigestItemRef::AuraPreDigest(item) =
self.logs().nth(aura_predigest_index).unwrap()
{
Some(item)
} else {
unreachable!()
}
} else {
None
}
}
pub fn babe_seal(&self) -> Option<&'a [u8; 64]> {
if let Some(babe_seal_index) = self.babe_seal_index {
if let DigestItemRef::BabeSeal(seal) = self.logs().nth(babe_seal_index).unwrap() {
Some(seal)
} else {
unreachable!()
}
} else {
None
}
}
pub fn babe_pre_runtime(&self) -> Option<BabePreDigestRef<'a>> {
if let Some(babe_predigest_index) = self.babe_predigest_index {
if let DigestItemRef::BabePreDigest(item) =
self.logs().nth(babe_predigest_index).unwrap()
{
Some(item)
} else {
unreachable!()
}
} else {
None
}
}
pub fn babe_epoch_information(&self) -> Option<(BabeNextEpochRef<'a>, Option<BabeNextConfig>)> {
if let Some(babe_next_epoch_data_index) = self.babe_next_epoch_data_index {
if let DigestItemRef::BabeConsensus(BabeConsensusLogRef::NextEpochData(epoch)) =
self.logs().nth(babe_next_epoch_data_index).unwrap()
{
if let Some(babe_next_config_data_index) = self.babe_next_config_data_index {
if let DigestItemRef::BabeConsensus(BabeConsensusLogRef::NextConfigData(
config,
)) = self.logs().nth(babe_next_config_data_index).unwrap()
{
Some((epoch, Some(config)))
} else {
panic!()
}
} else {
Some((epoch, None))
}
} else {
unreachable!()
}
} else {
debug_assert!(self.babe_next_config_data_index.is_none());
None
}
}
pub fn has_runtime_environment_updated(&self) -> bool {
self.has_runtime_environment_updated
}
pub fn pop_seal(&mut self) -> Option<Seal<'a>> {
let seal_pos = self.babe_seal_index.or(self.aura_seal_index)?;
match &mut self.inner {
DigestRefInner::Parsed(list) => {
debug_assert!(!list.is_empty());
debug_assert_eq!(seal_pos, list.len() - 1);
let item = &list[seal_pos];
*list = &list[..seal_pos];
match item {
DigestItem::AuraSeal(seal) => Some(Seal::Aura(seal)),
DigestItem::BabeSeal(seal) => Some(Seal::Babe(seal)),
_ => unreachable!(),
}
}
DigestRefInner::Undecoded {
digest,
digest_logs_len,
block_number_bytes,
} => {
debug_assert_eq!(seal_pos, *digest_logs_len - 1);
let mut iter = LogsIter {
inner: LogsIterInner::Undecoded {
pointer: digest,
remaining_len: *digest_logs_len,
block_number_bytes: *block_number_bytes,
},
};
for _ in 0..seal_pos {
let _item = iter.next();
debug_assert!(_item.is_some());
}
if let LogsIterInner::Undecoded {
pointer,
remaining_len,
..
} = iter.inner
{
*digest_logs_len -= 1;
*digest = &digest[..digest.len() - pointer.len()];
self.babe_seal_index = None;
debug_assert_eq!(remaining_len, 1);
} else {
unreachable!()
}
match iter.next() {
Some(DigestItemRef::AuraSeal(seal)) => Some(Seal::Aura(seal)),
Some(DigestItemRef::BabeSeal(seal)) => Some(Seal::Babe(seal)),
_ => unreachable!(),
}
}
}
}
pub fn logs(&self) -> LogsIter<'a> {
LogsIter {
inner: match self.inner {
DigestRefInner::Parsed(list) => LogsIterInner::Decoded(list.iter()),
DigestRefInner::Undecoded {
digest,
digest_logs_len,
block_number_bytes,
} => LogsIterInner::Undecoded {
pointer: digest,
remaining_len: digest_logs_len,
block_number_bytes,
},
},
}
}
pub fn scale_encoding(
&self,
block_number_bytes: usize,
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + use<'a>> + Clone + use<'a> {
let encoded_len = util::encode_scale_compact_usize(self.logs().len());
iter::once(either::Left(encoded_len)).chain(
self.logs()
.flat_map(move |v| v.scale_encoding(block_number_bytes).map(either::Right)),
)
}
pub fn scale_encoding_with_extra_item(
&self,
block_number_bytes: usize,
extra_item: DigestItemRef<'a>,
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + use<'a>> + Clone + use<'a> {
let new_len = self.logs().len() + 1;
let encoded_len = util::encode_scale_compact_usize(new_len);
iter::once(either::Left(encoded_len)).chain(
self.logs()
.chain(iter::once(extra_item))
.flat_map(move |v| v.scale_encoding(block_number_bytes).map(either::Right)),
)
}
pub fn from_slice(slice: &'a [DigestItem]) -> Result<Self, Error> {
let mut aura_seal_index = None;
let mut aura_predigest_index = None;
let mut babe_seal_index = None;
let mut babe_predigest_index = None;
let mut babe_next_epoch_data_index = None;
let mut babe_next_config_data_index = None;
let mut has_runtime_environment_updated = false;
for (item_num, item) in slice.iter().enumerate() {
match item {
DigestItem::AuraPreDigest(_) if aura_predigest_index.is_none() => {
aura_predigest_index = Some(item_num);
}
DigestItem::AuraPreDigest(_) => return Err(Error::MultipleAuraPreRuntimeDigests),
DigestItem::AuraConsensus(_) => {}
DigestItem::BabePreDigest(_) if babe_predigest_index.is_none() => {
babe_predigest_index = Some(item_num);
}
DigestItem::BabePreDigest(_) => return Err(Error::MultipleBabePreRuntimeDigests),
DigestItem::BabeConsensus(BabeConsensusLog::NextEpochData(_))
if babe_next_epoch_data_index.is_none() =>
{
babe_next_epoch_data_index = Some(item_num);
}
DigestItem::BabeConsensus(BabeConsensusLog::NextEpochData(_)) => {
return Err(Error::MultipleBabeEpochDescriptors);
}
DigestItem::BabeConsensus(BabeConsensusLog::NextConfigData(_))
if babe_next_config_data_index.is_none() =>
{
babe_next_config_data_index = Some(item_num);
}
DigestItem::BabeConsensus(BabeConsensusLog::NextConfigData(_)) => {
return Err(Error::MultipleBabeConfigDescriptors);
}
DigestItem::BabeConsensus(BabeConsensusLog::OnDisabled(_)) => {}
DigestItem::GrandpaConsensus(_) => {}
DigestItem::AuraSeal(_) if item_num == slice.len() - 1 => {
debug_assert!(aura_seal_index.is_none());
debug_assert!(babe_seal_index.is_none());
aura_seal_index = Some(item_num);
}
DigestItem::AuraSeal(_) => return Err(Error::SealIsntLastItem),
DigestItem::BabeSeal(_) if item_num == slice.len() - 1 => {
debug_assert!(aura_seal_index.is_none());
debug_assert!(babe_seal_index.is_none());
babe_seal_index = Some(item_num);
}
DigestItem::RuntimeEnvironmentUpdated if has_runtime_environment_updated => {
return Err(Error::MutipleRuntimeEnvironmentUpdated);
}
DigestItem::RuntimeEnvironmentUpdated => {
has_runtime_environment_updated = true;
}
DigestItem::BabeSeal(_) => return Err(Error::SealIsntLastItem),
DigestItem::UnknownSeal { .. } if item_num == slice.len() - 1 => {
debug_assert!(aura_seal_index.is_none());
debug_assert!(babe_seal_index.is_none());
}
DigestItem::UnknownSeal { .. } => return Err(Error::SealIsntLastItem),
DigestItem::UnknownConsensus { .. }
| DigestItem::UnknownPreRuntime { .. }
| DigestItem::Other(..) => {}
}
}
if babe_next_config_data_index.is_some() && babe_next_epoch_data_index.is_none() {
return Err(Error::UnexpectedBabeConfigDescriptor);
}
Ok(DigestRef {
inner: DigestRefInner::Parsed(slice),
aura_seal_index,
aura_predigest_index,
babe_seal_index,
babe_predigest_index,
babe_next_epoch_data_index,
babe_next_config_data_index,
has_runtime_environment_updated,
})
}
fn from_scale_bytes(
scale_encoded: &'a [u8],
block_number_bytes: usize,
) -> Result<(Self, &'a [u8]), Error> {
let (scale_encoded, digest_logs_len) =
crate::util::nom_scale_compact_usize::<nom::error::Error<&[u8]>>(scale_encoded)
.map_err(|_| Error::DigestItemLenDecodeError)?;
let mut aura_seal_index = None;
let mut aura_predigest_index = None;
let mut babe_seal_index = None;
let mut babe_predigest_index = None;
let mut babe_next_epoch_data_index = None;
let mut babe_next_config_data_index = None;
let mut has_runtime_environment_updated = false;
let mut next_digest = scale_encoded;
for item_num in 0..digest_logs_len {
let (item, next) = decode_item(next_digest, block_number_bytes)?;
next_digest = next;
match item {
DigestItemRef::AuraPreDigest(_) if aura_predigest_index.is_none() => {
aura_predigest_index = Some(item_num);
}
DigestItemRef::AuraPreDigest(_) => {
return Err(Error::MultipleAuraPreRuntimeDigests);
}
DigestItemRef::AuraConsensus(_) => {}
DigestItemRef::BabePreDigest(_) if babe_predigest_index.is_none() => {
babe_predigest_index = Some(item_num);
}
DigestItemRef::BabePreDigest(_) => {
return Err(Error::MultipleBabePreRuntimeDigests);
}
DigestItemRef::BabeConsensus(BabeConsensusLogRef::NextEpochData(_))
if babe_next_epoch_data_index.is_none() =>
{
babe_next_epoch_data_index = Some(item_num);
}
DigestItemRef::BabeConsensus(BabeConsensusLogRef::NextEpochData(_)) => {
return Err(Error::MultipleBabeEpochDescriptors);
}
DigestItemRef::BabeConsensus(BabeConsensusLogRef::NextConfigData(_))
if babe_next_config_data_index.is_none() =>
{
babe_next_config_data_index = Some(item_num);
}
DigestItemRef::BabeConsensus(BabeConsensusLogRef::NextConfigData(_)) => {
return Err(Error::MultipleBabeConfigDescriptors);
}
DigestItemRef::BabeConsensus(BabeConsensusLogRef::OnDisabled(_)) => {}
DigestItemRef::GrandpaConsensus(_) => {}
DigestItemRef::AuraSeal(_) if item_num == digest_logs_len - 1 => {
debug_assert!(aura_seal_index.is_none());
debug_assert!(babe_seal_index.is_none());
aura_seal_index = Some(item_num);
}
DigestItemRef::AuraSeal(_) => return Err(Error::SealIsntLastItem),
DigestItemRef::BabeSeal(_) if item_num == digest_logs_len - 1 => {
debug_assert!(aura_seal_index.is_none());
debug_assert!(babe_seal_index.is_none());
babe_seal_index = Some(item_num);
}
DigestItemRef::RuntimeEnvironmentUpdated if has_runtime_environment_updated => {
return Err(Error::MutipleRuntimeEnvironmentUpdated);
}
DigestItemRef::RuntimeEnvironmentUpdated => {
has_runtime_environment_updated = true;
}
DigestItemRef::BabeSeal(_) => return Err(Error::SealIsntLastItem),
DigestItemRef::UnknownSeal { .. } if item_num == digest_logs_len - 1 => {
debug_assert!(aura_seal_index.is_none());
debug_assert!(babe_seal_index.is_none());
}
DigestItemRef::UnknownSeal { .. } => return Err(Error::SealIsntLastItem),
DigestItemRef::UnknownConsensus { .. }
| DigestItemRef::UnknownPreRuntime { .. }
| DigestItemRef::Other { .. } => {}
}
}
if babe_next_config_data_index.is_some() && babe_next_epoch_data_index.is_none() {
return Err(Error::UnexpectedBabeConfigDescriptor);
}
let out = DigestRef {
inner: DigestRefInner::Undecoded {
digest_logs_len,
digest: scale_encoded,
block_number_bytes,
},
aura_seal_index,
aura_predigest_index,
babe_seal_index,
babe_predigest_index,
babe_next_epoch_data_index,
babe_next_config_data_index,
has_runtime_environment_updated,
};
Ok((out, next_digest))
}
}
impl<'a> fmt::Debug for DigestRef<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.logs()).finish()
}
}
impl<'a> From<&'a Digest> for DigestRef<'a> {
fn from(digest: &'a Digest) -> DigestRef<'a> {
DigestRef {
inner: DigestRefInner::Parsed(&digest.list),
aura_seal_index: digest.aura_seal_index,
aura_predigest_index: digest.aura_predigest_index,
babe_seal_index: digest.babe_seal_index,
babe_predigest_index: digest.babe_predigest_index,
babe_next_epoch_data_index: digest.babe_next_epoch_data_index,
babe_next_config_data_index: digest.babe_next_config_data_index,
has_runtime_environment_updated: digest.has_runtime_environment_updated,
}
}
}
pub enum Seal<'a> {
Aura(&'a [u8; 64]),
Babe(&'a [u8; 64]),
}
#[derive(Clone)]
pub struct Digest {
list: Vec<DigestItem>,
aura_seal_index: Option<usize>,
aura_predigest_index: Option<usize>,
babe_seal_index: Option<usize>,
babe_predigest_index: Option<usize>,
babe_next_epoch_data_index: Option<usize>,
babe_next_config_data_index: Option<usize>,
has_runtime_environment_updated: bool,
}
impl Digest {
pub fn logs(&'_ self) -> LogsIter<'_> {
DigestRef::from(self).logs()
}
pub fn aura_seal(&self) -> Option<&[u8; 64]> {
DigestRef::from(self).aura_seal()
}
pub fn babe_seal(&self) -> Option<&[u8; 64]> {
DigestRef::from(self).babe_seal()
}
pub fn babe_pre_runtime(&'_ self) -> Option<BabePreDigestRef<'_>> {
DigestRef::from(self).babe_pre_runtime()
}
pub fn babe_epoch_information(
&'_ self,
) -> Option<(BabeNextEpochRef<'_>, Option<BabeNextConfig>)> {
DigestRef::from(self).babe_epoch_information()
}
pub fn has_runtime_environment_updated(&self) -> bool {
self.has_runtime_environment_updated
}
}
impl fmt::Debug for Digest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.list.iter().map(DigestItemRef::from))
.finish()
}
}
impl<'a> From<DigestRef<'a>> for Digest {
fn from(digest: DigestRef<'a>) -> Digest {
Digest {
list: digest.logs().map(Into::into).collect(),
aura_seal_index: digest.aura_seal_index,
aura_predigest_index: digest.aura_predigest_index,
babe_seal_index: digest.babe_seal_index,
babe_predigest_index: digest.babe_predigest_index,
babe_next_epoch_data_index: digest.babe_next_epoch_data_index,
babe_next_config_data_index: digest.babe_next_config_data_index,
has_runtime_environment_updated: digest.has_runtime_environment_updated,
}
}
}
#[derive(Clone)]
pub struct LogsIter<'a> {
inner: LogsIterInner<'a>,
}
#[derive(Clone)]
enum LogsIterInner<'a> {
Decoded(slice::Iter<'a, DigestItem>),
Undecoded {
pointer: &'a [u8],
remaining_len: usize,
block_number_bytes: usize,
},
}
impl<'a> Iterator for LogsIter<'a> {
type Item = DigestItemRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
LogsIterInner::Decoded(iter) => iter.next().map(Into::into),
LogsIterInner::Undecoded {
pointer,
remaining_len,
block_number_bytes,
} => {
if *remaining_len == 0 {
return None;
}
let (item, new_pointer) = decode_item(pointer, *block_number_bytes).unwrap();
*pointer = new_pointer;
*remaining_len -= 1;
Some(item)
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.inner {
LogsIterInner::Decoded(iter) => iter.size_hint(),
LogsIterInner::Undecoded { remaining_len, .. } => {
(*remaining_len, Some(*remaining_len))
}
}
}
}
impl<'a> ExactSizeIterator for LogsIter<'a> {}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum DigestItemRef<'a> {
AuraPreDigest(AuraPreDigest),
AuraSeal(&'a [u8; 64]),
AuraConsensus(AuraConsensusLogRef<'a>),
BabePreDigest(BabePreDigestRef<'a>),
BabeConsensus(BabeConsensusLogRef<'a>),
BabeSeal(&'a [u8; 64]),
GrandpaConsensus(GrandpaConsensusLogRef<'a>),
UnknownConsensus {
engine: [u8; 4],
opaque: &'a [u8],
},
UnknownPreRuntime {
engine: [u8; 4],
opaque: &'a [u8],
},
UnknownSeal {
engine: [u8; 4],
opaque: &'a [u8],
},
Other(&'a [u8]),
RuntimeEnvironmentUpdated,
}
impl<'a> DigestItemRef<'a> {
pub fn is_aura(&self) -> bool {
matches!(
self,
DigestItemRef::AuraPreDigest(_)
| DigestItemRef::AuraSeal(_)
| DigestItemRef::AuraConsensus(_)
)
}
pub fn is_babe(&self) -> bool {
matches!(
self,
DigestItemRef::BabePreDigest(_)
| DigestItemRef::BabeConsensus(_)
| DigestItemRef::BabeSeal(_)
)
}
pub fn is_grandpa(&self) -> bool {
matches!(self, DigestItemRef::GrandpaConsensus(_))
}
pub fn from_scale_encoded(bytes: &'a [u8], block_number_bytes: usize) -> Result<Self, Error> {
let (item, remain) = decode_item(bytes, block_number_bytes)?;
if !remain.is_empty() {
return Err(Error::TooLong);
}
Ok(item)
}
pub fn scale_encoding(
&self,
block_number_bytes: usize,
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + use<'a>> + Clone + use<'a> {
let (item1, item2) = match *self {
DigestItemRef::AuraPreDigest(ref aura_pre_digest) => {
let encoded = aura_pre_digest
.scale_encoding()
.fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
let mut ret = Vec::with_capacity(12);
ret.push(6);
ret.extend_from_slice(b"aura");
ret.extend_from_slice(util::encode_scale_compact_usize(encoded.len()).as_ref());
(ret, either::Left(encoded))
}
DigestItemRef::AuraSeal(seal) => {
let mut ret = Vec::with_capacity(12);
ret.push(5);
ret.extend_from_slice(b"aura");
ret.extend_from_slice(util::encode_scale_compact_usize(64).as_ref());
(ret, either::Right(&seal[..]))
}
DigestItemRef::AuraConsensus(ref aura_consensus) => {
let encoded = aura_consensus
.scale_encoding()
.fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
let mut ret = Vec::with_capacity(12);
ret.push(4);
ret.extend_from_slice(b"aura");
ret.extend_from_slice(util::encode_scale_compact_usize(encoded.len()).as_ref());
(ret, either::Left(encoded))
}
DigestItemRef::BabePreDigest(ref babe_pre_digest) => {
let encoded = babe_pre_digest
.scale_encoding()
.fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
let mut ret = Vec::with_capacity(12);
ret.push(6);
ret.extend_from_slice(b"BABE");
ret.extend_from_slice(util::encode_scale_compact_usize(encoded.len()).as_ref());
(ret, either::Left(encoded))
}
DigestItemRef::BabeConsensus(ref babe_consensus) => {
let encoded = babe_consensus
.scale_encoding()
.fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
let mut ret = Vec::with_capacity(12);
ret.push(4);
ret.extend_from_slice(b"BABE");
ret.extend_from_slice(util::encode_scale_compact_usize(encoded.len()).as_ref());
(ret, either::Left(encoded))
}
DigestItemRef::GrandpaConsensus(ref gp_consensus) => {
let encoded =
gp_consensus
.scale_encoding(block_number_bytes)
.fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
let mut ret = Vec::with_capacity(12);
ret.push(4);
ret.extend_from_slice(b"FRNK");
ret.extend_from_slice(util::encode_scale_compact_usize(encoded.len()).as_ref());
(ret, either::Left(encoded))
}
DigestItemRef::BabeSeal(seal) => {
let mut ret = Vec::with_capacity(12);
ret.push(5);
ret.extend_from_slice(b"BABE");
ret.extend_from_slice(util::encode_scale_compact_usize(64).as_ref());
(ret, either::Right(&seal[..]))
}
DigestItemRef::UnknownConsensus { engine, opaque } => {
let mut ret = Vec::with_capacity(12);
ret.push(4);
ret.extend_from_slice(&engine);
ret.extend_from_slice(util::encode_scale_compact_usize(opaque.len()).as_ref());
(ret, either::Right(opaque))
}
DigestItemRef::UnknownSeal { engine, opaque } => {
let mut ret = Vec::with_capacity(12);
ret.push(5);
ret.extend_from_slice(&engine);
ret.extend_from_slice(util::encode_scale_compact_usize(opaque.len()).as_ref());
(ret, either::Right(opaque))
}
DigestItemRef::UnknownPreRuntime { engine, opaque } => {
let mut ret = Vec::with_capacity(12);
ret.push(6);
ret.extend_from_slice(&engine);
ret.extend_from_slice(util::encode_scale_compact_usize(opaque.len()).as_ref());
(ret, either::Right(opaque))
}
DigestItemRef::Other(raw) => {
let mut ret = Vec::with_capacity(12);
ret.push(0);
ret.extend_from_slice(util::encode_scale_compact_usize(raw.len()).as_ref());
(ret, either::Right(raw))
}
DigestItemRef::RuntimeEnvironmentUpdated => (vec![8], either::Right(&[][..])),
};
[either::Left(item1), item2].into_iter()
}
}
impl<'a> From<&'a DigestItem> for DigestItemRef<'a> {
fn from(a: &'a DigestItem) -> DigestItemRef<'a> {
match a {
DigestItem::AuraPreDigest(v) => DigestItemRef::AuraPreDigest(v.clone()),
DigestItem::AuraConsensus(v) => DigestItemRef::AuraConsensus(v.into()),
DigestItem::AuraSeal(v) => DigestItemRef::AuraSeal(v),
DigestItem::BabePreDigest(v) => DigestItemRef::BabePreDigest(v.into()),
DigestItem::BabeConsensus(v) => DigestItemRef::BabeConsensus(v.into()),
DigestItem::BabeSeal(v) => DigestItemRef::BabeSeal(v),
DigestItem::GrandpaConsensus(v) => DigestItemRef::GrandpaConsensus(v.into()),
DigestItem::UnknownConsensus { engine, opaque } => DigestItemRef::UnknownConsensus {
engine: *engine,
opaque,
},
DigestItem::UnknownSeal { engine, opaque } => DigestItemRef::UnknownSeal {
engine: *engine,
opaque,
},
DigestItem::UnknownPreRuntime { engine, opaque } => DigestItemRef::UnknownPreRuntime {
engine: *engine,
opaque,
},
DigestItem::Other(v) => DigestItemRef::Other(v),
DigestItem::RuntimeEnvironmentUpdated => DigestItemRef::RuntimeEnvironmentUpdated,
}
}
}
#[derive(Debug, Clone)]
pub enum DigestItem {
AuraPreDigest(AuraPreDigest),
AuraConsensus(AuraConsensusLog),
AuraSeal([u8; 64]),
BabePreDigest(BabePreDigest),
BabeConsensus(BabeConsensusLog),
BabeSeal([u8; 64]),
GrandpaConsensus(GrandpaConsensusLog),
UnknownConsensus {
engine: [u8; 4],
opaque: Vec<u8>,
},
UnknownPreRuntime {
engine: [u8; 4],
opaque: Vec<u8>,
},
UnknownSeal {
engine: [u8; 4],
opaque: Vec<u8>,
},
RuntimeEnvironmentUpdated,
Other(Vec<u8>),
}
impl<'a> From<DigestItemRef<'a>> for DigestItem {
fn from(a: DigestItemRef<'a>) -> DigestItem {
match a {
DigestItemRef::AuraPreDigest(v) => DigestItem::AuraPreDigest(v),
DigestItemRef::AuraConsensus(v) => DigestItem::AuraConsensus(v.into()),
DigestItemRef::AuraSeal(v) => {
let mut seal = [0; 64];
seal.copy_from_slice(v);
DigestItem::AuraSeal(seal)
}
DigestItemRef::BabePreDigest(v) => DigestItem::BabePreDigest(v.into()),
DigestItemRef::BabeConsensus(v) => DigestItem::BabeConsensus(v.into()),
DigestItemRef::BabeSeal(v) => {
let mut seal = [0; 64];
seal.copy_from_slice(v);
DigestItem::BabeSeal(seal)
}
DigestItemRef::GrandpaConsensus(v) => DigestItem::GrandpaConsensus(v.into()),
DigestItemRef::UnknownConsensus { engine, opaque } => DigestItem::UnknownConsensus {
opaque: opaque.to_vec(),
engine,
},
DigestItemRef::UnknownSeal { engine, opaque } => DigestItem::UnknownSeal {
opaque: opaque.to_vec(),
engine,
},
DigestItemRef::UnknownPreRuntime { engine, opaque } => DigestItem::UnknownPreRuntime {
opaque: opaque.to_vec(),
engine,
},
DigestItemRef::Other(v) => DigestItem::Other(v.to_vec()),
DigestItemRef::RuntimeEnvironmentUpdated => DigestItem::RuntimeEnvironmentUpdated,
}
}
}
fn decode_item(
mut slice: &'_ [u8],
block_number_bytes: usize,
) -> Result<(DigestItemRef<'_>, &'_ [u8]), Error> {
let index = *slice.first().ok_or(Error::TooShort)?;
slice = &slice[1..];
match index {
4..=6 => {
if slice.len() < 4 {
return Err(Error::TooShort);
}
let engine_id: &[u8; 4] = TryFrom::try_from(&slice[..4]).unwrap();
slice = &slice[4..];
let (mut slice, len) =
crate::util::nom_scale_compact_usize::<nom::error::Error<&[u8]>>(slice)
.map_err(|_| Error::DigestItemLenDecodeError)?;
if slice.len() < len {
return Err(Error::TooShort);
}
let content = &slice[..len];
slice = &slice[len..];
let item = decode_item_from_parts(index, block_number_bytes, engine_id, content)?;
Ok((item, slice))
}
8 => Ok((DigestItemRef::RuntimeEnvironmentUpdated, slice)),
0 => {
let (mut slice, len) =
crate::util::nom_scale_compact_usize::<nom::error::Error<&[u8]>>(slice)
.map_err(|_| Error::DigestItemLenDecodeError)?;
if slice.len() < len {
return Err(Error::TooShort);
}
let content = &slice[..len];
slice = &slice[len..];
let item = DigestItemRef::Other(content);
Ok((item, slice))
}
ty => Err(Error::UnknownDigestLogType { unknown_type: ty }),
}
}
fn decode_item_from_parts<'a>(
index: u8,
block_number_bytes: usize,
engine_id: &'a [u8; 4],
content: &'a [u8],
) -> Result<DigestItemRef<'a>, Error> {
Ok(match (index, engine_id) {
(_, b"pow_") => return Err(Error::PowIdeologicallyNotSupported),
(4, b"aura") => DigestItemRef::AuraConsensus(AuraConsensusLogRef::from_slice(content)?),
(4, b"BABE") => DigestItemRef::BabeConsensus(BabeConsensusLogRef::from_slice(content)?),
(4, b"FRNK") => DigestItemRef::GrandpaConsensus(GrandpaConsensusLogRef::from_slice(
content,
block_number_bytes,
)?),
(4, engine) => DigestItemRef::UnknownConsensus {
engine: *engine,
opaque: content,
},
(5, b"aura") => DigestItemRef::AuraSeal({
TryFrom::try_from(content).map_err(|_| Error::BadAuraSealLength)?
}),
(5, b"BABE") => DigestItemRef::BabeSeal({
TryFrom::try_from(content).map_err(|_| Error::BadBabeSealLength)?
}),
(5, engine) => DigestItemRef::UnknownSeal {
engine: *engine,
opaque: content,
},
(6, b"aura") => DigestItemRef::AuraPreDigest(AuraPreDigest::from_slice(content)?),
(6, b"BABE") => DigestItemRef::BabePreDigest(BabePreDigestRef::from_slice(content)?),
(6, engine) => DigestItemRef::UnknownPreRuntime {
engine: *engine,
opaque: content,
},
_ => unreachable!(),
})
}