openzeppelin_monitor/models/blockchain/midnight/block.rs
1//! Midnight block data structures.
2//!
3//! This module provides data structures and implementations for working with Midnight blockchain blocks.
4//! It includes types for representing block headers, digests, and block data from RPC responses.
5//!
6//! Note: These structures are based on the Midnight RPC implementation:
7//! <https://github.com/midnightntwrk/midnight-node/blob/39dbdf54afc5f0be7e7913b387637ac52d0c50f2/pallets/midnight/rpc/src/lib.rs>
8
9use serde::{Deserialize, Serialize};
10use std::ops::Deref;
11
12use crate::models::MidnightRpcTransactionEnum;
13
14/// Represents a Midnight block
15///
16/// This struct contains the block header, body (transactions), and transaction indices.
17/// The transactions_index field is renamed from "transactionsIndex" in the RPC response
18/// to follow Rust naming conventions.
19///
20/// <https://github.com/midnightntwrk/midnight-node/blob/39dbdf54afc5f0be7e7913b387637ac52d0c50f2/pallets/midnight/rpc/src/lib.rs#L214-L218>
21#[derive(Clone, Debug, Serialize, Deserialize)]
22pub struct RpcBlock<Header = BlockHeader> {
23 /// The block header containing metadata about the block
24 pub header: Header,
25 /// The list of transactions in the block
26 pub body: Vec<MidnightRpcTransactionEnum>,
27 // NOTE: This should be `transactionsIndex` in the RPC response but it's not
28 // so we're using `transactions_index` here but expect this may change in the future
29 #[serde(rename = "transactions_index")]
30 pub transactions_index: Vec<(String, String)>,
31}
32
33/// Represents a Midnight block header
34///
35/// This struct contains the essential metadata for a Midnight block, including
36/// parent hash, block number, state root, and digest information.
37///
38/// Based on the response from the Midnight RPC endpoint
39/// <https://docs.midnight.network/files/Insomnia_2024-11-21.json>
40#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
41#[serde(rename_all = "camelCase")]
42pub struct BlockHeader {
43 /// Hash of the parent block
44 pub parent_hash: String, // Hash
45 /// Block number in hexadecimal format
46 pub number: String, // Hex string
47 /// State root hash representing the final state after applying all transactions
48 pub state_root: String, // Hash
49 /// Extrinsics root hash representing the Merkle root of all transactions
50 pub extrinsics_root: String, // Hash
51 /// Block digest containing additional block information
52 pub digest: BlockDigest,
53}
54
55/// Block digest containing logs
56///
57/// This struct represents the digest information for a block, which includes
58/// various logs and metadata about the block's processing.
59#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
60pub struct BlockDigest {
61 /// Vector of log entries in hexadecimal format
62 pub logs: Vec<String>, // Hex strings
63}
64
65/// Wrapper around RpcBlock that implements additional functionality
66///
67/// This type provides a convenient interface for working with Midnight block data
68/// while maintaining compatibility with the RPC response format. It implements
69/// methods for accessing and processing block information.
70#[derive(Debug, Serialize, Deserialize, Clone)]
71pub struct Block(pub RpcBlock);
72
73impl Block {
74 /// Get the block number as a decimal value
75 ///
76 /// Converts the hexadecimal block number to a decimal u64 value.
77 /// Returns None if the conversion fails.
78 ///
79 /// # Returns
80 /// * `Option<u64>` - The block number as a decimal value, or None if conversion fails
81 pub fn number(&self) -> Option<u64> {
82 Some(u64::from_str_radix(self.0.header.number.trim_start_matches("0x"), 16).unwrap_or(0))
83 }
84}
85
86impl From<RpcBlock> for Block {
87 /// Creates a new Block from an RpcBlock
88 ///
89 /// # Arguments
90 /// * `header` - The RpcBlock to convert
91 ///
92 /// # Returns
93 /// A new Block instance
94 fn from(header: RpcBlock) -> Self {
95 Self(header)
96 }
97}
98
99impl Deref for Block {
100 type Target = RpcBlock;
101
102 /// Dereferences the Block to access the underlying RpcBlock
103 ///
104 /// # Returns
105 /// A reference to the underlying RpcBlock
106 fn deref(&self) -> &Self::Target {
107 &self.0
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 /// Tests block creation and number conversion
116 #[test]
117 fn test_block_creation_and_number() {
118 let rpc_block = RpcBlock::<BlockHeader> {
119 header: BlockHeader {
120 parent_hash: "0xabc123".to_string(),
121 number: "0x12345".to_string(),
122 state_root: "0x1234567890abcdef".to_string(),
123 extrinsics_root: "0xabcdef1234567890".to_string(),
124 digest: BlockDigest { logs: vec![] },
125 },
126 body: vec![],
127 transactions_index: vec![],
128 };
129
130 let block = Block::from(rpc_block.clone());
131
132 // Test number() method
133 assert_eq!(block.number(), Some(74565u64)); // decimal representation of 0x12345
134
135 // Test Deref implementation
136 assert_eq!(block.header.parent_hash, "0xabc123");
137 assert_eq!(block.header.number, "0x12345");
138 assert_eq!(block.header.state_root, "0x1234567890abcdef");
139 assert_eq!(block.header.extrinsics_root, "0xabcdef1234567890");
140 assert_eq!(block.header.digest.logs, Vec::<String>::new());
141 }
142
143 /// Tests serialization and deserialization of Block
144 #[test]
145 fn test_serde_serialization() {
146 let rpc_block = RpcBlock {
147 header: BlockHeader {
148 parent_hash: "0xabc123".to_string(),
149 number: "0x12345".to_string(),
150 state_root: "0x1234567890abcdef".to_string(),
151 extrinsics_root: "0xabcdef1234567890".to_string(),
152 digest: BlockDigest { logs: vec![] },
153 },
154 body: vec![],
155 transactions_index: vec![],
156 };
157
158 let block = Block(rpc_block);
159
160 // Test serialization
161 let serialized = serde_json::to_string(&block).unwrap();
162
163 // Test deserialization
164 let deserialized: Block = serde_json::from_str(&serialized).unwrap();
165
166 assert_eq!(deserialized.header.parent_hash, "0xabc123");
167 assert_eq!(deserialized.number(), Some(74565u64)); // decimal representation of 0x12345
168 assert_eq!(deserialized.header.number, "0x12345");
169 assert_eq!(deserialized.header.state_root, "0x1234567890abcdef");
170 assert_eq!(deserialized.header.extrinsics_root, "0xabcdef1234567890");
171 assert_eq!(deserialized.body, vec![]);
172 assert_eq!(deserialized.transactions_index, vec![]);
173 }
174}