openzeppelin_monitor/models/blockchain/
mod.rs

1//! Blockchain-specific model implementations.
2//!
3//! This module contains type definitions and implementations for different
4//! blockchain platforms (EVM, Stellar, Midnight, Solana, etc). Each submodule implements the
5//! platform-specific logic for blocks, transactions, and event monitoring.
6
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10pub mod evm;
11pub mod midnight;
12pub mod solana;
13pub mod stellar;
14
15/// Rules for function and event signature validation
16#[derive(Debug, Clone)]
17pub struct SignatureRules {
18	/// Whether the blockchain requires parentheses in signatures
19	pub requires_parentheses: bool,
20}
21
22/// Supported blockchain platform types
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
24#[serde(deny_unknown_fields)]
25pub enum BlockChainType {
26	/// Ethereum Virtual Machine based chains
27	EVM,
28	/// Stellar blockchain
29	Stellar,
30	/// Midnight blockchain
31	Midnight,
32	/// Solana blockchain
33	Solana,
34}
35
36impl BlockChainType {
37	/// Returns the signature validation rules for this blockchain type.
38	///
39	/// Different blockchains have different signature formats:
40	/// - EVM-style chains require signatures like `transfer(address,uint256)`
41	/// - Solana allows raw instruction names like `transfer`
42	pub fn signature_rules(&self) -> SignatureRules {
43		match self {
44			BlockChainType::EVM | BlockChainType::Stellar | BlockChainType::Midnight => {
45				SignatureRules {
46					requires_parentheses: true,
47				}
48			}
49			BlockChainType::Solana => SignatureRules {
50				requires_parentheses: false,
51			},
52		}
53	}
54}
55
56impl fmt::Display for BlockChainType {
57	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58		match self {
59			BlockChainType::EVM => write!(f, "EVM"),
60			BlockChainType::Stellar => write!(f, "Stellar"),
61			BlockChainType::Midnight => write!(f, "Midnight"),
62			BlockChainType::Solana => write!(f, "Solana"),
63		}
64	}
65}
66
67/// Block data from different blockchain platforms
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub enum BlockType {
70	/// EVM block and transaction data
71	///
72	/// # Note
73	/// Box is used here to equalize the enum variants
74	EVM(Box<evm::EVMBlock>),
75	/// Stellar ledger and transaction data
76	///
77	/// # Note
78	/// Box is used here to equalize the enum variants
79	Stellar(Box<stellar::StellarBlock>),
80	/// Midnight block and transaction data
81	///
82	/// # Note
83	/// Box is used here to equalize the enum variants
84	Midnight(Box<midnight::MidnightBlock>),
85	/// Solana slot and transaction data
86	///
87	/// # Note
88	/// Box is used here to equalize the enum variants
89	Solana(Box<solana::SolanaBlock>),
90}
91
92impl BlockType {
93	pub fn number(&self) -> Option<u64> {
94		match self {
95			BlockType::EVM(b) => b.number(),
96			BlockType::Stellar(b) => b.number(),
97			BlockType::Midnight(b) => b.number(),
98			BlockType::Solana(b) => b.number(),
99		}
100	}
101}
102
103/// Transaction data from different blockchain platforms
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[allow(clippy::large_enum_variant)]
106pub enum TransactionType {
107	/// EVM transaction
108	EVM(evm::EVMTransaction),
109	/// Stellar transaction
110	Stellar(Box<stellar::StellarTransaction>),
111	/// Midnight transaction
112	Midnight(midnight::MidnightTransaction),
113	/// Solana transaction
114	Solana(Box<solana::SolanaTransaction>),
115}
116
117/// Contract spec from different blockchain platforms
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
119#[serde(untagged)]
120pub enum ContractSpec {
121	/// EVM contract spec
122	EVM(evm::EVMContractSpec),
123	/// Stellar contract spec
124	Stellar(stellar::StellarContractSpec),
125	/// Midnight contract spec
126	Midnight,
127	/// Solana contract spec (IDL)
128	Solana(solana::SolanaContractSpec),
129}
130
131/// Monitor match results from different blockchain platforms
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub enum MonitorMatch {
134	/// Matched conditions from EVM chains
135	///
136	/// # Note
137	/// Box is used here to equalize the enum variants
138	EVM(Box<evm::EVMMonitorMatch>),
139	/// Matched conditions from Stellar chains
140	///
141	/// # Note
142	/// Box is used here to equalize the enum variants
143	Stellar(Box<stellar::StellarMonitorMatch>),
144	/// Matched conditions from Midnight chains
145	///
146	/// # Note
147	/// Box is used here to equalize the enum variants
148	Midnight(Box<midnight::MidnightMonitorMatch>),
149	/// Matched conditions from Solana chains
150	///
151	/// # Note
152	/// Box is used here to equalize the enum variants
153	Solana(Box<solana::SolanaMonitorMatch>),
154}
155
156/// Chain-specific configuration
157#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
158pub struct ChainConfiguration {
159	/// Midnight-specific configuration
160	#[serde(skip_serializing_if = "Option::is_none")]
161	pub midnight: Option<midnight::MidnightMonitorConfig>,
162
163	/// EVM-specific configuration
164	#[serde(skip_serializing_if = "Option::is_none")]
165	pub evm: Option<evm::EVMMonitorConfig>,
166
167	/// Stellar-specific configuration
168	#[serde(skip_serializing_if = "Option::is_none")]
169	pub stellar: Option<stellar::StellarMonitorConfig>,
170
171	/// Solana-specific configuration
172	#[serde(skip_serializing_if = "Option::is_none")]
173	pub solana: Option<solana::SolanaMonitorConfig>,
174}
175
176/// Structure to hold block processing results
177///
178/// This is used to pass the results of block processing to the trigger handler
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct ProcessedBlock {
181	pub block_number: u64,
182	pub network_slug: String,
183	pub processing_results: Vec<MonitorMatch>,
184}
185
186#[cfg(test)]
187mod tests {
188	use super::*;
189
190	#[test]
191	fn test_block_type_number_evm() {
192		use alloy::rpc::types::{Block, Header};
193
194		let evm_block = evm::EVMBlock::from(Block {
195			header: Header {
196				inner: alloy::consensus::Header {
197					number: 12345,
198					..Default::default()
199				},
200				..Default::default()
201			},
202			..Default::default()
203		});
204
205		let block_type = BlockType::EVM(Box::new(evm_block));
206		assert_eq!(block_type.number(), Some(12345));
207	}
208
209	#[test]
210	fn test_block_type_number_solana() {
211		let solana_block = solana::SolanaBlock::from(solana::SolanaConfirmedBlock {
212			slot: 999888777,
213			blockhash: "test_hash".to_string(),
214			previous_blockhash: "prev_hash".to_string(),
215			parent_slot: 999888776,
216			block_time: Some(1234567890),
217			block_height: Some(100000),
218			transactions: vec![],
219		});
220
221		let block_type = BlockType::Solana(Box::new(solana_block));
222		assert_eq!(block_type.number(), Some(999888777));
223	}
224
225	#[test]
226	fn test_block_type_number_stellar() {
227		let stellar_block = stellar::StellarBlock::from(stellar::StellarLedgerInfo {
228			sequence: 54321,
229			hash: "stellar_hash".to_string(),
230			ledger_close_time: "2024-01-01T00:00:00Z".to_string(),
231			ledger_header: "base64header".to_string(),
232			ledger_header_json: None,
233			ledger_metadata: "base64metadata".to_string(),
234			ledger_metadata_json: None,
235		});
236
237		let block_type = BlockType::Stellar(Box::new(stellar_block));
238		assert_eq!(block_type.number(), Some(54321));
239	}
240
241	#[test]
242	fn test_block_type_number_midnight() {
243		let midnight_block = midnight::MidnightBlock::from(midnight::MidnightRpcBlock {
244			header: midnight::MidnightBlockHeader {
245				parent_hash: "0xparent_hash".to_string(),
246				number: "0x12fd1".to_string(), // 77777 in hex
247				state_root: "0xstate_root".to_string(),
248				extrinsics_root: "0xextrinsics_root".to_string(),
249				digest: midnight::MidnightBlockDigest { logs: vec![] },
250			},
251			body: vec![],
252			transactions_index: vec![],
253		});
254
255		let block_type = BlockType::Midnight(Box::new(midnight_block));
256		assert_eq!(block_type.number(), Some(77777));
257	}
258
259	#[test]
260	fn test_blockchain_type_variants() {
261		// Ensure all blockchain types are correctly represented
262		assert_eq!(BlockChainType::EVM, BlockChainType::EVM);
263		assert_eq!(BlockChainType::Stellar, BlockChainType::Stellar);
264		assert_eq!(BlockChainType::Midnight, BlockChainType::Midnight);
265		assert_eq!(BlockChainType::Solana, BlockChainType::Solana);
266
267		// Test that different types are not equal
268		assert_ne!(BlockChainType::EVM, BlockChainType::Solana);
269		assert_ne!(BlockChainType::Stellar, BlockChainType::Midnight);
270	}
271
272	#[test]
273	fn test_signature_rules() {
274		// Test EVM requires parentheses
275		let evm_rules = BlockChainType::EVM.signature_rules();
276		assert!(evm_rules.requires_parentheses);
277
278		// Test Stellar requires parentheses
279		let stellar_rules = BlockChainType::Stellar.signature_rules();
280		assert!(stellar_rules.requires_parentheses);
281
282		// Test Midnight requires parentheses
283		let midnight_rules = BlockChainType::Midnight.signature_rules();
284		assert!(midnight_rules.requires_parentheses);
285
286		// Test Solana does not require parentheses
287		let solana_rules = BlockChainType::Solana.signature_rules();
288		assert!(!solana_rules.requires_parentheses);
289	}
290
291	#[test]
292	fn test_blockchain_type_display() {
293		assert_eq!(format!("{}", BlockChainType::EVM), "EVM");
294		assert_eq!(format!("{}", BlockChainType::Stellar), "Stellar");
295		assert_eq!(format!("{}", BlockChainType::Midnight), "Midnight");
296		assert_eq!(format!("{}", BlockChainType::Solana), "Solana");
297	}
298}