|
| 1 | +//! Log payload types for orchestration layer logs. |
| 2 | +
|
| 3 | +use strata_codec::{Codec, VarVec}; |
| 4 | + |
| 5 | +/// Payload for a simple withdrawal intent log. |
| 6 | +/// |
| 7 | +/// Emitted by the OL STF when a withdrawal message is processed at the bridge |
| 8 | +/// gateway account. |
| 9 | +#[derive(Debug, Clone, PartialEq, Eq, Codec)] |
| 10 | +pub struct SimpleWithdrawalIntentLogData { |
| 11 | + /// Amount being withdrawn (sats). |
| 12 | + pub amt: u64, |
| 13 | + |
| 14 | + /// Destination BOSD. |
| 15 | + pub dest: VarVec<u8>, |
| 16 | + |
| 17 | + /// User's selected operator index for withdrawal assignment. |
| 18 | + // TODO(STR-1861): encode as varint to reduce DA cost in checkpoint payloads. |
| 19 | + pub selected_operator: u32, |
| 20 | +} |
| 21 | + |
| 22 | +impl SimpleWithdrawalIntentLogData { |
| 23 | + /// Create a new simple withdrawal intent log data instance. |
| 24 | + pub fn new(amt: u64, dest: Vec<u8>, selected_operator: u32) -> Option<Self> { |
| 25 | + let dest = VarVec::from_vec(dest)?; |
| 26 | + Some(Self { |
| 27 | + amt, |
| 28 | + dest, |
| 29 | + selected_operator, |
| 30 | + }) |
| 31 | + } |
| 32 | + |
| 33 | + /// Get the withdrawal amount. |
| 34 | + pub fn amt(&self) -> u64 { |
| 35 | + self.amt |
| 36 | + } |
| 37 | + |
| 38 | + /// Get the destination as bytes. |
| 39 | + pub fn dest(&self) -> &[u8] { |
| 40 | + self.dest.as_ref() |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +/// Payload for a snark account update log. |
| 45 | +/// |
| 46 | +/// This log is emitted when a snark account is updated through a transaction. |
| 47 | +/// It contains the new message index (sequence number) and any extra data |
| 48 | +/// from the update operation. |
| 49 | +#[derive(Debug, Clone, PartialEq, Eq, Codec)] |
| 50 | +pub struct SnarkAccountUpdateLogData { |
| 51 | + /// The new message index (sequence number) after the update. |
| 52 | + pub new_msg_idx: u64, |
| 53 | + |
| 54 | + /// Extra data from the update operation. |
| 55 | + pub extra_data: VarVec<u8>, |
| 56 | +} |
| 57 | + |
| 58 | +impl SnarkAccountUpdateLogData { |
| 59 | + /// Create a new snark account update log data instance. |
| 60 | + pub fn new(new_msg_idx: u64, extra_data: Vec<u8>) -> Option<Self> { |
| 61 | + let extra_data = VarVec::from_vec(extra_data)?; |
| 62 | + Some(Self { |
| 63 | + new_msg_idx, |
| 64 | + extra_data, |
| 65 | + }) |
| 66 | + } |
| 67 | + |
| 68 | + /// Get the new message index. |
| 69 | + pub fn new_msg_idx(&self) -> u64 { |
| 70 | + self.new_msg_idx |
| 71 | + } |
| 72 | + |
| 73 | + /// Get the extra data as bytes. |
| 74 | + pub fn extra_data(&self) -> &[u8] { |
| 75 | + self.extra_data.as_ref() |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +#[cfg(test)] |
| 80 | +mod tests { |
| 81 | + use strata_codec::{decode_buf_exact, encode_to_vec}; |
| 82 | + |
| 83 | + use super::*; |
| 84 | + |
| 85 | + #[test] |
| 86 | + fn test_simple_withdrawal_intent_log_data_codec() { |
| 87 | + // Create test data |
| 88 | + let log_data = SimpleWithdrawalIntentLogData { |
| 89 | + amt: 100_000_000, // 1 BTC |
| 90 | + dest: VarVec::from_vec(b"bc1qtest123456789".to_vec()).unwrap(), |
| 91 | + selected_operator: 42, |
| 92 | + }; |
| 93 | + |
| 94 | + // Encode |
| 95 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 96 | + |
| 97 | + // Decode |
| 98 | + let decoded: SimpleWithdrawalIntentLogData = decode_buf_exact(&encoded).unwrap(); |
| 99 | + |
| 100 | + // Verify round-trip |
| 101 | + assert_eq!(decoded.amt, log_data.amt); |
| 102 | + assert_eq!(decoded.dest.as_ref(), log_data.dest.as_ref()); |
| 103 | + assert_eq!(decoded.selected_operator, log_data.selected_operator); |
| 104 | + } |
| 105 | + |
| 106 | + #[test] |
| 107 | + fn test_simple_withdrawal_intent_empty_dest() { |
| 108 | + // Test with empty destination (probably invalid, but codec should handle it) |
| 109 | + let log_data = SimpleWithdrawalIntentLogData { |
| 110 | + amt: 50_000, |
| 111 | + dest: VarVec::from_vec(vec![]).unwrap(), |
| 112 | + selected_operator: 0, |
| 113 | + }; |
| 114 | + |
| 115 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 116 | + let decoded: SimpleWithdrawalIntentLogData = decode_buf_exact(&encoded).unwrap(); |
| 117 | + |
| 118 | + assert_eq!(decoded.amt, 50_000); |
| 119 | + assert!(decoded.dest.is_empty()); |
| 120 | + } |
| 121 | + |
| 122 | + #[test] |
| 123 | + fn test_simple_withdrawal_intent_max_values() { |
| 124 | + // Test with maximum values |
| 125 | + let log_data = SimpleWithdrawalIntentLogData { |
| 126 | + amt: u64::MAX, |
| 127 | + dest: VarVec::from_vec(vec![255u8; 200]).unwrap(), |
| 128 | + selected_operator: u32::MAX, |
| 129 | + }; |
| 130 | + |
| 131 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 132 | + let decoded: SimpleWithdrawalIntentLogData = decode_buf_exact(&encoded).unwrap(); |
| 133 | + |
| 134 | + assert_eq!(decoded.amt, u64::MAX); |
| 135 | + assert_eq!(decoded.dest.len(), 200); |
| 136 | + assert_eq!(decoded.dest.as_ref(), &vec![255u8; 200][..]); |
| 137 | + } |
| 138 | + |
| 139 | + #[test] |
| 140 | + fn test_simple_withdrawal_intent_zero_amount() { |
| 141 | + // Test with zero amount |
| 142 | + let log_data = SimpleWithdrawalIntentLogData { |
| 143 | + amt: 0, |
| 144 | + dest: VarVec::from_vec(b"addr1test".to_vec()).unwrap(), |
| 145 | + selected_operator: 5, |
| 146 | + }; |
| 147 | + |
| 148 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 149 | + let decoded: SimpleWithdrawalIntentLogData = decode_buf_exact(&encoded).unwrap(); |
| 150 | + |
| 151 | + assert_eq!(decoded.amt, 0); |
| 152 | + assert_eq!(decoded.dest.as_ref(), b"addr1test"); |
| 153 | + } |
| 154 | + |
| 155 | + #[test] |
| 156 | + fn test_snark_account_update_log_data_codec() { |
| 157 | + // Create test data |
| 158 | + let log_data = SnarkAccountUpdateLogData { |
| 159 | + new_msg_idx: 12345, |
| 160 | + extra_data: VarVec::from_vec(b"extra_test_data".to_vec()).unwrap(), |
| 161 | + }; |
| 162 | + |
| 163 | + // Encode |
| 164 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 165 | + |
| 166 | + // Decode |
| 167 | + let decoded: SnarkAccountUpdateLogData = decode_buf_exact(&encoded).unwrap(); |
| 168 | + |
| 169 | + // Verify round-trip |
| 170 | + assert_eq!(decoded.new_msg_idx, log_data.new_msg_idx); |
| 171 | + assert_eq!(decoded.extra_data.as_ref(), log_data.extra_data.as_ref()); |
| 172 | + } |
| 173 | + |
| 174 | + #[test] |
| 175 | + fn test_snark_account_update_empty_extra_data() { |
| 176 | + // Test with empty extra data |
| 177 | + let log_data = SnarkAccountUpdateLogData { |
| 178 | + new_msg_idx: 999, |
| 179 | + extra_data: VarVec::from_vec(vec![]).unwrap(), |
| 180 | + }; |
| 181 | + |
| 182 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 183 | + let decoded: SnarkAccountUpdateLogData = decode_buf_exact(&encoded).unwrap(); |
| 184 | + |
| 185 | + assert_eq!(decoded.new_msg_idx, 999); |
| 186 | + assert!(decoded.extra_data.is_empty()); |
| 187 | + } |
| 188 | + |
| 189 | + #[test] |
| 190 | + fn test_snark_account_update_max_values() { |
| 191 | + // Test with maximum values |
| 192 | + let log_data = SnarkAccountUpdateLogData { |
| 193 | + new_msg_idx: u64::MAX, |
| 194 | + extra_data: VarVec::from_vec(vec![255u8; 250]).unwrap(), |
| 195 | + }; |
| 196 | + |
| 197 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 198 | + let decoded: SnarkAccountUpdateLogData = decode_buf_exact(&encoded).unwrap(); |
| 199 | + |
| 200 | + assert_eq!(decoded.new_msg_idx, u64::MAX); |
| 201 | + assert_eq!(decoded.extra_data.len(), 250); |
| 202 | + assert_eq!(decoded.extra_data.as_ref(), &vec![255u8; 250][..]); |
| 203 | + } |
| 204 | + |
| 205 | + #[test] |
| 206 | + fn test_snark_account_update_zero_msg_idx() { |
| 207 | + // Test with zero message index |
| 208 | + let log_data = SnarkAccountUpdateLogData { |
| 209 | + new_msg_idx: 0, |
| 210 | + extra_data: VarVec::from_vec(b"test".to_vec()).unwrap(), |
| 211 | + }; |
| 212 | + |
| 213 | + let encoded = encode_to_vec(&log_data).unwrap(); |
| 214 | + let decoded: SnarkAccountUpdateLogData = decode_buf_exact(&encoded).unwrap(); |
| 215 | + |
| 216 | + assert_eq!(decoded.new_msg_idx, 0); |
| 217 | + assert_eq!(decoded.extra_data.as_ref(), b"test"); |
| 218 | + } |
| 219 | +} |
0 commit comments