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 Debug;
36
37#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, Default)]
39#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40pub struct Digest {
41 pub logs: Vec<DigestItem>,
43}
44
45impl Digest {
46 pub fn logs(&self) -> &[DigestItem] {
48 &self.logs
49 }
50
51 pub fn push(&mut self, item: DigestItem) {
53 self.logs.push(item);
54 }
55
56 pub fn pop(&mut self) -> Option<DigestItem> {
58 self.logs.pop()
59 }
60
61 pub fn log<T: ?Sized, F: Fn(&DigestItem) -> Option<&T>>(&self, predicate: F) -> Option<&T> {
63 self.logs().iter().find_map(predicate)
64 }
65
66 pub fn convert_first<T, F: Fn(&DigestItem) -> Option<T>>(&self, predicate: F) -> Option<T> {
68 self.logs().iter().find_map(predicate)
69 }
70}
71
72#[derive(PartialEq, Eq, Clone, DecodeWithMemTracking, Debug)]
75pub enum DigestItem {
76 PreRuntime(ConsensusEngineId, Vec<u8>),
89
90 Consensus(ConsensusEngineId, Vec<u8>),
94
95 Seal(ConsensusEngineId, Vec<u8>),
98
99 Other(Vec<u8>),
101
102 RuntimeEnvironmentUpdated,
109}
110
111#[cfg(feature = "serde")]
112impl serde::Serialize for DigestItem {
113 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
114 where
115 S: serde::Serializer,
116 {
117 self.using_encoded(|bytes| sp_core::bytes::serialize(bytes, seq))
118 }
119}
120
121#[cfg(feature = "serde")]
122impl<'a> serde::Deserialize<'a> for DigestItem {
123 fn deserialize<D>(de: D) -> Result<Self, D::Error>
124 where
125 D: serde::Deserializer<'a>,
126 {
127 let r = sp_core::bytes::deserialize(de)?;
128 Decode::decode(&mut &r[..])
129 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
130 }
131}
132
133impl TypeInfo for DigestItem {
134 type Identity = Self;
135
136 fn type_info() -> Type {
137 Type::builder().path(Path::new("DigestItem", module_path!())).variant(
138 Variants::new()
139 .variant("PreRuntime", |v| {
140 v.index(DigestItemType::PreRuntime as u8).fields(
141 Fields::unnamed()
142 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
143 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
144 )
145 })
146 .variant("Consensus", |v| {
147 v.index(DigestItemType::Consensus as u8).fields(
148 Fields::unnamed()
149 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
150 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
151 )
152 })
153 .variant("Seal", |v| {
154 v.index(DigestItemType::Seal as u8).fields(
155 Fields::unnamed()
156 .field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
157 .field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
158 )
159 })
160 .variant("Other", |v| {
161 v.index(DigestItemType::Other as u8)
162 .fields(Fields::unnamed().field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")))
163 })
164 .variant("RuntimeEnvironmentUpdated", |v| {
165 v.index(DigestItemType::RuntimeEnvironmentUpdated as u8).fields(Fields::unit())
166 }),
167 )
168 }
169}
170
171#[derive(PartialEq, Eq, Clone, Debug)]
174pub enum DigestItemRef<'a> {
175 PreRuntime(&'a ConsensusEngineId, &'a [u8]),
182 Consensus(&'a ConsensusEngineId, &'a [u8]),
186 Seal(&'a ConsensusEngineId, &'a [u8]),
189 Other(&'a [u8]),
191 RuntimeEnvironmentUpdated,
193}
194
195#[repr(u32)]
200#[derive(Encode, Decode)]
201pub enum DigestItemType {
202 Other = 0,
203 Consensus = 4,
204 Seal = 5,
205 PreRuntime = 6,
206 RuntimeEnvironmentUpdated = 8,
207}
208
209#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
212pub enum OpaqueDigestItemId<'a> {
213 PreRuntime(&'a ConsensusEngineId),
215 Consensus(&'a ConsensusEngineId),
217 Seal(&'a ConsensusEngineId),
219 Other,
221}
222
223impl DigestItem {
224 pub fn dref(&self) -> DigestItemRef<'_> {
226 match *self {
227 Self::PreRuntime(ref v, ref s) => DigestItemRef::PreRuntime(v, s),
228 Self::Consensus(ref v, ref s) => DigestItemRef::Consensus(v, s),
229 Self::Seal(ref v, ref s) => DigestItemRef::Seal(v, s),
230 Self::Other(ref v) => DigestItemRef::Other(v),
231 Self::RuntimeEnvironmentUpdated => DigestItemRef::RuntimeEnvironmentUpdated,
232 }
233 }
234
235 pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &[u8])> {
237 self.dref().as_pre_runtime()
238 }
239
240 pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &[u8])> {
242 self.dref().as_consensus()
243 }
244
245 pub fn as_seal(&self) -> Option<(ConsensusEngineId, &[u8])> {
247 self.dref().as_seal()
248 }
249
250 pub fn as_other(&self) -> Option<&[u8]> {
252 self.dref().as_other()
253 }
254
255 pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&[u8]> {
257 self.dref().try_as_raw(id)
258 }
259
260 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| DecodeAll::decode_all(&mut x).ok())
371 }
372
373 pub fn seal_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
377 self.as_seal()
378 .filter(|s| s.0 == *id)
379 .and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
380 }
381
382 pub fn consensus_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
387 self.as_consensus()
388 .filter(|s| s.0 == *id)
389 .and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
390 }
391
392 pub fn pre_runtime_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
397 self.as_pre_runtime()
398 .filter(|s| s.0 == *id)
399 .and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
400 }
401}
402
403impl<'a> Encode for DigestItemRef<'a> {
404 fn encode(&self) -> Vec<u8> {
405 match *self {
406 Self::Consensus(val, data) => (DigestItemType::Consensus, val, data).encode(),
407 Self::Seal(val, sig) => (DigestItemType::Seal, val, sig).encode(),
408 Self::PreRuntime(val, data) => (DigestItemType::PreRuntime, val, data).encode(),
409 Self::Other(val) => (DigestItemType::Other, val).encode(),
410 Self::RuntimeEnvironmentUpdated => DigestItemType::RuntimeEnvironmentUpdated.encode(),
411 }
412 }
413}
414
415impl<'a> codec::EncodeLike for DigestItemRef<'a> {}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 #[test]
422 fn should_serialize_digest() {
423 let digest = Digest {
424 logs: vec![DigestItem::Other(vec![1, 2, 3]), DigestItem::Seal(*b"test", vec![1, 2, 3])],
425 };
426
427 assert_eq!(
428 serde_json::to_string(&digest).unwrap(),
429 r#"{"logs":["0x000c010203","0x05746573740c010203"]}"#
430 );
431 }
432
433 #[test]
434 fn digest_item_type_info() {
435 let type_info = DigestItem::type_info();
436 let variants = if let scale_info::TypeDef::Variant(variant) = type_info.type_def {
437 variant.variants
438 } else {
439 panic!("Should be a TypeDef::TypeDefVariant")
440 };
441
442 let check = |digest_item_type: DigestItemType| {
444 let (variant_name, digest_item) = match digest_item_type {
445 DigestItemType::Other => ("Other", DigestItem::Other(Default::default())),
446 DigestItemType::Consensus =>
447 ("Consensus", DigestItem::Consensus(Default::default(), Default::default())),
448 DigestItemType::Seal =>
449 ("Seal", DigestItem::Seal(Default::default(), Default::default())),
450 DigestItemType::PreRuntime =>
451 ("PreRuntime", DigestItem::PreRuntime(Default::default(), Default::default())),
452 DigestItemType::RuntimeEnvironmentUpdated =>
453 ("RuntimeEnvironmentUpdated", DigestItem::RuntimeEnvironmentUpdated),
454 };
455 let encoded = digest_item.encode();
456 let variant = variants
457 .iter()
458 .find(|v| v.name == variant_name)
459 .expect(&format!("Variant {} not found", variant_name));
460
461 assert_eq!(encoded[0], variant.index)
462 };
463
464 check(DigestItemType::Other);
465 check(DigestItemType::Consensus);
466 check(DigestItemType::Seal);
467 check(DigestItemType::PreRuntime);
468 check(DigestItemType::RuntimeEnvironmentUpdated);
469 }
470}