Skip to main content

hypercall_blocks/
entry.rs

1use crate::BlockLogHeader;
2use serde::{Deserialize, Serialize};
3
4/// Serializable block-log entry: signed header plus all command records.
5///
6/// The command payload type is generic because this crate owns only the block
7/// envelope. The runtime chooses the command record wire type for each log.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct BlockLogEntry<C> {
10    pub header: BlockLogHeader,
11    pub commands: Vec<C>,
12}
13
14#[cfg(test)]
15mod tests {
16    use super::*;
17
18    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19    struct TestCommand {
20        id: u64,
21        payload: Vec<u8>,
22    }
23
24    #[test]
25    fn block_log_entry_roundtrips_with_commands() {
26        let entry = BlockLogEntry {
27            header: header(2),
28            commands: vec![
29                TestCommand {
30                    id: 1,
31                    payload: vec![1, 2, 3],
32                },
33                TestCommand {
34                    id: 2,
35                    payload: vec![4, 5, 6],
36                },
37            ],
38        };
39
40        assert_eq!(msgpack_roundtrip(&entry), entry);
41    }
42
43    #[test]
44    fn block_log_entry_roundtrips_empty_command_batch() {
45        let entry = BlockLogEntry::<TestCommand> {
46            header: header(0),
47            commands: Vec::new(),
48        };
49
50        assert_eq!(msgpack_roundtrip(&entry), entry);
51    }
52
53    #[test]
54    fn block_entry_equality_includes_header_and_commands() {
55        let base = BlockLogEntry {
56            header: header(1),
57            commands: vec![TestCommand {
58                id: 1,
59                payload: vec![1],
60            }],
61        };
62
63        let mut changed_header = base.clone();
64        changed_header.header.batch_seq += 1;
65        assert_ne!(base, changed_header);
66
67        let mut changed_command = base.clone();
68        changed_command.commands[0].payload.push(2);
69        assert_ne!(base, changed_command);
70    }
71
72    fn header(command_count: u64) -> BlockLogHeader {
73        BlockLogHeader {
74            batch_seq: 7,
75            prev_block_hash: [1; 32],
76            commands_hash: [2; 32],
77            batch_root: [3; 32],
78            command_count,
79            first_seq: 42,
80            timestamp: 1_700_000_000,
81            signer: [4; 20],
82            signature: vec![5; 65],
83        }
84    }
85
86    fn msgpack_roundtrip<T>(value: &T) -> T
87    where
88        T: Clone + PartialEq + std::fmt::Debug + Serialize + for<'de> Deserialize<'de>,
89    {
90        let bytes = rmp_serde::to_vec_named(value).expect("serialize block log entry");
91        rmp_serde::from_slice(&bytes).expect("deserialize block log entry")
92    }
93}