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}