amaru_kernel/cardano/
block.rs1use std::collections::{BTreeMap, BTreeSet};
16
17use crate::{
18 AuxiliaryData, Hash, Hasher, Header, HeaderHash, Transaction, TransactionBody, WitnessSet, cbor,
19 size::{BLOCK_BODY, HEADER},
20};
21
22#[derive(Debug, Clone, PartialEq, cbor::Encode, serde::Serialize, serde::Deserialize)]
23pub struct Block {
24 #[cbor(skip)]
25 original_body_size: u64,
26
27 #[cbor(skip)]
28 original_header_size: u64,
29
30 #[cbor(skip)]
31 hash: Hash<BLOCK_BODY>,
32
33 #[cbor(skip)]
34 header_hash: HeaderHash,
35
36 #[n(0)]
37 pub header: Header,
38
39 #[b(1)]
40 pub transaction_bodies: Vec<TransactionBody>,
41
42 #[n(2)]
43 pub transaction_witnesses: Vec<WitnessSet>,
44
45 #[n(3)]
46 pub auxiliary_data: BTreeMap<u32, AuxiliaryData>,
47
48 #[n(4)]
49 pub invalid_transactions: Option<BTreeSet<u32>>,
50}
51
52impl Block {
53 pub fn body_len(&self) -> u64 {
55 self.original_body_size
56 }
57
58 pub fn header_len(&self) -> u64 {
60 self.original_header_size
61 }
62
63 pub fn header_hash(&self) -> HeaderHash {
64 self.header_hash
65 }
66}
67
68impl IntoIterator for Block {
69 type Item = (u32, Transaction);
70 type IntoIter = std::vec::IntoIter<Self::Item>;
71
72 fn into_iter(mut self) -> Self::IntoIter {
73 (0u32..)
74 .zip(self.transaction_bodies)
75 .zip(self.transaction_witnesses)
76 .map(|((i, body), witnesses)| {
77 let is_expected_valid =
78 !self.invalid_transactions.as_ref().map(|set| set.contains(&i)).unwrap_or(false);
79
80 let auxiliary_data: Option<AuxiliaryData> = self.auxiliary_data.remove(&i);
82
83 (i, Transaction { body, witnesses, is_expected_valid, auxiliary_data })
84 })
85 .collect::<Vec<_>>()
86 .into_iter()
87 }
88}
89
90impl<'b, C> cbor::Decode<'b, C> for Block {
104 fn decode(d: &mut cbor::Decoder<'b>, ctx: &mut C) -> Result<Self, cbor::decode::Error> {
105 cbor::heterogeneous_array(d, |d, assert_len| {
106 assert_len(5)?;
107
108 let (header, header_bytes) = cbor::tee(d, |d| d.decode_with(ctx))?;
109
110 let (transaction_bodies, transaction_bodies_bytes) = cbor::tee(d, |d| d.decode_with(ctx))?;
111
112 let (transaction_witnesses, transaction_witnesses_bytes) = cbor::tee(d, |d| d.decode_with(ctx))?;
113
114 let (auxiliary_data, auxiliary_data_bytes) = cbor::tee(d, |d| d.decode_with(ctx))?;
115
116 let (invalid_transactions, invalid_transactions_bytes) = cbor::tee(d, |d| d.decode_with(ctx))?;
117
118 let mut block_body_hash = Vec::with_capacity(4 * BLOCK_BODY);
119 for component in [
120 transaction_bodies_bytes,
121 transaction_witnesses_bytes,
122 auxiliary_data_bytes,
123 invalid_transactions_bytes,
124 ] {
125 let body_part = Hasher::<{ 8 * BLOCK_BODY }>::hash(component);
126 block_body_hash.extend_from_slice(&body_part[..]);
127 }
128
129 Ok(Block {
130 original_body_size: (transaction_bodies_bytes.len()
131 + transaction_witnesses_bytes.len()
132 + auxiliary_data_bytes.len()
133 + invalid_transactions_bytes.len()) as u64,
134 original_header_size: header_bytes.len() as u64,
135 hash: Hasher::<{ 8 * BLOCK_BODY }>::hash(&block_body_hash[..]),
136 header_hash: Hasher::<{ 8 * HEADER }>::hash(header_bytes),
137 header,
138 transaction_bodies,
139 transaction_witnesses,
140 auxiliary_data,
141 invalid_transactions,
142 })
143 })
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use test_case::test_case;
150
151 use super::*;
152 use crate::EraName;
153
154 macro_rules! fixture {
155 ($id:expr) => {{
156 (
157 Hash::from(&hex::decode($id).unwrap()[..]),
158 $crate::try_include_cbor!(concat!("cbor.decode/block/", $id, "/sample.cbor")),
159 )
160 }};
161 }
162
163 #[test_case(
164 70175999,
165 fixture!("b9bef52dd8dedf992837d20c18399a284d80fde0ae9435f2a33649aaee7c5698")
166 )]
167 #[test_case(
168 70206662,
169 fixture!("b99a61170fcdb5bade252be2cb0fa6e3ac550b9f5cc4e9d001eda88291eb9de7")
170 )]
171 #[test_case(
172 70225763,
173 fixture!("313e774e32c23b3691751e62d6b57181538cf3164b242505919bce29226de19f")
174 )]
175 #[test_case(
176 70582226,
177 fixture!("e1b90d83d6ae89860e2d1a0f398355cd4ed6defddb028dd610748d1f5610b546")
178 )]
179 #[test_case(
180 71419349,
181 fixture!("0df40008e40348c40cdc3b92a1e31d0e55675ddf2bb05ff7683b38a837048bca")
182 )]
183 fn decode_wellformed(slot: u64, (id, result): (Hash<HEADER>, Result<(EraName, Block), cbor::decode::Error>)) {
184 match result {
185 Err(err) => panic!("{err}"),
186 Ok((era_version, block)) => {
187 assert_eq!(era_version, EraName::Conway);
188
189 assert_eq!(hex::encode(&block.hash[..]), hex::encode(&block.header.header_body.block_body_hash[..]),);
190
191 assert_eq!(block.header_hash, id);
192 assert_eq!(block.header.header_body.slot, slot);
193 }
194 }
195 }
196}