1#[cfg(all(not(feature = "std"), feature = "serde"))]
21use alloc::format;
22use alloc::vec::Vec;
23use codec::DecodeAll;
24#[cfg(feature = "serde")]
25use serde::{Deserialize, Serialize};
26
27use crate::{
28 codec::{Decode, DecodeWithMemTracking, Encode, Error, Input},
29 scale_info::{
30 build::{Fields, Variants},
31 Path, Type, TypeInfo,
32 },
33 ConsensusEngineId,
34};
35use sp_core::RuntimeDebug;
36
37#[derive(
39 PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, TypeInfo, Default,
40)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42pub struct Digest {
43 pub logs: Vec<DigestItem>,
45}
46
47impl Digest {
48 pub fn logs(&self) -> &[DigestItem] {
50 &self.logs
51 }
52
53 pub fn push(&mut self, item: DigestItem) {
55 self.logs.push(item);
56 }
57
58 pub fn pop(&mut self) -> Option<DigestItem> {
60 self.logs.pop()
61 }
62
63 pub fn log<T: ?Sized, F: Fn(&DigestItem) -> Option<&T>>(&self, predicate: F) -> Option<&T> {
65 self.logs().iter().find_map(predicate)
66 }
67
68 pub fn convert_first<T, F: Fn(&DigestItem) -> Option<T>>(&self, predicate: F) -> Option<T> {
70 self.logs().iter().find_map(predicate)
71 }
72}
73
74#[derive(PartialEq, Eq, Clone, DecodeWithMemTracking, RuntimeDebug)]
77pub enum DigestItem {
78 PreRuntime(ConsensusEngineId, Vec<u8>),
91
92 Consensus(ConsensusEngineId, Vec<u8>),
96
97 Seal(ConsensusEngineId, Vec<u8>),
100
101 Other(Vec<u8>),
103
104 RuntimeEnvironmentUpdated,
111}
112
113#[cfg(feature = "serde")]
114impl serde::Serialize for DigestItem {
115 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
116 where
117 S: serde::Serializer,
118 {
119 self.using_encoded(|bytes| sp_core::bytes::serialize(bytes, seq))
120 }
121}
122
123#[cfg(feature = "serde")]
124impl<'a> serde::Deserialize<'a> for DigestItem {
125 fn deserialize<D>(de: D) -> Result<Self, D::Error>
126 where
127 D: serde::Deserializer<'a>,
128 {
129 let r = sp_core::bytes::deserialize(de)?;
130 Decode::decode(&mut &r[..])
131 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
132 }
133}
134
135impl TypeInfo for DigestItem {
136 type Identity = Self;
137
138 fn type_info() -> Type {
139 Type::builder().path(Path::new("DigestItem", module_path!())).variant(
140 Variants::new()
141 .variant("PreRuntime", |v| {
142 v.index(DigestItemType::PreRuntime as u8).fields(
143 Fields::unnamed()
144 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
145 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
146 )
147 })
148 .variant("Consensus", |v| {
149 v.index(DigestItemType::Consensus as u8).fields(
150 Fields::unnamed()
151 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
152 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
153 )
154 })
155 .variant("Seal", |v| {
156 v.index(DigestItemType::Seal as u8).fields(
157 Fields::unnamed()
158 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
159 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
160 )
161 })
162 .variant("Other", |v| {
163 v.index(DigestItemType::Other as u8)
164 .fields(Fields::unnamed().field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")))
165 })
166 .variant("RuntimeEnvironmentUpdated", |v| {
167 v.index(DigestItemType::RuntimeEnvironmentUpdated as u8).fields(Fields::unit())
168 }),
169 )
170 }
171}
172
173#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
176pub enum DigestItemRef<'a> {
177 PreRuntime(&'a ConsensusEngineId, &'a [u8]),
184 Consensus(&'a ConsensusEngineId, &'a [u8]),
188 Seal(&'a ConsensusEngineId, &'a [u8]),
191 Other(&'a [u8]),
193 RuntimeEnvironmentUpdated,
195}
196
197#[repr(u32)]
202#[derive(Encode, Decode)]
203pub enum DigestItemType {
204 Other = 0,
205 Consensus = 4,
206 Seal = 5,
207 PreRuntime = 6,
208 RuntimeEnvironmentUpdated = 8,
209}
210
211#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
214pub enum OpaqueDigestItemId<'a> {
215 PreRuntime(&'a ConsensusEngineId),
217 Consensus(&'a ConsensusEngineId),
219 Seal(&'a ConsensusEngineId),
221 Other,
223}
224
225impl DigestItem {
226 pub fn dref(&self) -> DigestItemRef {
228 match *self {
229 Self::PreRuntime(ref v, ref s) => DigestItemRef::PreRuntime(v, s),
230 Self::Consensus(ref v, ref s) => DigestItemRef::Consensus(v, s),
231 Self::Seal(ref v, ref s) => DigestItemRef::Seal(v, s),
232 Self::Other(ref v) => DigestItemRef::Other(v),
233 Self::RuntimeEnvironmentUpdated => DigestItemRef::RuntimeEnvironmentUpdated,
234 }
235 }
236
237 pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &[u8])> {
239 self.dref().as_pre_runtime()
240 }
241
242 pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &[u8])> {
244 self.dref().as_consensus()
245 }
246
247 pub fn as_seal(&self) -> Option<(ConsensusEngineId, &[u8])> {
249 self.dref().as_seal()
250 }
251
252 pub fn as_other(&self) -> Option<&[u8]> {
254 self.dref().as_other()
255 }
256
257 pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&[u8]> {
259 self.dref().try_as_raw(id)
260 }
261
262 pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
264 self.dref().try_to::<T>(id)
265 }
266
267 pub fn seal_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
271 self.dref().seal_try_to(id)
272 }
273
274 pub fn consensus_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
279 self.dref().consensus_try_to(id)
280 }
281
282 pub fn pre_runtime_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
287 self.dref().pre_runtime_try_to(id)
288 }
289}
290
291impl Encode for DigestItem {
292 fn encode(&self) -> Vec<u8> {
293 self.dref().encode()
294 }
295}
296
297impl codec::EncodeLike for DigestItem {}
298
299impl Decode for DigestItem {
300 #[allow(deprecated)]
301 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
302 let item_type: DigestItemType = Decode::decode(input)?;
303 match item_type {
304 DigestItemType::PreRuntime => {
305 let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
306 Ok(Self::PreRuntime(vals.0, vals.1))
307 },
308 DigestItemType::Consensus => {
309 let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
310 Ok(Self::Consensus(vals.0, vals.1))
311 },
312 DigestItemType::Seal => {
313 let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
314 Ok(Self::Seal(vals.0, vals.1))
315 },
316 DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)),
317 DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated),
318 }
319 }
320}
321
322impl<'a> DigestItemRef<'a> {
323 pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
325 match *self {
326 Self::PreRuntime(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
327 _ => None,
328 }
329 }
330
331 pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
333 match *self {
334 Self::Consensus(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
335 _ => None,
336 }
337 }
338
339 pub fn as_seal(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
341 match *self {
342 Self::Seal(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
343 _ => None,
344 }
345 }
346
347 pub fn as_other(&self) -> Option<&'a [u8]> {
349 match *self {
350 Self::Other(data) => Some(data),
351 _ => None,
352 }
353 }
354
355 pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&'a [u8]> {
358 match (id, self) {
359 (OpaqueDigestItemId::Consensus(w), &Self::Consensus(v, s)) |
360 (OpaqueDigestItemId::Seal(w), &Self::Seal(v, s)) |
361 (OpaqueDigestItemId::PreRuntime(w), &Self::PreRuntime(v, s))
362 if v == w =>
363 Some(s),
364 (OpaqueDigestItemId::Other, &Self::Other(s)) => Some(s),
365 _ => None,
366 }
367 }
368
369 pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
372 self.try_as_raw(id).and_then(|mut x| DecodeAll::decode_all(&mut x).ok())
373 }
374
375 pub fn seal_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
379 self.as_seal()
380 .filter(|s| s.0 == *id)
381 .and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
382 }
383
384 pub fn consensus_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
389 self.as_consensus()
390 .filter(|s| s.0 == *id)
391 .and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
392 }
393
394 pub fn pre_runtime_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
399 self.as_pre_runtime()
400 .filter(|s| s.0 == *id)
401 .and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
402 }
403}
404
405impl<'a> Encode for DigestItemRef<'a> {
406 fn encode(&self) -> Vec<u8> {
407 match *self {
408 Self::Consensus(val, data) => (DigestItemType::Consensus, val, data).encode(),
409 Self::Seal(val, sig) => (DigestItemType::Seal, val, sig).encode(),
410 Self::PreRuntime(val, data) => (DigestItemType::PreRuntime, val, data).encode(),
411 Self::Other(val) => (DigestItemType::Other, val).encode(),
412 Self::RuntimeEnvironmentUpdated => DigestItemType::RuntimeEnvironmentUpdated.encode(),
413 }
414 }
415}
416
417impl<'a> codec::EncodeLike for DigestItemRef<'a> {}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 #[test]
424 fn should_serialize_digest() {
425 let digest = Digest {
426 logs: vec![DigestItem::Other(vec![1, 2, 3]), DigestItem::Seal(*b"test", vec![1, 2, 3])],
427 };
428
429 assert_eq!(
430 serde_json::to_string(&digest).unwrap(),
431 r#"{"logs":["0x000c010203","0x05746573740c010203"]}"#
432 );
433 }
434
435 #[test]
436 fn digest_item_type_info() {
437 let type_info = DigestItem::type_info();
438 let variants = if let scale_info::TypeDef::Variant(variant) = type_info.type_def {
439 variant.variants
440 } else {
441 panic!("Should be a TypeDef::TypeDefVariant")
442 };
443
444 let check = |digest_item_type: DigestItemType| {
446 let (variant_name, digest_item) = match digest_item_type {
447 DigestItemType::Other => ("Other", DigestItem::Other(Default::default())),
448 DigestItemType::Consensus =>
449 ("Consensus", DigestItem::Consensus(Default::default(), Default::default())),
450 DigestItemType::Seal =>
451 ("Seal", DigestItem::Seal(Default::default(), Default::default())),
452 DigestItemType::PreRuntime =>
453 ("PreRuntime", DigestItem::PreRuntime(Default::default(), Default::default())),
454 DigestItemType::RuntimeEnvironmentUpdated =>
455 ("RuntimeEnvironmentUpdated", DigestItem::RuntimeEnvironmentUpdated),
456 };
457 let encoded = digest_item.encode();
458 let variant = variants
459 .iter()
460 .find(|v| v.name == variant_name)
461 .expect(&format!("Variant {} not found", variant_name));
462
463 assert_eq!(encoded[0], variant.index)
464 };
465
466 check(DigestItemType::Other);
467 check(DigestItemType::Consensus);
468 check(DigestItemType::Seal);
469 check(DigestItemType::PreRuntime);
470 check(DigestItemType::RuntimeEnvironmentUpdated);
471 }
472}