1#[cfg(all(not(feature = "std"), feature = "serde"))]
21use alloc::format;
22use alloc::vec::Vec;
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25
26use crate::{
27 codec::{Decode, Encode, Error, Input},
28 scale_info::{
29 build::{Fields, Variants},
30 Path, Type, TypeInfo,
31 },
32 ConsensusEngineId,
33};
34use sp_core::RuntimeDebug;
35
36#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39pub struct Digest {
40 pub logs: Vec<DigestItem>,
42}
43
44impl Digest {
45 pub fn logs(&self) -> &[DigestItem] {
47 &self.logs
48 }
49
50 pub fn push(&mut self, item: DigestItem) {
52 self.logs.push(item);
53 }
54
55 pub fn pop(&mut self) -> Option<DigestItem> {
57 self.logs.pop()
58 }
59
60 pub fn log<T: ?Sized, F: Fn(&DigestItem) -> Option<&T>>(&self, predicate: F) -> Option<&T> {
62 self.logs().iter().find_map(predicate)
63 }
64
65 pub fn convert_first<T, F: Fn(&DigestItem) -> Option<T>>(&self, predicate: F) -> Option<T> {
67 self.logs().iter().find_map(predicate)
68 }
69}
70
71#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
74pub enum DigestItem {
75 PreRuntime(ConsensusEngineId, Vec<u8>),
88
89 Consensus(ConsensusEngineId, Vec<u8>),
93
94 Seal(ConsensusEngineId, Vec<u8>),
97
98 Other(Vec<u8>),
100
101 RuntimeEnvironmentUpdated,
108}
109
110#[cfg(feature = "serde")]
111impl serde::Serialize for DigestItem {
112 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
113 where
114 S: serde::Serializer,
115 {
116 self.using_encoded(|bytes| sp_core::bytes::serialize(bytes, seq))
117 }
118}
119
120#[cfg(feature = "serde")]
121impl<'a> serde::Deserialize<'a> for DigestItem {
122 fn deserialize<D>(de: D) -> Result<Self, D::Error>
123 where
124 D: serde::Deserializer<'a>,
125 {
126 let r = sp_core::bytes::deserialize(de)?;
127 Decode::decode(&mut &r[..])
128 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
129 }
130}
131
132impl TypeInfo for DigestItem {
133 type Identity = Self;
134
135 fn type_info() -> Type {
136 Type::builder().path(Path::new("DigestItem", module_path!())).variant(
137 Variants::new()
138 .variant("PreRuntime", |v| {
139 v.index(DigestItemType::PreRuntime as u8).fields(
140 Fields::unnamed()
141 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
142 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
143 )
144 })
145 .variant("Consensus", |v| {
146 v.index(DigestItemType::Consensus as u8).fields(
147 Fields::unnamed()
148 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
149 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
150 )
151 })
152 .variant("Seal", |v| {
153 v.index(DigestItemType::Seal as u8).fields(
154 Fields::unnamed()
155 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
156 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
157 )
158 })
159 .variant("Other", |v| {
160 v.index(DigestItemType::Other as u8)
161 .fields(Fields::unnamed().field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")))
162 })
163 .variant("RuntimeEnvironmentUpdated", |v| {
164 v.index(DigestItemType::RuntimeEnvironmentUpdated as u8).fields(Fields::unit())
165 }),
166 )
167 }
168}
169
170#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
173pub enum DigestItemRef<'a> {
174 PreRuntime(&'a ConsensusEngineId, &'a [u8]),
181 Consensus(&'a ConsensusEngineId, &'a [u8]),
185 Seal(&'a ConsensusEngineId, &'a [u8]),
188 Other(&'a [u8]),
190 RuntimeEnvironmentUpdated,
192}
193
194#[repr(u32)]
199#[derive(Encode, Decode)]
200pub enum DigestItemType {
201 Other = 0,
202 Consensus = 4,
203 Seal = 5,
204 PreRuntime = 6,
205 RuntimeEnvironmentUpdated = 8,
206}
207
208#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
211pub enum OpaqueDigestItemId<'a> {
212 PreRuntime(&'a ConsensusEngineId),
214 Consensus(&'a ConsensusEngineId),
216 Seal(&'a ConsensusEngineId),
218 Other,
220}
221
222impl DigestItem {
223 pub fn dref(&self) -> DigestItemRef {
225 match *self {
226 Self::PreRuntime(ref v, ref s) => DigestItemRef::PreRuntime(v, s),
227 Self::Consensus(ref v, ref s) => DigestItemRef::Consensus(v, s),
228 Self::Seal(ref v, ref s) => DigestItemRef::Seal(v, s),
229 Self::Other(ref v) => DigestItemRef::Other(v),
230 Self::RuntimeEnvironmentUpdated => DigestItemRef::RuntimeEnvironmentUpdated,
231 }
232 }
233
234 pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &[u8])> {
236 self.dref().as_pre_runtime()
237 }
238
239 pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &[u8])> {
241 self.dref().as_consensus()
242 }
243
244 pub fn as_seal(&self) -> Option<(ConsensusEngineId, &[u8])> {
246 self.dref().as_seal()
247 }
248
249 pub fn as_other(&self) -> Option<&[u8]> {
251 self.dref().as_other()
252 }
253
254 pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&[u8]> {
256 self.dref().try_as_raw(id)
257 }
258
259 pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
262 self.dref().try_to::<T>(id)
263 }
264
265 pub fn seal_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
269 self.dref().seal_try_to(id)
270 }
271
272 pub fn consensus_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
277 self.dref().consensus_try_to(id)
278 }
279
280 pub fn pre_runtime_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
285 self.dref().pre_runtime_try_to(id)
286 }
287}
288
289impl Encode for DigestItem {
290 fn encode(&self) -> Vec<u8> {
291 self.dref().encode()
292 }
293}
294
295impl codec::EncodeLike for DigestItem {}
296
297impl Decode for DigestItem {
298 #[allow(deprecated)]
299 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
300 let item_type: DigestItemType = Decode::decode(input)?;
301 match item_type {
302 DigestItemType::PreRuntime => {
303 let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
304 Ok(Self::PreRuntime(vals.0, vals.1))
305 },
306 DigestItemType::Consensus => {
307 let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
308 Ok(Self::Consensus(vals.0, vals.1))
309 },
310 DigestItemType::Seal => {
311 let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
312 Ok(Self::Seal(vals.0, vals.1))
313 },
314 DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)),
315 DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated),
316 }
317 }
318}
319
320impl<'a> DigestItemRef<'a> {
321 pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
323 match *self {
324 Self::PreRuntime(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
325 _ => None,
326 }
327 }
328
329 pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
331 match *self {
332 Self::Consensus(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
333 _ => None,
334 }
335 }
336
337 pub fn as_seal(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
339 match *self {
340 Self::Seal(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
341 _ => None,
342 }
343 }
344
345 pub fn as_other(&self) -> Option<&'a [u8]> {
347 match *self {
348 Self::Other(data) => Some(data),
349 _ => None,
350 }
351 }
352
353 pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&'a [u8]> {
356 match (id, self) {
357 (OpaqueDigestItemId::Consensus(w), &Self::Consensus(v, s)) |
358 (OpaqueDigestItemId::Seal(w), &Self::Seal(v, s)) |
359 (OpaqueDigestItemId::PreRuntime(w), &Self::PreRuntime(v, s))
360 if v == w =>
361 Some(s),
362 (OpaqueDigestItemId::Other, &Self::Other(s)) => Some(s),
363 _ => None,
364 }
365 }
366
367 pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
370 self.try_as_raw(id).and_then(|mut x| Decode::decode(&mut x).ok())
371 }
372
373 pub fn seal_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
377 match self {
378 Self::Seal(v, s) if *v == id => Decode::decode(&mut &s[..]).ok(),
379 _ => None,
380 }
381 }
382
383 pub fn consensus_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
388 match self {
389 Self::Consensus(v, s) if *v == id => Decode::decode(&mut &s[..]).ok(),
390 _ => None,
391 }
392 }
393
394 pub fn pre_runtime_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
399 match self {
400 Self::PreRuntime(v, s) if *v == id => Decode::decode(&mut &s[..]).ok(),
401 _ => None,
402 }
403 }
404}
405
406impl<'a> Encode for DigestItemRef<'a> {
407 fn encode(&self) -> Vec<u8> {
408 let mut v = Vec::new();
409
410 match *self {
411 Self::Consensus(val, data) => {
412 DigestItemType::Consensus.encode_to(&mut v);
413 (val, data).encode_to(&mut v);
414 },
415 Self::Seal(val, sig) => {
416 DigestItemType::Seal.encode_to(&mut v);
417 (val, sig).encode_to(&mut v);
418 },
419 Self::PreRuntime(val, data) => {
420 DigestItemType::PreRuntime.encode_to(&mut v);
421 (val, data).encode_to(&mut v);
422 },
423 Self::Other(val) => {
424 DigestItemType::Other.encode_to(&mut v);
425 val.encode_to(&mut v);
426 },
427 Self::RuntimeEnvironmentUpdated => {
428 DigestItemType::RuntimeEnvironmentUpdated.encode_to(&mut v);
429 },
430 }
431
432 v
433 }
434}
435
436impl<'a> codec::EncodeLike for DigestItemRef<'a> {}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441
442 #[test]
443 fn should_serialize_digest() {
444 let digest = Digest {
445 logs: vec![DigestItem::Other(vec![1, 2, 3]), DigestItem::Seal(*b"test", vec![1, 2, 3])],
446 };
447
448 assert_eq!(
449 serde_json::to_string(&digest).unwrap(),
450 r#"{"logs":["0x000c010203","0x05746573740c010203"]}"#
451 );
452 }
453
454 #[test]
455 fn digest_item_type_info() {
456 let type_info = DigestItem::type_info();
457 let variants = if let scale_info::TypeDef::Variant(variant) = type_info.type_def {
458 variant.variants
459 } else {
460 panic!("Should be a TypeDef::TypeDefVariant")
461 };
462
463 let check = |digest_item_type: DigestItemType| {
465 let (variant_name, digest_item) = match digest_item_type {
466 DigestItemType::Other => ("Other", DigestItem::Other(Default::default())),
467 DigestItemType::Consensus =>
468 ("Consensus", DigestItem::Consensus(Default::default(), Default::default())),
469 DigestItemType::Seal =>
470 ("Seal", DigestItem::Seal(Default::default(), Default::default())),
471 DigestItemType::PreRuntime =>
472 ("PreRuntime", DigestItem::PreRuntime(Default::default(), Default::default())),
473 DigestItemType::RuntimeEnvironmentUpdated =>
474 ("RuntimeEnvironmentUpdated", DigestItem::RuntimeEnvironmentUpdated),
475 };
476 let encoded = digest_item.encode();
477 let variant = variants
478 .iter()
479 .find(|v| v.name == variant_name)
480 .expect(&format!("Variant {} not found", variant_name));
481
482 assert_eq!(encoded[0], variant.index)
483 };
484
485 check(DigestItemType::Other);
486 check(DigestItemType::Consensus);
487 check(DigestItemType::Seal);
488 check(DigestItemType::PreRuntime);
489 check(DigestItemType::RuntimeEnvironmentUpdated);
490 }
491}