1use crate::{
4 txtype::{
5 COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930,
6 COMPACT_IDENTIFIER_LEGACY,
7 },
8 Compact,
9};
10use alloy_consensus::{
11 transaction::{TxEip1559, TxEip2930, TxEip7702, TxLegacy},
12 Signed, TxEip4844Variant,
13};
14use alloy_consensus::TxEip4844;
15use alloy_eips::eip2718::{EIP7702_TX_TYPE_ID, EIP4844_TX_TYPE_ID};
16use alloy_primitives::{aliases::U96, Bytes, ChainId, Signature, TxKind, U256};
17use bytes::{Buf, BufMut, BytesMut};
18use reth_codecs_derive::generate_tests;
19use seismic_alloy_consensus::{
20 transaction::TxSeismicElements, SeismicTxEnvelope, SeismicTxType, SeismicTypedTransaction,
21 TxSeismic as AlloyTxSeismic, SEISMIC_TX_TYPE_ID,
22};
23
24use super::ethereum::{CompactEnvelope, Envelope, FromTxCompact, ToTxCompact};
25
26#[derive(Debug, Clone, PartialEq, Eq, Default, Compact)]
28#[reth_codecs(crate = "crate")]
29#[cfg_attr(
30 any(test, feature = "test-utils"),
31 derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize),
32 crate::add_arbitrary_tests(crate, compact)
33)]
34#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
35pub(crate) struct TxSeismic {
36 chain_id: ChainId,
38 nonce: u64,
40 gas_price: u128,
48 gas_limit: u64,
54 to: TxKind,
57 value: U256,
62 seismic_elements: TxSeismicElements,
64 input: Bytes,
70}
71
72impl Compact for TxSeismicElements {
73 fn to_compact<B>(&self, buf: &mut B) -> usize
74 where
75 B: bytes::BufMut + AsMut<[u8]>,
76 {
77 let mut len = 0;
78 len += self.encryption_pubkey.serialize().to_compact(buf);
79
80 buf.put_u8(self.message_version);
81 len += core::mem::size_of::<u8>();
82
83 let mut cache = BytesMut::new();
84 let nonce_len = self.encryption_nonce.to_compact(&mut cache);
85 buf.put_u8(nonce_len as u8);
86 buf.put_slice(&cache);
87 len += nonce_len + 1;
88
89 len
90 }
91
92 fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) {
93 let encryption_pubkey_compressed_bytes =
94 &buf[..seismic_enclave::constants::PUBLIC_KEY_SIZE];
95 let encryption_pubkey =
96 seismic_enclave::PublicKey::from_slice(encryption_pubkey_compressed_bytes).unwrap();
97 buf.advance(seismic_enclave::constants::PUBLIC_KEY_SIZE);
98
99 let (message_version, buf) = (buf[0], &buf[1..]);
100
101 let (nonce_len, buf) = (buf[0], &buf[1..]);
102 let (encryption_nonce, buf) = U96::from_compact(buf, nonce_len as usize);
103 (Self { encryption_pubkey, encryption_nonce, message_version }, buf)
104 }
105}
106
107impl Compact for AlloyTxSeismic {
108 fn to_compact<B>(&self, buf: &mut B) -> usize
109 where
110 B: bytes::BufMut + AsMut<[u8]>,
111 {
112 let tx = TxSeismic {
113 chain_id: self.chain_id,
114 nonce: self.nonce,
115 gas_price: self.gas_price,
116 gas_limit: self.gas_limit,
117 to: self.to,
118 value: self.value,
119 seismic_elements: self.seismic_elements,
120 input: self.input.clone(),
121 };
122
123 tx.to_compact(buf)
124 }
125
126 fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
127 let (tx, _) = TxSeismic::from_compact(buf, len);
128
129 let alloy_tx = Self {
130 chain_id: tx.chain_id,
131 nonce: tx.nonce,
132 gas_price: tx.gas_price,
133 gas_limit: tx.gas_limit,
134 to: tx.to,
135 value: tx.value,
136 seismic_elements: tx.seismic_elements,
137 input: tx.input,
138 };
139
140 (alloy_tx, buf)
141 }
142}
143
144impl Compact for SeismicTxType {
145 fn to_compact<B>(&self, buf: &mut B) -> usize
146 where
147 B: bytes::BufMut + AsMut<[u8]>,
148 {
149 match self {
150 Self::Legacy => COMPACT_IDENTIFIER_LEGACY,
151 Self::Eip2930 => COMPACT_IDENTIFIER_EIP2930,
152 Self::Eip1559 => COMPACT_IDENTIFIER_EIP1559,
153 Self::Eip4844 => {
154 buf.put_u8(EIP4844_TX_TYPE_ID);
155 COMPACT_EXTENDED_IDENTIFIER_FLAG
156 }
157 Self::Eip7702 => {
158 buf.put_u8(EIP7702_TX_TYPE_ID);
159 COMPACT_EXTENDED_IDENTIFIER_FLAG
160 }
161 Self::Seismic => {
162 buf.put_u8(SEISMIC_TX_TYPE_ID);
163 COMPACT_EXTENDED_IDENTIFIER_FLAG
164 }
165 }
166 }
167
168 fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) {
169 use bytes::Buf;
170 (
171 match identifier {
172 COMPACT_IDENTIFIER_LEGACY => Self::Legacy,
173 COMPACT_IDENTIFIER_EIP2930 => Self::Eip2930,
174 COMPACT_IDENTIFIER_EIP1559 => Self::Eip1559,
175 COMPACT_EXTENDED_IDENTIFIER_FLAG => {
176 let extended_identifier = buf.get_u8();
177 match extended_identifier {
178 EIP7702_TX_TYPE_ID => Self::Eip7702,
179 SEISMIC_TX_TYPE_ID => Self::Seismic,
180 _ => panic!("Unsupported TxType identifier: {extended_identifier}"),
181 }
182 }
183 _ => panic!("Unknown identifier for TxType: {identifier}"),
184 },
185 buf,
186 )
187 }
188}
189
190impl Compact for SeismicTypedTransaction {
191 fn to_compact<B>(&self, out: &mut B) -> usize
192 where
193 B: bytes::BufMut + AsMut<[u8]>,
194 {
195 let identifier = self.tx_type().to_compact(out);
196 match self {
197 Self::Legacy(tx) => tx.to_compact(out),
198 Self::Eip2930(tx) => tx.to_compact(out),
199 Self::Eip1559(tx) => tx.to_compact(out),
200 Self::Eip4844(tx) => {
201 match tx {
202 TxEip4844Variant::TxEip4844(tx) => tx.to_compact(out),
203 TxEip4844Variant::TxEip4844WithSidecar(tx) => {
204 let inner: &TxEip4844 = tx.tx();
206 inner.to_compact(out)
207 },
208 }
209 },
210 Self::Eip7702(tx) => tx.to_compact(out),
211 Self::Seismic(tx) => tx.to_compact(out),
212 };
213 identifier
214 }
215
216 fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) {
217 let (tx_type, buf) = SeismicTxType::from_compact(buf, identifier);
218 match tx_type {
219 SeismicTxType::Legacy => {
220 let (tx, buf) = Compact::from_compact(buf, buf.len());
221 (Self::Legacy(tx), buf)
222 }
223 SeismicTxType::Eip2930 => {
224 let (tx, buf) = Compact::from_compact(buf, buf.len());
225 (Self::Eip2930(tx), buf)
226 }
227 SeismicTxType::Eip1559 => {
228 let (tx, buf) = Compact::from_compact(buf, buf.len());
229 (Self::Eip1559(tx), buf)
230 }
231 SeismicTxType::Eip4844 => {
232 let (tx, buf): (TxEip4844, _) = Compact::from_compact(buf, buf.len());
233 let tx = TxEip4844Variant::TxEip4844(tx);
234 (Self::Eip4844(tx), buf)
235 }
236 SeismicTxType::Eip7702 => {
237 let (tx, buf) = Compact::from_compact(buf, buf.len());
238 (Self::Eip7702(tx), buf)
239 }
240 SeismicTxType::Seismic => {
241 let (tx, buf) = Compact::from_compact(buf, buf.len());
242 (Self::Seismic(tx), buf)
243 }
244 }
245 }
246}
247
248impl ToTxCompact for SeismicTxEnvelope {
249 fn to_tx_compact(&self, buf: &mut (impl BufMut + AsMut<[u8]>)) {
250 match self {
251 Self::Legacy(tx) => tx.tx().to_compact(buf),
252 Self::Eip2930(tx) => tx.tx().to_compact(buf),
253 Self::Eip1559(tx) => tx.tx().to_compact(buf),
254 Self::Eip4844(tx) => {
255 match tx.tx() {
256 TxEip4844Variant::TxEip4844(tx) => tx.to_compact(buf),
257 TxEip4844Variant::TxEip4844WithSidecar(tx) => {
258 Compact::to_compact(&tx.tx(), buf)
259 },
260 }
261 },
262 Self::Eip7702(tx) => tx.tx().to_compact(buf),
263 Self::Seismic(tx) => tx.tx().to_compact(buf),
264 };
265 }
266}
267
268impl FromTxCompact for SeismicTxEnvelope {
269 type TxType = SeismicTxType;
270
271 fn from_tx_compact(
272 buf: &[u8],
273 tx_type: SeismicTxType,
274 signature: Signature,
275 ) -> (Self, &[u8]) {
276 match tx_type {
277 SeismicTxType::Legacy => {
278 let (tx, buf) = TxLegacy::from_compact(buf, buf.len());
279 let tx = Signed::new_unhashed(tx, signature);
280 (Self::Legacy(tx), buf)
281 }
282 SeismicTxType::Eip2930 => {
283 let (tx, buf) = TxEip2930::from_compact(buf, buf.len());
284 let tx = Signed::new_unhashed(tx, signature);
285 (Self::Eip2930(tx), buf)
286 }
287 SeismicTxType::Eip1559 => {
288 let (tx, buf) = TxEip1559::from_compact(buf, buf.len());
289 let tx = Signed::new_unhashed(tx, signature);
290 (Self::Eip1559(tx), buf)
291 }
292 SeismicTxType::Eip4844 => {
293 let (variant_tag, rest) = buf.split_first().expect("buffer should not be empty");
294
295 match variant_tag {
296 0 => {
297 let (tx, buf) = TxEip4844::from_compact(rest, rest.len());
298 let tx = Signed::new_unhashed(TxEip4844Variant::TxEip4844(tx), signature);
299 (Self::Eip4844(tx), buf)
300 }
301 1 => unreachable!("seismic does not serialize sidecars yet"),
302 _ => panic!("Unknown EIP-4844 variant tag: {}", variant_tag),
303 }
304 }
305 SeismicTxType::Eip7702 => {
306 let (tx, buf) = TxEip7702::from_compact(buf, buf.len());
307 let tx = Signed::new_unhashed(tx, signature);
308 (Self::Eip7702(tx), buf)
309 }
310 SeismicTxType::Seismic => {
311 let (tx, buf) = AlloyTxSeismic::from_compact(buf, buf.len());
312 let tx = Signed::new_unhashed(tx, signature);
313 (Self::Seismic(tx), buf)
314 }
315 }
316 }
317}
318
319impl Envelope for SeismicTxEnvelope {
320 fn signature(&self) -> &Signature {
321 match self {
322 Self::Legacy(tx) => tx.signature(),
323 Self::Eip2930(tx) => tx.signature(),
324 Self::Eip1559(tx) => tx.signature(),
325 Self::Eip4844(tx) => tx.signature(),
326 Self::Eip7702(tx) => tx.signature(),
327 Self::Seismic(tx) => tx.signature(),
328 }
329 }
330
331 fn tx_type(&self) -> Self::TxType {
332 Self::tx_type(self)
333 }
334}
335
336impl Compact for SeismicTxEnvelope {
337 fn to_compact<B>(&self, buf: &mut B) -> usize
338 where
339 B: BufMut + AsMut<[u8]>,
340 {
341 CompactEnvelope::to_compact(self, buf)
342 }
343
344 fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
345 CompactEnvelope::from_compact(buf, len)
346 }
347}
348
349generate_tests!(#[crate, compact] SeismicTypedTransaction, SeismicTypedTransactionTests);
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354 use alloy_primitives::{hex, Bytes, TxKind};
355 use bytes::BytesMut;
356 use seismic_enclave::PublicKey;
357
358 #[test]
359 fn test_seismic_tx_compact_roundtrip() {
360 let tx = AlloyTxSeismic {
362 chain_id: 1166721750861005481,
363 nonce: 13985005159674441909,
364 gas_price: 296133358425745351516777806240018869443,
365 gas_limit: 6091425913586946366,
366 to: TxKind::Create,
367 value: U256::from_str_radix(
368 "30997721070913355446596643088712595347117842472993214294164452566768407578853",
369 10,
370 )
371 .unwrap(),
372 seismic_elements: TxSeismicElements {
373 encryption_pubkey: PublicKey::from_slice(
374 &hex::decode(
375 "02d211b6b0a191b9469bb3674e9c609f453d3801c3e3fd7e0bb00c6cc1e1d941df",
376 )
377 .unwrap(),
378 )
379 .unwrap(),
380 encryption_nonce: U96::from_str_radix("11856476099097235301", 10).unwrap(),
381 message_version: 85,
382 },
383 input: Bytes::from_static(&[0x24]),
384 };
385
386 let mut buf = BytesMut::new();
388 let encoded_size = tx.to_compact(&mut buf);
389
390 let (decoded_tx, _) = AlloyTxSeismic::from_compact(&buf, encoded_size);
392
393 assert_eq!(tx.chain_id, decoded_tx.chain_id);
395 assert_eq!(tx.nonce, decoded_tx.nonce);
396 assert_eq!(tx.gas_price, decoded_tx.gas_price);
397 assert_eq!(tx.gas_limit, decoded_tx.gas_limit);
398 assert_eq!(tx.to, decoded_tx.to);
399 assert_eq!(tx.value, decoded_tx.value);
400 assert_eq!(tx.input, decoded_tx.input);
401
402 assert_eq!(
404 tx.seismic_elements.encryption_pubkey.serialize(),
405 decoded_tx.seismic_elements.encryption_pubkey.serialize()
406 );
407 assert_eq!(
408 tx.seismic_elements.encryption_nonce,
409 decoded_tx.seismic_elements.encryption_nonce
410 );
411 assert_eq!(
412 tx.seismic_elements.message_version,
413 decoded_tx.seismic_elements.message_version
414 );
415 }
416}