openzeppelin_monitor/models/blockchain/stellar/
block.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Stellar block (ledger) data structures.
//!
//! Note: These structures are based on the Stellar RPC implementation:
//! <https://github.com/stellar/stellar-rpc/blob/main/cmd/stellar-rpc/internal/methods/get_ledgers.go>

use std::ops::Deref;

use serde::{Deserialize, Serialize};
use serde_json::Value;

/// Information about a Stellar ledger (block)
///
/// This structure represents the response from the Stellar RPC endpoint
/// and matches the format defined in the stellar-rpc repository.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct LedgerInfo {
	/// Hash of the ledger
	#[serde(rename = "hash")]
	pub hash: String,

	/// Sequence number of the ledger
	#[serde(rename = "sequence")]
	pub sequence: u32,

	/// Timestamp when the ledger was closed
	#[serde(rename = "ledgerCloseTime")]
	pub ledger_close_time: String,

	/// Base64-encoded XDR of the ledger header
	#[serde(rename = "headerXdr")]
	pub ledger_header: String,

	/// Decoded JSON representation of the ledger header
	#[serde(rename = "headerJson")]
	#[serde(skip_serializing_if = "Option::is_none")]
	pub ledger_header_json: Option<Value>,

	/// Base64-encoded XDR of the ledger metadata
	#[serde(rename = "metadataXdr")]
	pub ledger_metadata: String,

	/// Decoded JSON representation of the ledger metadata
	#[serde(rename = "metadataJSON")]
	#[serde(skip_serializing_if = "Option::is_none")]
	pub ledger_metadata_json: Option<Value>,
}

/// Wrapper around LedgerInfo that implements additional functionality
///
/// This type provides a convenient interface for working with Stellar ledger data
/// while maintaining compatibility with the RPC response format.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Block(pub LedgerInfo);

impl Block {
	/// Get the block number (sequence)
	pub fn number(&self) -> Option<u64> {
		Some(self.0.sequence as u64)
	}
}

impl From<LedgerInfo> for Block {
	fn from(header: LedgerInfo) -> Self {
		Self(header)
	}
}

impl Deref for Block {
	type Target = LedgerInfo;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use serde_json::json;

	#[test]
	fn test_block_creation_and_number() {
		let ledger_info = LedgerInfo {
			hash: "abc123".to_string(),
			sequence: 12345,
			ledger_close_time: "2024-03-20T10:00:00Z".to_string(),
			ledger_header: "base64header".to_string(),
			ledger_header_json: Some(json!({"version": 1})),
			ledger_metadata: "base64metadata".to_string(),
			ledger_metadata_json: Some(json!({"operations": []})),
		};

		let block = Block::from(ledger_info.clone());

		// Test number() method
		assert_eq!(block.number(), Some(12345u64));

		// Test Deref implementation
		assert_eq!(block.hash, "abc123");
		assert_eq!(block.sequence, 12345);
		assert_eq!(block.ledger_close_time, "2024-03-20T10:00:00Z");
		assert_eq!(block.ledger_header, "base64header");
		assert_eq!(block.ledger_metadata, "base64metadata");
	}

	#[test]
	fn test_default_implementation() {
		let block = Block::default();

		assert_eq!(block.hash, "");
		assert_eq!(block.sequence, 0);
		assert_eq!(block.ledger_close_time, "");
		assert_eq!(block.ledger_header, "");
		assert_eq!(block.ledger_metadata, "");
		assert!(block.ledger_header_json.is_none());
		assert!(block.ledger_metadata_json.is_none());
	}

	#[test]
	fn test_serde_serialization() {
		let ledger_info = LedgerInfo {
			hash: "abc123".to_string(),
			sequence: 12345,
			ledger_close_time: "2024-03-20T10:00:00Z".to_string(),
			ledger_header: "base64header".to_string(),
			ledger_header_json: Some(json!({"version": 1})),
			ledger_metadata: "base64metadata".to_string(),
			ledger_metadata_json: Some(json!({"operations": []})),
		};

		let block = Block(ledger_info);

		// Test serialization
		let serialized = serde_json::to_string(&block).unwrap();

		// Test deserialization
		let deserialized: Block = serde_json::from_str(&serialized).unwrap();

		assert_eq!(deserialized.hash, "abc123");
		assert_eq!(deserialized.sequence, 12345);
		assert_eq!(deserialized.number(), Some(12345u64));
	}
}