Skip to main content

amaru_kernel/cardano/
raw_block.rs

1// Copyright 2026 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{fmt, ops::Deref, sync::Arc};
16
17use minicbor::decode;
18
19use crate::{Block, cardano::network_block::NetworkBlock};
20
21/// Cheaply cloneable block bytes
22#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
23pub struct RawBlock(Arc<[u8]>);
24
25impl fmt::Debug for RawBlock {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        let bytes = &self.0;
28        let total_len = bytes.len();
29        let preview_len = 32.min(total_len);
30        let preview = &bytes[0..preview_len];
31
32        let mut preview_hex = String::with_capacity(2 * preview_len + 3);
33        for &b in preview {
34            const HEX_CHARS: [u8; 16] = *b"0123456789abcdef";
35            preview_hex.push(HEX_CHARS[(b >> 4) as usize] as char);
36            preview_hex.push(HEX_CHARS[(b & 0x0f) as usize] as char);
37        }
38        if preview_len < total_len {
39            preview_hex.push_str("...");
40        }
41
42        write!(f, "RawBlock({total_len}, {preview_hex})")
43    }
44}
45
46impl Deref for RawBlock {
47    type Target = [u8];
48
49    fn deref(&self) -> &Self::Target {
50        &self.0
51    }
52}
53
54impl From<&[u8]> for RawBlock {
55    fn from(bytes: &[u8]) -> Self {
56        Self(Arc::from(bytes))
57    }
58}
59
60impl From<Box<[u8]>> for RawBlock {
61    fn from(bytes: Box<[u8]>) -> Self {
62        Self(Arc::from(bytes))
63    }
64}
65
66impl RawBlock {
67    /// Decode the inner Block by first decoding the raw bytes as a NetworkBlock (which contains the
68    /// era tag), then by decoding the Block.
69    pub fn decode(&self) -> Result<Block, decode::Error> {
70        let network_block: NetworkBlock = minicbor::decode(&self.0)?;
71        network_block.decode_block()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use amaru_minicbor_extra::{from_cbor, to_cbor};
78
79    use crate::{
80        BlockHeader, TESTNET_ERA_HISTORY,
81        cardano::network_block::{NetworkBlock, make_block_with_header},
82        make_header,
83    };
84
85    #[test]
86    fn decode_returns_inner_block() {
87        let header = BlockHeader::from(make_header(1, 42, None));
88        let era_history = &*TESTNET_ERA_HISTORY;
89
90        // make a network block from a block
91        let block = make_block_with_header(&header);
92
93        // first check the round-trip encoding / decoding for a block
94        assert_eq!(block, from_cbor(to_cbor(&block).as_slice()).unwrap());
95
96        // then check that the block can be retrieved from the network block
97        let network_block = NetworkBlock::new(era_history, &block).expect("make network block");
98        let decoded_block = network_block.decode_block().expect("network block should decode");
99        assert_eq!(decoded_block, block);
100
101        // finally check that the block can be retrieved from the raw block
102        let raw_block = network_block.raw_block();
103        let decoded_block = raw_block.decode().expect("raw block should decode");
104        assert_eq!(decoded_block, block);
105    }
106}