|
| 1 | +//! Trigger serialization for Rust WASM modules. |
| 2 | +//! |
| 3 | +//! Provides the `ToRustBytes` trait for serializing blockchain triggers |
| 4 | +//! to the TLV format expected by Graphite SDK handlers. |
| 5 | +
|
| 6 | +use super::types::ToRustWasm; |
| 7 | +use std::io::{self, Write}; |
| 8 | + |
| 9 | +/// Trait for serializing trigger data to Rust WASM format. |
| 10 | +/// |
| 11 | +/// Implemented by chain-specific trigger types (e.g., Ethereum MappingTrigger). |
| 12 | +/// The serialized format matches what Graphite SDK's `FromWasmBytes` expects. |
| 13 | +pub trait ToRustBytes { |
| 14 | + /// Serialize to TLV bytes for Rust handlers. |
| 15 | + fn to_rust_bytes(&self) -> Vec<u8>; |
| 16 | +} |
| 17 | + |
| 18 | +/// Log trigger data in a format suitable for serialization. |
| 19 | +/// |
| 20 | +/// This struct provides a chain-agnostic representation of a log trigger |
| 21 | +/// that can be serialized for Rust WASM handlers. |
| 22 | +#[derive(Debug, Clone)] |
| 23 | +pub struct RustLogTrigger { |
| 24 | + /// Contract address that emitted the log (20 bytes) |
| 25 | + pub address: [u8; 20], |
| 26 | + /// Transaction hash (32 bytes) |
| 27 | + pub tx_hash: [u8; 32], |
| 28 | + /// Log index within the block |
| 29 | + pub log_index: u64, |
| 30 | + /// Block number |
| 31 | + pub block_number: u64, |
| 32 | + /// Block timestamp (Unix seconds) |
| 33 | + pub block_timestamp: u64, |
| 34 | + /// Log topics (event selector + indexed params) |
| 35 | + pub topics: Vec<[u8; 32]>, |
| 36 | + /// ABI-encoded non-indexed event data |
| 37 | + pub data: Vec<u8>, |
| 38 | +} |
| 39 | + |
| 40 | +impl ToRustBytes for RustLogTrigger { |
| 41 | + fn to_rust_bytes(&self) -> Vec<u8> { |
| 42 | + let mut buf = Vec::new(); |
| 43 | + self.write_to(&mut buf).expect("write to vec cannot fail"); |
| 44 | + buf |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +impl ToRustWasm for RustLogTrigger { |
| 49 | + fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> { |
| 50 | + // Fixed-size fields first (no length prefix) |
| 51 | + writer.write_all(&self.address)?; // 20 bytes |
| 52 | + writer.write_all(&self.tx_hash)?; // 32 bytes |
| 53 | + writer.write_all(&self.log_index.to_le_bytes())?; // 8 bytes |
| 54 | + writer.write_all(&self.block_number.to_le_bytes())?; // 8 bytes |
| 55 | + writer.write_all(&self.block_timestamp.to_le_bytes())?; // 8 bytes |
| 56 | + |
| 57 | + // Topics array: count + data |
| 58 | + writer.write_all(&(self.topics.len() as u32).to_le_bytes())?; |
| 59 | + for topic in &self.topics { |
| 60 | + writer.write_all(topic)?; // 32 bytes each |
| 61 | + } |
| 62 | + |
| 63 | + // Data: length + bytes |
| 64 | + writer.write_all(&(self.data.len() as u32).to_le_bytes())?; |
| 65 | + writer.write_all(&self.data)?; |
| 66 | + |
| 67 | + Ok(()) |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +#[cfg(test)] |
| 72 | +mod tests { |
| 73 | + use super::*; |
| 74 | + |
| 75 | + #[test] |
| 76 | + fn serialize_log_trigger() { |
| 77 | + let trigger = RustLogTrigger { |
| 78 | + address: [0xde; 20], |
| 79 | + tx_hash: [0xab; 32], |
| 80 | + log_index: 42, |
| 81 | + block_number: 12345678, |
| 82 | + block_timestamp: 1700000000, |
| 83 | + topics: vec![[0x11; 32], [0x22; 32]], |
| 84 | + data: vec![1, 2, 3, 4], |
| 85 | + }; |
| 86 | + |
| 87 | + let bytes = trigger.to_rust_bytes(); |
| 88 | + |
| 89 | + // Verify structure: |
| 90 | + // 20 (address) + 32 (tx_hash) + 8*3 (log_index, block_number, timestamp) |
| 91 | + // + 4 (topics count) + 64 (2 topics) + 4 (data len) + 4 (data) |
| 92 | + assert_eq!(bytes.len(), 20 + 32 + 24 + 4 + 64 + 4 + 4); |
| 93 | + |
| 94 | + // Check address |
| 95 | + assert_eq!(&bytes[0..20], &[0xde; 20]); |
| 96 | + |
| 97 | + // Check topics count |
| 98 | + let topics_offset = 20 + 32 + 24; |
| 99 | + let topics_count = u32::from_le_bytes(bytes[topics_offset..topics_offset+4].try_into().unwrap()); |
| 100 | + assert_eq!(topics_count, 2); |
| 101 | + } |
| 102 | +} |
0 commit comments