// This file is part of Substrate. // Copyright (C) 2017-2021 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Generic implementation of a digest. #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_std::prelude::*; use crate::ConsensusEngineId; use crate::codec::{Decode, Encode, Input, Error}; use sp_core::{ChangesTrieConfiguration, RuntimeDebug}; /// Generic header digest. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, parity_util_mem::MallocSizeOf))] pub struct Digest { /// A list of logs in the digest. #[cfg_attr( feature = "std", serde(bound(serialize = "Hash: codec::Codec", deserialize = "Hash: codec::Codec")) )] pub logs: Vec>, } impl Default for Digest { fn default() -> Self { Self { logs: Vec::new(), } } } impl Digest { /// Get reference to all digest items. pub fn logs(&self) -> &[DigestItem] { &self.logs } /// Push new digest item. pub fn push(&mut self, item: DigestItem) { self.logs.push(item); } /// Pop a digest item. pub fn pop(&mut self) -> Option> { self.logs.pop() } /// Get reference to the first digest item that matches the passed predicate. pub fn log) -> Option<&T>>(&self, predicate: F) -> Option<&T> { self.logs().iter().find_map(predicate) } /// Get a conversion of the first digest item that successfully converts using the function. pub fn convert_first) -> Option>(&self, predicate: F) -> Option { self.logs().iter().find_map(predicate) } } /// Digest item that is able to encode/decode 'system' digest items and /// provide opaque access to other items. #[derive(PartialEq, Eq, Clone, RuntimeDebug)] #[cfg_attr(feature = "std", derive(parity_util_mem::MallocSizeOf))] pub enum DigestItem { /// System digest item that contains the root of changes trie at given /// block. It is created for every block iff runtime supports changes /// trie creation. ChangesTrieRoot(Hash), /// A pre-runtime digest. /// /// These are messages from the consensus engine to the runtime, although /// the consensus engine can (and should) read them itself to avoid /// code and state duplication. It is erroneous for a runtime to produce /// these, but this is not (yet) checked. /// /// NOTE: the runtime is not allowed to panic or fail in an `on_initialize` /// call if an expected `PreRuntime` digest is not present. It is the /// responsibility of a external block verifier to check this. Runtime API calls /// will initialize the block without pre-runtime digests, so initialization /// cannot fail when they are missing. PreRuntime(ConsensusEngineId, Vec), /// A message from the runtime to the consensus engine. This should *never* /// be generated by the native code of any consensus engine, but this is not /// checked (yet). Consensus(ConsensusEngineId, Vec), /// Put a Seal on it. This is only used by native code, and is never seen /// by runtimes. Seal(ConsensusEngineId, Vec), /// Digest item that contains signal from changes tries manager to the /// native code. ChangesTrieSignal(ChangesTrieSignal), /// Some other thing. Unsupported and experimental. Other(Vec), } /// Available changes trie signals. #[derive(PartialEq, Eq, Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, parity_util_mem::MallocSizeOf))] pub enum ChangesTrieSignal { /// New changes trie configuration is enacted, starting from **next block**. /// /// The block that emits this signal will contain changes trie (CT) that covers /// blocks range [BEGIN; current block], where BEGIN is (order matters): /// - LAST_TOP_LEVEL_DIGEST_BLOCK+1 if top level digest CT has ever been created /// using current configuration AND the last top level digest CT has been created /// at block LAST_TOP_LEVEL_DIGEST_BLOCK; /// - LAST_CONFIGURATION_CHANGE_BLOCK+1 if there has been CT configuration change /// before and the last configuration change happened at block /// LAST_CONFIGURATION_CHANGE_BLOCK; /// - 1 otherwise. NewConfiguration(Option), } #[cfg(feature = "std")] impl serde::Serialize for DigestItem { fn serialize(&self, seq: S) -> Result where S: serde::Serializer { self.using_encoded(|bytes| { sp_core::bytes::serialize(bytes, seq) }) } } #[cfg(feature = "std")] impl<'a, Hash: Decode> serde::Deserialize<'a> for DigestItem { fn deserialize(de: D) -> Result where D: serde::Deserializer<'a>, { let r = sp_core::bytes::deserialize(de)?; Decode::decode(&mut &r[..]) .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e))) } } /// A 'referencing view' for digest item. Does not own its contents. Used by /// final runtime implementations for encoding/decoding its log items. #[derive(PartialEq, Eq, Clone, RuntimeDebug)] pub enum DigestItemRef<'a, Hash: 'a> { /// Reference to `DigestItem::ChangesTrieRoot`. ChangesTrieRoot(&'a Hash), /// A pre-runtime digest. /// /// These are messages from the consensus engine to the runtime, although /// the consensus engine can (and should) read them itself to avoid /// code and state duplication. It is erroneous for a runtime to produce /// these, but this is not (yet) checked. PreRuntime(&'a ConsensusEngineId, &'a Vec), /// A message from the runtime to the consensus engine. This should *never* /// be generated by the native code of any consensus engine, but this is not /// checked (yet). Consensus(&'a ConsensusEngineId, &'a Vec), /// Put a Seal on it. This is only used by native code, and is never seen /// by runtimes. Seal(&'a ConsensusEngineId, &'a Vec), /// Digest item that contains signal from changes tries manager to the /// native code. ChangesTrieSignal(&'a ChangesTrieSignal), /// Any 'non-system' digest item, opaque to the native code. Other(&'a Vec), } /// Type of the digest item. Used to gain explicit control over `DigestItem` encoding /// process. We need an explicit control, because final runtimes are encoding their own /// digest items using `DigestItemRef` type and we can't auto-derive `Decode` /// trait for `DigestItemRef`. #[repr(u32)] #[derive(Encode, Decode)] pub enum DigestItemType { Other = 0, ChangesTrieRoot = 2, Consensus = 4, Seal = 5, PreRuntime = 6, ChangesTrieSignal = 7, } /// Type of a digest item that contains raw data; this also names the consensus engine ID where /// applicable. Used to identify one or more digest items of interest. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum OpaqueDigestItemId<'a> { /// Type corresponding to DigestItem::PreRuntime. PreRuntime(&'a ConsensusEngineId), /// Type corresponding to DigestItem::Consensus. Consensus(&'a ConsensusEngineId), /// Type corresponding to DigestItem::Seal. Seal(&'a ConsensusEngineId), /// Some other (non-prescribed) type. Other, } impl DigestItem { /// Returns a 'referencing view' for this digest item. pub fn dref(&self) -> DigestItemRef { match *self { Self::ChangesTrieRoot(ref v) => DigestItemRef::ChangesTrieRoot(v), Self::PreRuntime(ref v, ref s) => DigestItemRef::PreRuntime(v, s), Self::Consensus(ref v, ref s) => DigestItemRef::Consensus(v, s), Self::Seal(ref v, ref s) => DigestItemRef::Seal(v, s), Self::ChangesTrieSignal(ref s) => DigestItemRef::ChangesTrieSignal(s), Self::Other(ref v) => DigestItemRef::Other(v), } } /// Returns `Some` if the entry is the `ChangesTrieRoot` entry. pub fn as_changes_trie_root(&self) -> Option<&Hash> { self.dref().as_changes_trie_root() } /// Returns `Some` if this entry is the `PreRuntime` entry. pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &[u8])> { self.dref().as_pre_runtime() } /// Returns `Some` if this entry is the `Consensus` entry. pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &[u8])> { self.dref().as_consensus() } /// Returns `Some` if this entry is the `Seal` entry. pub fn as_seal(&self) -> Option<(ConsensusEngineId, &[u8])> { self.dref().as_seal() } /// Returns `Some` if the entry is the `ChangesTrieSignal` entry. pub fn as_changes_trie_signal(&self) -> Option<&ChangesTrieSignal> { self.dref().as_changes_trie_signal() } /// Returns Some if `self` is a `DigestItem::Other`. pub fn as_other(&self) -> Option<&[u8]> { self.dref().as_other() } /// Returns the opaque data contained in the item if `Some` if this entry has the id given. pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&[u8]> { self.dref().try_as_raw(id) } /// Returns the data contained in the item if `Some` if this entry has the id given, decoded /// to the type provided `T`. pub fn try_to(&self, id: OpaqueDigestItemId) -> Option { self.dref().try_to::(id) } /// Try to match this to a `Self::Seal`, check `id` matches and decode it. /// /// Returns `None` if this isn't a seal item, the `id` doesn't match or when the decoding fails. pub fn seal_try_to(&self, id: &ConsensusEngineId) -> Option { self.dref().seal_try_to(id) } /// Try to match this to a `Self::Consensus`, check `id` matches and decode it. /// /// Returns `None` if this isn't a consensus item, the `id` doesn't match or /// when the decoding fails. pub fn consensus_try_to(&self, id: &ConsensusEngineId) -> Option { self.dref().consensus_try_to(id) } /// Try to match this to a `Self::PreRuntime`, check `id` matches and decode it. /// /// Returns `None` if this isn't a pre-runtime item, the `id` doesn't match or /// when the decoding fails. pub fn pre_runtime_try_to(&self, id: &ConsensusEngineId) -> Option { self.dref().pre_runtime_try_to(id) } } impl Encode for DigestItem { fn encode(&self) -> Vec { self.dref().encode() } } impl codec::EncodeLike for DigestItem {} impl Decode for DigestItem { #[allow(deprecated)] fn decode(input: &mut I) -> Result { let item_type: DigestItemType = Decode::decode(input)?; match item_type { DigestItemType::ChangesTrieRoot => Ok(Self::ChangesTrieRoot( Decode::decode(input)?, )), DigestItemType::PreRuntime => { let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; Ok(Self::PreRuntime(vals.0, vals.1)) }, DigestItemType::Consensus => { let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; Ok(Self::Consensus(vals.0, vals.1)) } DigestItemType::Seal => { let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; Ok(Self::Seal(vals.0, vals.1)) }, DigestItemType::ChangesTrieSignal => Ok(Self::ChangesTrieSignal( Decode::decode(input)?, )), DigestItemType::Other => Ok(Self::Other( Decode::decode(input)?, )), } } } impl<'a, Hash> DigestItemRef<'a, Hash> { /// Cast this digest item into `ChangesTrieRoot`. pub fn as_changes_trie_root(&self) -> Option<&'a Hash> { match *self { Self::ChangesTrieRoot(ref changes_trie_root) => Some(changes_trie_root), _ => None, } } /// Cast this digest item into `PreRuntime` pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &'a [u8])> { match *self { Self::PreRuntime(consensus_engine_id, ref data) => Some((*consensus_engine_id, data)), _ => None, } } /// Cast this digest item into `Consensus` pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &'a [u8])> { match *self { Self::Consensus(consensus_engine_id, ref data) => Some((*consensus_engine_id, data)), _ => None, } } /// Cast this digest item into `Seal` pub fn as_seal(&self) -> Option<(ConsensusEngineId, &'a [u8])> { match *self { Self::Seal(consensus_engine_id, ref data) => Some((*consensus_engine_id, data)), _ => None, } } /// Cast this digest item into `ChangesTrieSignal`. pub fn as_changes_trie_signal(&self) -> Option<&'a ChangesTrieSignal> { match *self { Self::ChangesTrieSignal(ref changes_trie_signal) => Some(changes_trie_signal), _ => None, } } /// Cast this digest item into `PreRuntime` pub fn as_other(&self) -> Option<&'a [u8]> { match *self { Self::Other(ref data) => Some(data), _ => None, } } /// Try to match this digest item to the given opaque item identifier; if it matches, then /// return the opaque data it contains. pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&'a [u8]> { match (id, self) { (OpaqueDigestItemId::Consensus(w), &Self::Consensus(v, s)) | (OpaqueDigestItemId::Seal(w), &Self::Seal(v, s)) | (OpaqueDigestItemId::PreRuntime(w), &Self::PreRuntime(v, s)) if v == w => Some(&s[..]), (OpaqueDigestItemId::Other, &Self::Other(s)) => Some(&s[..]), _ => None, } } /// Try to match this digest item to the given opaque item identifier; if it matches, then /// try to cast to the given data type; if that works, return it. pub fn try_to(&self, id: OpaqueDigestItemId) -> Option { self.try_as_raw(id).and_then(|mut x| Decode::decode(&mut x).ok()) } /// Try to match this to a `Self::Seal`, check `id` matches and decode it. /// /// Returns `None` if this isn't a seal item, the `id` doesn't match or when the decoding fails. pub fn seal_try_to(&self, id: &ConsensusEngineId) -> Option { match self { Self::Seal(v, s) if *v == id => Decode::decode(&mut &s[..]).ok(), _ => None, } } /// Try to match this to a `Self::Consensus`, check `id` matches and decode it. /// /// Returns `None` if this isn't a consensus item, the `id` doesn't match or /// when the decoding fails. pub fn consensus_try_to(&self, id: &ConsensusEngineId) -> Option { match self { Self::Consensus(v, s) if *v == id => Decode::decode(&mut &s[..]).ok(), _ => None, } } /// Try to match this to a `Self::PreRuntime`, check `id` matches and decode it. /// /// Returns `None` if this isn't a pre-runtime item, the `id` doesn't match or /// when the decoding fails. pub fn pre_runtime_try_to(&self, id: &ConsensusEngineId) -> Option { match self { Self::PreRuntime(v, s) if *v == id => Decode::decode(&mut &s[..]).ok(), _ => None, } } } impl<'a, Hash: Encode> Encode for DigestItemRef<'a, Hash> { fn encode(&self) -> Vec { let mut v = Vec::new(); match *self { Self::ChangesTrieRoot(changes_trie_root) => { DigestItemType::ChangesTrieRoot.encode_to(&mut v); changes_trie_root.encode_to(&mut v); }, Self::Consensus(val, data) => { DigestItemType::Consensus.encode_to(&mut v); (val, data).encode_to(&mut v); }, Self::Seal(val, sig) => { DigestItemType::Seal.encode_to(&mut v); (val, sig).encode_to(&mut v); }, Self::PreRuntime(val, data) => { DigestItemType::PreRuntime.encode_to(&mut v); (val, data).encode_to(&mut v); }, Self::ChangesTrieSignal(changes_trie_signal) => { DigestItemType::ChangesTrieSignal.encode_to(&mut v); changes_trie_signal.encode_to(&mut v); }, Self::Other(val) => { DigestItemType::Other.encode_to(&mut v); val.encode_to(&mut v); }, } v } } impl ChangesTrieSignal { /// Try to cast this signal to NewConfiguration. pub fn as_new_configuration(&self) -> Option<&Option> { match self { Self::NewConfiguration(config) => Some(config), } } } impl<'a, Hash: Encode> codec::EncodeLike for DigestItemRef<'a, Hash> {} #[cfg(test)] mod tests { use super::*; #[test] fn should_serialize_digest() { let digest = Digest { logs: vec![ DigestItem::ChangesTrieRoot(4), DigestItem::Other(vec![1, 2, 3]), DigestItem::Seal(*b"test", vec![1, 2, 3]) ], }; assert_eq!( serde_json::to_string(&digest).unwrap(), r#"{"logs":["0x0204000000","0x000c010203","0x05746573740c010203"]}"# ); } }