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