ethereum_tx_sign/
lib.rs

1#![deny(warnings)]
2#![deny(clippy::all)]
3extern crate serde;
4#[macro_use]
5extern crate serde_derive;
6extern crate bytes;
7extern crate hex;
8extern crate num_traits;
9extern crate rlp;
10extern crate secp256k1;
11extern crate tiny_keccak;
12
13#[cfg(test)]
14extern crate ethereum_types;
15#[cfg(test)]
16extern crate serde_json;
17
18use rlp::{Encodable, RlpStream};
19use secp256k1::{Message, Secp256k1, SecretKey};
20use serde::de::Error as SerdeErr;
21use serde::ser::SerializeSeq;
22use serde::Deserialize;
23use std::convert::TryInto;
24use tiny_keccak::{Hasher, Keccak};
25
26/// Ethereum transaction
27pub trait Transaction {
28    /// [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) chain ID
29    fn chain(&self) -> u64;
30
31    /// Compute the unique transaction hash
32    fn hash(&self) -> [u8; 32] {
33        let rlp = self.rlp_parts();
34        let mut rlp_stream = RlpStream::new();
35        rlp_stream.begin_unbounded_list();
36        for r in rlp.iter() {
37            rlp_stream.append(r);
38        }
39
40        // `None` means it is legacy
41        if Self::transaction_type().is_none() {
42            rlp_stream.append(&self.chain());
43            rlp_stream.append_raw(&[0x80], 1);
44            rlp_stream.append_raw(&[0x80], 1);
45        }
46
47        rlp_stream.finalize_unbounded_list();
48        let mut rlp_bytes = rlp_stream.out().to_vec();
49
50        if let Some(tt) = Self::transaction_type() {
51            rlp_bytes.insert(0usize, tt);
52        }
53
54        keccak256_hash(&rlp_bytes)
55    }
56
57    /// Compute the [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) for the transaction
58    fn ecdsa(&self, private_key: &[u8]) -> Result<EcdsaSig, Error> {
59        let hash = self.hash();
60
61        let chain = match Self::transaction_type() {
62            Some(_) => None,
63            None => Some(self.chain()),
64        };
65
66        EcdsaSig::generate(hash, private_key, chain)
67    }
68
69    /// Sign and encode this transaction using the given ECDSA signature.
70    /// Signing is done in two steps. Example:
71    /// ```
72    /// use ethereum_tx_sign::{LegacyTransaction, Transaction};
73    ///
74    /// let tx = LegacyTransaction {
75    ///     chain: 1,
76    ///     nonce: 0,
77    ///     to: Some([0x45; 20]),
78    ///     value: 1000,
79    ///     gas_price: 20 * 10u128.pow(9),
80    ///     gas: 21000,
81    ///     data: vec![]
82    /// };
83    /// let ecdsa = tx.ecdsa(&vec![0x35; 32]).unwrap();
84    /// let tx_bytes = tx.sign(&ecdsa);
85    /// ```
86    fn sign(&self, ecdsa: &EcdsaSig) -> Vec<u8>;
87
88    /// Return the fields of the transaction as a list of RLP-encodable
89    /// parts. The parts must follow the order that they will be encoded,
90    /// hashed, or signed.
91    fn rlp_parts(&self) -> Vec<Box<dyn Encodable>>;
92
93    /// Returns the transaction defined as TransactionType in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718).
94    /// LegacyTransactions do not have a type, so will return None.
95    fn transaction_type() -> Option<u8>;
96}
97
98#[derive(Debug)]
99pub enum Error {
100    Secp256k1(secp256k1::Error),
101}
102
103impl From<secp256k1::Error> for Error {
104    fn from(error: secp256k1::Error) -> Self {
105        Error::Secp256k1(error)
106    }
107}
108
109/// Internal function that avoids duplicating a lot of signing code
110fn sign_bytes<T: Transaction>(tx_type: Option<u8>, ecdsa: &EcdsaSig, t: &T) -> Vec<u8> {
111    let mut rlp_stream = RlpStream::new();
112    let rlp = t.rlp_parts();
113    rlp_stream.begin_unbounded_list();
114    for r in rlp.iter() {
115        rlp_stream.append(r);
116    }
117    let EcdsaSig { v, s, r } = ecdsa;
118
119    // removes leading zeroes
120    let mut r_n = r.clone();
121    let mut s_n = s.clone();
122    while r_n[0] == 0 {
123        r_n.remove(0);
124    }
125    while s_n[0] == 0 {
126        s_n.remove(0);
127    }
128
129    rlp_stream.append(v);
130    rlp_stream.append(&r_n);
131    rlp_stream.append(&s_n);
132
133    rlp_stream.finalize_unbounded_list();
134
135    let mut vec = rlp_stream.out().to_vec();
136    if let Some(b) = tx_type {
137        vec.insert(0usize, b)
138    }
139    vec
140}
141
142/// Description of a Transaction, pending or in the chain.
143#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
144pub struct LegacyTransaction {
145    /// Chain ID
146    pub chain: u64,
147    /// Nonce
148    pub nonce: u128,
149    /// Recipient (None when contract creation)
150    #[serde(serialize_with = "option_array_u8_serialize")]
151    #[serde(deserialize_with = "option_array_u8_deserialize")]
152    #[serde(default)]
153    pub to: Option<[u8; 20]>,
154    /// Transfered value
155    pub value: u128,
156    /// Gas price
157    #[serde(rename = "gasPrice")]
158    pub gas_price: u128,
159    /// Gas limit
160    #[serde(alias = "gasLimit")]
161    pub gas: u128,
162    /// Input data
163    #[serde(serialize_with = "slice_u8_serialize")]
164    #[serde(deserialize_with = "slice_u8_deserialize")]
165    #[serde(default)]
166    pub data: Vec<u8>,
167}
168
169impl Transaction for LegacyTransaction {
170    fn chain(&self) -> u64 {
171        self.chain
172    }
173
174    fn rlp_parts(&self) -> Vec<Box<dyn Encodable>> {
175        let to: Vec<u8> = match self.to {
176            Some(ref to) => to.to_vec(),
177            None => vec![],
178        };
179        vec![
180            Box::new(self.nonce),
181            Box::new(self.gas_price),
182            Box::new(self.gas),
183            Box::new(to),
184            Box::new(self.value),
185            Box::new(self.data.clone()),
186        ]
187    }
188
189    fn sign(&self, ecdsa: &EcdsaSig) -> Vec<u8> {
190        sign_bytes(None, ecdsa, self)
191    }
192
193    fn transaction_type() -> Option<u8> {
194        None
195    }
196}
197
198#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
199/// A list of addresses and storage keys that the transaction plans to access.
200pub struct Access {
201    #[serde(serialize_with = "array_u8_20_serialize")]
202    #[serde(deserialize_with = "array_u8_20_deserialize")]
203    pub address: [u8; 20],
204    #[serde(serialize_with = "storage_keys_serialize")]
205    #[serde(deserialize_with = "storage_keys_deserialize")]
206    #[serde(rename = "storageKeys")]
207    pub storage_keys: Vec<[u8; 32]>,
208}
209
210#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
211/// [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) access list.
212pub struct AccessList(pub Vec<Access>);
213
214impl Encodable for AccessList {
215    /// Encodes the access list according to [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930).
216    fn rlp_append(&self, rlp_stream: &mut RlpStream) {
217        rlp_stream.begin_unbounded_list();
218
219        for access in self.0.iter() {
220            let address_bytes: Vec<u8> = access.address.to_vec();
221
222            rlp_stream.begin_unbounded_list();
223            rlp_stream.append(&address_bytes);
224
225            // append the list of keys
226            {
227                rlp_stream.begin_unbounded_list();
228                for storage_key in access.storage_keys.iter() {
229                    let storage_key_bytes: Vec<u8> = storage_key.to_vec();
230                    rlp_stream.append(&storage_key_bytes);
231                }
232                rlp_stream.finalize_unbounded_list();
233            }
234
235            rlp_stream.finalize_unbounded_list();
236        }
237
238        rlp_stream.finalize_unbounded_list();
239    }
240}
241
242#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
243/// [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) access list transaction.
244pub struct AccessListTransaction {
245    /// Chain ID
246    pub chain: u64,
247    /// Nonce
248    pub nonce: u128,
249    /// Gas price
250    #[serde(rename = "gasPrice")]
251    pub gas_price: u128,
252    /// Gas limit
253    #[serde(alias = "gasLimit")]
254    pub gas: u128,
255    /// Recipient (None when contract creation)
256    #[serde(serialize_with = "option_array_u8_serialize")]
257    #[serde(deserialize_with = "option_array_u8_deserialize")]
258    #[serde(default)]
259    pub to: Option<[u8; 20]>,
260    /// Transfered value
261    pub value: u128,
262    /// Input data
263    #[serde(serialize_with = "slice_u8_serialize")]
264    #[serde(deserialize_with = "slice_u8_deserialize")]
265    #[serde(default)]
266    pub data: Vec<u8>,
267    /// List of addresses and storage keys the transaction plans to access
268    #[serde(rename = "accessList")]
269    pub access_list: AccessList,
270}
271
272fn option_array_u8_serialize<S>(to: &Option<[u8; 20]>, s: S) -> Result<S::Ok, S::Error>
273where
274    S: serde::Serializer,
275{
276    match to {
277        Some(ref array) => slice_u8_serialize(array, s),
278        None => s.serialize_none(),
279    }
280}
281
282/// We allow hex strings such as "0x00ffaa". The 0x prefix is not necessary when
283/// you know it is hex.
284const HEX_PREFIX: &str = "0x";
285
286fn slice_u8_deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
287where
288    D: serde::Deserializer<'de>,
289{
290    let s: String = String::deserialize(deserializer)?;
291    let s = if s.starts_with(HEX_PREFIX) {
292        s.replace(HEX_PREFIX, "")
293    } else {
294        s
295    };
296    match hex::decode(&s) {
297        Ok(s) => Ok(s),
298        Err(err) => Err(derr::<D>(&s, err)),
299    }
300}
301
302fn storage_keys_deserialize<'de, D>(deserializer: D) -> Result<Vec<[u8; 32]>, D::Error>
303where
304    D: serde::Deserializer<'de>,
305{
306    let storage_key_vec: Vec<String> = Vec::deserialize(deserializer)?;
307    let mut storage_keys = vec![];
308    for storage_key in storage_key_vec.into_iter() {
309        let s = if storage_key.starts_with(HEX_PREFIX) {
310            storage_key.replace(HEX_PREFIX, "")
311        } else {
312            storage_key
313        };
314        let s = match hex::decode(&s) {
315            Ok(s) => s,
316            Err(err) => return Err(derr::<D>(&s, err)),
317        };
318        let s_len = s.len();
319        let arr = match s.try_into() {
320            Ok(a) => a,
321            Err(_) => {
322                return Err(D::Error::invalid_length(
323                    s_len,
324                    &"a hex string of length 20",
325                ))
326            }
327        };
328        storage_keys.push(arr) // TODO
329    }
330    Ok(storage_keys)
331}
332
333fn storage_keys_serialize<S>(storage_keys: &[[u8; 32]], s: S) -> Result<S::Ok, S::Error>
334where
335    S: serde::Serializer,
336{
337    let mut seq = s.serialize_seq(Some(storage_keys.len()))?;
338    for storage_key in storage_keys.iter() {
339        seq.serialize_element(&hex::encode(storage_key))?;
340    }
341    seq.end()
342}
343
344fn array_u8_20_serialize<S>(storage_keys: &[u8; 20], s: S) -> Result<S::Ok, S::Error>
345where
346    S: serde::Serializer,
347{
348    s.serialize_str(&hex::encode(storage_keys))
349}
350
351fn array_u8_20_deserialize<'de, D>(d: D) -> Result<[u8; 20], D::Error>
352where
353    D: serde::Deserializer<'de>,
354{
355    match option_array_u8_deserialize(d)? {
356        Some(a) => Ok(a),
357        None => Err(
358            D::Error::invalid_value(serde::de::Unexpected::Option, &"a hex string of length 20"), // TODO is error accurate?
359        ),
360    }
361}
362
363fn option_array_u8_deserialize<'de, D>(deserializer: D) -> Result<Option<[u8; 20]>, D::Error>
364where
365    D: serde::Deserializer<'de>,
366{
367    const TO_LEN: usize = 20;
368    let s_option: Option<String> = Option::deserialize(deserializer)?;
369    match s_option {
370        None => Ok(None),
371        Some(s) => {
372            let s = if s.starts_with(HEX_PREFIX) {
373                s.replace(HEX_PREFIX, "")
374            } else {
375                s
376            };
377            match hex::decode(&s) {
378                Ok(s) => {
379                    let mut to = [0u8; 20];
380                    if s.len() == TO_LEN {
381                        for (i, b) in s.iter().enumerate() {
382                            to[i] = *b;
383                        }
384
385                        Ok(Some(to))
386                    } else {
387                        Err(D::Error::invalid_length(
388                            s.len(),
389                            &"a hex string of length 20",
390                        ))
391                    }
392                }
393                Err(err) => Err(derr::<D>(&s, err)),
394            }
395        }
396    }
397}
398
399fn derr<'de, D: serde::Deserializer<'de>>(s: &str, err: hex::FromHexError) -> D::Error {
400    match err {
401        hex::FromHexError::InvalidHexCharacter { c, .. } => {
402            D::Error::invalid_value(serde::de::Unexpected::Char(c), &"a valid hex character")
403        }
404        hex::FromHexError::OddLength => {
405            D::Error::invalid_length(s.len(), &"a hex string of even length")
406        }
407        hex::FromHexError::InvalidStringLength => {
408            D::Error::invalid_length(s.len(), &"a hex string that matches container length")
409        }
410    }
411}
412
413fn slice_u8_serialize<S>(slice: &[u8], s: S) -> Result<S::Ok, S::Error>
414where
415    S: serde::Serializer,
416{
417    s.serialize_str(&hex::encode(slice))
418}
419
420const EIP_2930_TYPE: u8 = 0x01;
421
422impl Transaction for AccessListTransaction {
423    fn chain(&self) -> u64 {
424        self.chain
425    }
426
427    #[allow(warnings)]
428    fn rlp_parts(&self) -> Vec<Box<dyn Encodable>> {
429        let to: Vec<u8> = match self.to {
430            Some(ref to) => to.to_vec(),
431            None => vec![],
432        };
433        let mut parts: Vec<Box<dyn Encodable>> = vec![
434            Box::new(self.chain),
435            Box::new(self.nonce),
436            Box::new(self.gas_price),
437            Box::new(self.gas),
438            Box::new(to),
439            Box::new(self.value),
440            Box::new(self.data.clone()),
441            Box::new(self.access_list.clone()),
442        ];
443
444        parts
445    }
446
447    fn sign(&self, ecdsa: &EcdsaSig) -> Vec<u8> {
448        sign_bytes(Some(EIP_2930_TYPE), ecdsa, self)
449    }
450
451    fn transaction_type() -> Option<u8> {
452        Some(EIP_2930_TYPE)
453    }
454}
455
456const EIP_1559_TYPE: u8 = 0x02;
457
458#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
459/// [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) fee market transaction.
460pub struct FeeMarketTransaction {
461  /// Chain ID
462  pub chain: u64,
463  /// Nonce
464  pub nonce: u128,
465  /// Gas price
466  #[serde(rename = "maxPriorityFeePerGas")]
467  pub max_priority_fee_per_gas: u128,
468  #[serde(rename = "maxFeePerGas")]
469  pub max_fee_per_gas: u128,
470  /// Gas limit
471  #[serde(alias = "gasLimit")]
472  pub gas: u128,
473  /// Recipient (None when contract creation)
474  #[serde(serialize_with = "option_array_u8_serialize")]
475  #[serde(deserialize_with = "option_array_u8_deserialize")]
476  #[serde(default)]
477  pub to: Option<[u8; 20]>,
478  /// Transfered value
479  pub value: u128,
480  /// Input data
481  #[serde(serialize_with = "slice_u8_serialize")]
482  #[serde(deserialize_with = "slice_u8_deserialize")]
483  #[serde(default)]
484  pub data: Vec<u8>,
485  /// List of addresses and storage keys the transaction plans to access
486  #[serde(rename = "accessList")]
487  pub access_list: AccessList,
488}
489
490impl Transaction for FeeMarketTransaction {
491  fn chain(&self) -> u64 { self.chain }
492
493  fn sign(&self, ecdsa: &EcdsaSig) -> Vec<u8> {
494    sign_bytes(Some(EIP_1559_TYPE), ecdsa, self)
495  }
496
497  fn rlp_parts(&self) -> Vec<Box<dyn Encodable>> {
498    let to: Vec<u8> = match self.to {
499      Some(ref to) => to.to_vec(),
500      None => vec![],
501    };
502    vec![
503      Box::new(self.chain),
504      Box::new(self.nonce),
505      Box::new(self.max_priority_fee_per_gas),
506      Box::new(self.max_fee_per_gas),
507      Box::new(self.gas),
508      Box::new(to),
509      Box::new(self.value),
510      Box::new(self.data.clone()),
511      Box::new(self.access_list.clone()),
512    ]
513  }
514
515  fn transaction_type() -> Option<u8> {
516    Some(EIP_1559_TYPE)
517  }
518}
519
520#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
521/// Represents an [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) signature.
522pub struct EcdsaSig {
523    pub v: u64,
524    #[serde(serialize_with = "slice_u8_serialize")]
525    #[serde(deserialize_with = "slice_u8_deserialize")]
526    pub r: Vec<u8>,
527    #[serde(serialize_with = "slice_u8_serialize")]
528    #[serde(deserialize_with = "slice_u8_deserialize")]
529    pub s: Vec<u8>,
530}
531
532impl EcdsaSig {
533    fn generate(
534        hash: [u8; 32],
535        private_key: &[u8],
536        chain_id: Option<u64>,
537    ) -> Result<EcdsaSig, Error> {
538        let s = Secp256k1::signing_only();
539        let msg = Message::from_slice(&hash)?;
540        let key = SecretKey::from_slice(private_key)?;
541        let (v, sig_bytes) = s.sign_ecdsa_recoverable(&msg, &key).serialize_compact();
542
543        let v = v.to_i32() as u64
544            + match chain_id {
545                Some(c) => c * 2 + 35,
546                None => 0,
547            };
548
549        Ok(EcdsaSig {
550            v,
551            r: sig_bytes[0..32].to_vec(),
552            s: sig_bytes[32..64].to_vec(),
553        })
554    }
555}
556
557fn keccak256_hash(bytes: &[u8]) -> [u8; 32] {
558    let mut hasher = Keccak::v256();
559    hasher.update(bytes);
560    let mut resp: [u8; 32] = Default::default();
561    hasher.finalize(&mut resp);
562    resp
563}
564
565#[cfg(test)]
566mod test {
567    use crate::{AccessListTransaction, EcdsaSig, LegacyTransaction, Transaction, FeeMarketTransaction};
568
569    use serde_json;
570    use std::collections::HashMap;
571    use std::fmt::Debug;
572    use std::fs::File;
573    use std::io::Read;
574
575    // TX RANDOM FEE MARKET 001
576
577    #[test]
578    fn test_random_fee_market_transaction_001() {
579        run_signing_test::<FeeMarketTransaction>("./test/random_eip_1559_001.json");
580    }
581
582    #[test]
583    fn test_random_fee_market_transaction_001_ecdsa() {
584        run_ecdsa_test::<FeeMarketTransaction>("./test/random_eip_1559_001.json");
585    }
586
587    #[test]
588    fn test_random_fee_market_transaction_001_hash() {
589        run_hash_test::<FeeMarketTransaction>("./test/random_eip_1559_001.json");
590    }
591
592    // TX RANDOM FEE MARKET 002
593
594    #[test]
595    fn test_random_fee_market_transaction_002() {
596        run_signing_test::<FeeMarketTransaction>("./test/random_eip_1559_002.json");
597    }
598
599    #[test]
600    fn test_random_fee_market_transaction_002_ecdsa() {
601        run_ecdsa_test::<FeeMarketTransaction>("./test/random_eip_1559_002.json");
602    }
603
604    #[test]
605    fn test_random_fee_market_transaction_002_hash() {
606        run_hash_test::<FeeMarketTransaction>("./test/random_eip_1559_002.json");
607    }
608
609    // TX RANDOM FEE MARKET 003
610
611    #[test]
612    fn test_random_fee_market_transaction_003() {
613        run_signing_test::<FeeMarketTransaction>("./test/random_eip_1559_003.json");
614    }
615
616    #[test]
617    fn test_random_fee_market_transaction_003_ecdsa() {
618        run_ecdsa_test::<FeeMarketTransaction>("./test/random_eip_1559_003.json");
619    }
620
621    #[test]
622    fn test_random_fee_market_transaction_003_hash() {
623        run_hash_test::<FeeMarketTransaction>("./test/random_eip_1559_003.json");
624    }
625
626    // TX RANDOM ACCESS LIST 001
627
628    #[test]
629    fn test_random_access_list_transaction_001() {
630        run_signing_test::<AccessListTransaction>("./test/random_eip_2930_001.json");
631    }
632
633    #[test]
634    fn test_random_access_list_transaction_001_ecdsa() {
635        run_ecdsa_test::<AccessListTransaction>("./test/random_eip_2930_001.json");
636    }
637
638    #[test]
639    fn test_random_access_list_transaction_001_hash() {
640        run_hash_test::<AccessListTransaction>("./test/random_eip_2930_001.json");
641    }
642
643    // TX RANDOM ACCESS LIST 002
644
645    #[test]
646    fn test_random_access_list_transaction_002() {
647        run_signing_test::<AccessListTransaction>("./test/random_eip_2930_002.json");
648    }
649
650    #[test]
651    fn test_random_access_list_transaction_002_ecdsa() {
652        run_ecdsa_test::<AccessListTransaction>("./test/random_eip_2930_002.json");
653    }
654
655    #[test]
656    fn test_random_access_list_transaction_002_hash() {
657        run_hash_test::<AccessListTransaction>("./test/random_eip_2930_002.json");
658    }
659
660    // TX RANDOM ACCESS LIST 003
661
662    #[test]
663    fn test_random_access_list_transaction_003() {
664        run_signing_test::<AccessListTransaction>("./test/random_eip_2930_003.json");
665    }
666
667    #[test]
668    fn test_random_access_list_transaction_003_ecdsa() {
669        run_ecdsa_test::<AccessListTransaction>("./test/random_eip_2930_003.json");
670    }
671
672    #[test]
673    fn test_random_access_list_transaction_003_hash() {
674        run_hash_test::<AccessListTransaction>("./test/random_eip_2930_003.json");
675    }
676
677    // TX RANDOM LEGACY 001
678
679    #[test]
680    fn test_random_legacy_001() {
681        run_signing_test::<LegacyTransaction>("./test/random_legacy_001.json");
682    }
683
684    #[test]
685    fn test_random_legacy_001_ecdsa() {
686        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_001.json");
687    }
688
689    #[test]
690    fn test_random_legacy_001_hash() {
691        run_hash_test::<LegacyTransaction>("./test/random_legacy_001.json");
692    }
693
694    // TX RANDOM LEGACY 002
695
696    #[test]
697    fn test_random_legacy_002() {
698        run_signing_test::<LegacyTransaction>("./test/random_legacy_002.json");
699    }
700
701    #[test]
702    fn test_random_legacy_002_ecdsa() {
703        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_002.json");
704    }
705
706    #[test]
707    fn test_random_legacy_002_hash() {
708      run_hash_test::<LegacyTransaction>("./test/random_legacy_002.json");
709    }
710
711    // TX RANDOM LEGACY 003
712
713    #[test]
714    fn test_random_legacy_003() {
715        run_signing_test::<LegacyTransaction>("./test/random_legacy_003.json");
716    }
717
718    #[test]
719    fn test_random_legacy_003_ecdsa() {
720        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_003.json");
721    }
722
723    #[test]
724    fn test_random_legacy_003_hash() {
725        run_hash_test::<LegacyTransaction>("./test/random_legacy_003.json");
726    }
727
728    // TX RANDOM LEGACY 004
729
730    #[test]
731    fn test_random_legacy_004() {
732        run_signing_test::<LegacyTransaction>("./test/random_legacy_004.json");
733    }
734
735    #[test]
736    fn test_random_legacy_004_ecdsa() {
737        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_004.json");
738    }
739
740    #[test]
741    fn test_random_legacy_004_hash() {
742        run_hash_test::<LegacyTransaction>("./test/random_legacy_004.json");
743    }
744
745    // TX RANDOM LEGACY 005
746
747    #[test]
748    fn test_random_legacy_005() {
749        run_signing_test::<LegacyTransaction>("./test/random_legacy_005.json");
750    }
751
752    #[test]
753    fn test_random_legacy_005_ecdsa() {
754        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_005.json");
755    }
756
757    #[test]
758    fn test_random_legacy_005_hash() {
759        run_hash_test::<LegacyTransaction>("./test/random_legacy_005.json");
760    }
761
762    // TX RANDOM LEADING ZEROES 001
763
764    #[test]
765    fn test_random_legacy_leading_zeroes_001_ecdsa() {
766        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_leading_zeroes_001.json");
767    }
768
769    #[test]
770    fn test_random_legacy_leading_zeroes_001_hash() {
771        run_hash_test::<LegacyTransaction>("./test/random_legacy_leading_zeroes_001.json");
772    }
773
774    // TX RANDOM LEADING ZEROES 002
775
776    #[test]
777    fn test_random_legacy_leading_zeroes_002_ecdsa() {
778        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_leading_zeroes_002.json");
779    }
780
781    #[test]
782    fn test_random_legacy_leading_zeroes_002_hash() {
783        run_hash_test::<LegacyTransaction>("./test/random_legacy_leading_zeroes_002.json");
784    }
785
786    // TX RANDOM LEADING ZEROES 003
787
788    #[test]
789    fn test_random_legacy_leading_zeroes_003_ecdsa() {
790        run_ecdsa_test::<LegacyTransaction>("./test/random_legacy_leading_zeroes_003.json");
791    }
792
793    #[test]
794    fn test_random_legacy_leading_zeroes_003_hash() {
795        run_hash_test::<LegacyTransaction>("./test/random_legacy_leading_zeroes_003.json");
796    }
797
798    // TX ZERO LEGACY 001
799
800    #[test]
801    fn test_zero_legacy_001() {
802        run_signing_test::<LegacyTransaction>("./test/zero_legacy_001.json");
803    }
804
805    #[test]
806    fn test_zero_legacy_001_ecdsa() {
807        run_ecdsa_test::<LegacyTransaction>("./test/zero_legacy_001.json");
808    }
809
810    #[test]
811    fn test_zero_legacy_001_hash() {
812        run_hash_test::<LegacyTransaction>("./test/zero_legacy_001.json");
813    }
814
815    // TX ZERO LEGACY 002
816
817    #[test]
818    fn test_zero_legacy_002() {
819        run_signing_test::<LegacyTransaction>("./test/zero_legacy_002.json");
820    }
821
822    #[test]
823    fn test_zero_legacy_002_ecdsa() {
824        run_ecdsa_test::<LegacyTransaction>("./test/zero_legacy_002.json");
825    }
826
827    #[test]
828    fn test_zero_legacy_002_hash() {
829        run_hash_test::<LegacyTransaction>("./test/zero_legacy_002.json");
830    }
831
832    // TX ZERO LEGACY 003
833
834    #[test]
835    fn test_zero_legacy_003() {
836        run_signing_test::<LegacyTransaction>("./test/zero_legacy_003.json");
837    }
838
839    #[test]
840    fn test_zero_legacy_003_ecdsa() {
841        run_ecdsa_test::<LegacyTransaction>("./test/zero_legacy_003.json");
842    }
843
844    #[test]
845    fn test_zero_legacy_003_hash() {
846        run_hash_test::<LegacyTransaction>("./test/zero_legacy_003.json");
847    }
848
849    // TX ZERO ACCESS LIST 001
850
851    #[test]
852    fn test_zero_access_list_transaction_001() {
853        run_signing_test::<AccessListTransaction>("./test/zero_eip_2718_001.json");
854    }
855
856    #[test]
857    fn test_zero_access_list_transaction_001_ecdsa() {
858        run_ecdsa_test::<AccessListTransaction>("./test/zero_eip_2718_001.json");
859    }
860
861    #[test]
862    fn test_zero_access_list_transaction_001_hash() {
863        run_hash_test::<AccessListTransaction>("./test/zero_eip_2718_001.json");
864    }
865
866    // TX ZERO ACCESS LIST 002
867
868    #[test]
869    fn test_zero_access_list_transaction_002() {
870        run_signing_test::<AccessListTransaction>("./test/zero_eip_2718_002.json");
871    }
872
873    #[test]
874    fn test_zero_access_list_transaction_002_ecdsa() {
875        run_ecdsa_test::<AccessListTransaction>("./test/zero_eip_2718_002.json");
876    }
877
878    #[test]
879    fn test_zero_access_list_transaction_002_hash() {
880        run_hash_test::<AccessListTransaction>("./test/zero_eip_2718_002.json");
881    }
882
883    // TX ZERO ACCESS LIST 003
884
885    #[test]
886    fn test_zero_access_list_transaction_003() {
887        run_signing_test::<AccessListTransaction>("./test/zero_eip_2718_003.json");
888    }
889
890    #[test]
891    fn test_zero_access_list_transaction_003_ecdsa() {
892        run_ecdsa_test::<AccessListTransaction>("./test/zero_eip_2718_003.json");
893    }
894
895    #[test]
896    fn test_zero_access_list_transaction_003_hash() {
897        run_ecdsa_test::<AccessListTransaction>("./test/zero_eip_2718_003.json");
898    }
899
900    // TX ZERO FEE MARKET 001
901
902    #[test]
903    fn test_zero_fee_market_transaction_001() {
904        run_signing_test::<FeeMarketTransaction>("./test/zero_eip_1559_001.json");
905    }
906
907    #[test]
908    fn test_zero_fee_market_transaction_001_ecdsa() {
909        run_ecdsa_test::<FeeMarketTransaction>("./test/zero_eip_1559_001.json");
910    }
911
912    #[test]
913    fn test_zero_fee_market_transaction_001_hash() {
914        run_ecdsa_test::<FeeMarketTransaction>("./test/zero_eip_1559_001.json");
915    }
916
917    // TX ZERO FEE MARKET 002
918
919    #[test]
920    fn test_zero_fee_market_transaction_002() {
921        run_signing_test::<FeeMarketTransaction>("./test/zero_eip_1559_002.json");
922    }
923
924    #[test]
925    fn test_zero_fee_market_transaction_002_ecdsa() {
926        run_ecdsa_test::<FeeMarketTransaction>("./test/zero_eip_1559_002.json");
927    }
928
929    #[test]
930    fn test_zero_fee_market_transaction_002_hash() {
931        run_ecdsa_test::<FeeMarketTransaction>("./test/zero_eip_1559_002.json");
932    }
933
934    // TX ZERO FEE MARKET 003
935
936    #[test]
937    fn test_zero_fee_market_transaction_003() {
938        run_signing_test::<FeeMarketTransaction>("./test/zero_eip_1559_003.json");
939    }
940
941    #[test]
942    fn test_zero_fee_market_transaction_003_ecdsa() {
943        run_ecdsa_test::<FeeMarketTransaction>("./test/zero_eip_1559_003.json");
944    }
945
946    #[test]
947    fn test_zero_fee_market_transaction_003_hash() {
948        run_ecdsa_test::<FeeMarketTransaction>("./test/zero_eip_1559_003.json");
949    }
950
951    // Serialization tests
952
953    // ACCESS LIST SERIALIZATION
954
955    #[test]
956    fn test_serde_random_access_list_transaction_001() {
957        run_serialization_deserialization_test::<AccessListTransaction>(
958            "./test/random_eip_2930_001.json",
959        );
960    }
961
962    #[test]
963    fn test_serde_random_access_list_transaction_002() {
964        run_serialization_deserialization_test::<AccessListTransaction>(
965            "./test/random_eip_2930_002.json",
966        );
967    }
968
969    #[test]
970    fn test_serde_random_access_list_transaction_003() {
971        run_serialization_deserialization_test::<AccessListTransaction>(
972            "./test/random_eip_2930_003.json",
973        );
974    }
975
976    // FEE MARKET SERIALIZATION
977
978    #[test]
979    fn test_serde_random_fee_market_transaction_001() {
980      run_serialization_deserialization_test::<FeeMarketTransaction>(
981        "./test/random_eip_1559_001.json",
982      );
983    }
984
985    #[test]
986    fn test_serde_random_fee_market_transaction_002() {
987      run_serialization_deserialization_test::<FeeMarketTransaction>(
988        "./test/random_eip_1559_002.json",
989      );
990    }
991
992    #[test]
993    fn test_serde_random_fee_market_transaction_003() {
994      run_serialization_deserialization_test::<FeeMarketTransaction>(
995        "./test/random_eip_1559_003.json",
996      );
997    }
998
999    fn run_serialization_deserialization_test<
1000        T: Transaction
1001            + serde::de::DeserializeOwned
1002            + std::fmt::Debug
1003            + serde::Serialize
1004            + serde::de::DeserializeOwned
1005            + std::cmp::Eq,
1006    >(
1007        path: &str,
1008    ) {
1009        let mut file = File::open(path).unwrap_or_else(|_| panic!("Failed to open: {}", path));
1010        let mut f_string = String::new();
1011        file.read_to_string(&mut f_string).unwrap();
1012
1013        let values: HashMap<String, serde_json::Value> = serde_json::from_str(&f_string).unwrap();
1014        let transaction_original: T = serde_json::from_value(values["input"].clone()).unwrap();
1015        let transaction_string = serde_json::to_string(&transaction_original).unwrap();
1016
1017        assert_eq!(
1018            transaction_original,
1019            serde_json::from_str(&transaction_string).unwrap()
1020        )
1021    }
1022
1023    // TODO refactor some of the below
1024
1025    fn run_signing_test<T: Transaction + Debug + serde::de::DeserializeOwned>(path: &str) {
1026        let mut file = File::open(path).unwrap_or_else(|_| panic!("Failed to open: {}", path));
1027        let mut f_string = String::new();
1028        file.read_to_string(&mut f_string).unwrap();
1029
1030        let values: HashMap<String, serde_json::Value> = serde_json::from_str(&f_string).unwrap();
1031
1032        let transaction: T = serde_json::from_value(values["input"].clone()).unwrap();
1033        let ecdsa: EcdsaSig = serde_json::from_value(values["output"].clone()).unwrap();
1034        let expected_bytes_string: String =
1035            serde_json::from_value(values["output"]["bytes"].clone()).unwrap();
1036        let expected_bytes_string = expected_bytes_string.replace("0x", "");
1037
1038        let actual_bytes = transaction.sign(&ecdsa);
1039        let actual_bytes_string = hex::encode(&actual_bytes);
1040
1041        println!(
1042            "Expecting {} byte(s), got {} byte(s)",
1043            expected_bytes_string.len(),
1044            actual_bytes_string.len()
1045        );
1046
1047        assert_eq!(expected_bytes_string, actual_bytes_string);
1048    }
1049
1050    fn run_ecdsa_test<T: Transaction + serde::de::DeserializeOwned>(path: &str)
1051    where
1052        T: std::fmt::Debug,
1053    {
1054        let mut file = File::open(path).unwrap_or_else(|_| panic!("Failed to open: {}", path));
1055        let mut f_string = String::new();
1056        file.read_to_string(&mut f_string).unwrap();
1057
1058        let values: HashMap<String, serde_json::Value> = serde_json::from_str(&f_string).unwrap();
1059
1060        let transaction: T = serde_json::from_value(values["input"].clone()).unwrap();
1061        let private_key: String = match &values["privateKey"] {
1062            serde_json::Value::String(ref pk) => pk.clone(),
1063            _ => panic!("Unexpected type for private key (expected string)"),
1064        };
1065        let decoded_pk = hex::decode(private_key.replace("0x", "")).unwrap();
1066        let signed_ecdsa = transaction.ecdsa(&decoded_pk).unwrap();
1067        let expected_ecdsa: EcdsaSig = serde_json::from_value(values["output"].clone()).unwrap();
1068
1069        assert_eq!(expected_ecdsa, signed_ecdsa)
1070    }
1071
1072    fn run_hash_test<T: Transaction + serde::de::DeserializeOwned>(path: &str)
1073    where
1074        T: std::fmt::Debug,
1075    {
1076        let mut file = File::open(&path).unwrap_or_else(|_| panic!("Failed to open: {}", path));
1077        let mut f_string = String::new();
1078        file.read_to_string(&mut f_string).unwrap();
1079
1080        let values: HashMap<String, serde_json::Value> = serde_json::from_str(&f_string).unwrap();
1081
1082        let transaction: T = serde_json::from_value(values["input"].clone()).unwrap();
1083        let expected_hash = match &values["output"]["hash"] {
1084            serde_json::Value::String(ref h) => h.clone().replace("0x", ""),
1085            serde_json::Value::Null => panic!("Test is missing `hash`"),
1086            v => panic!("Unexpected type for hash (expected string, got {:?})", v),
1087        };
1088        let actual_hash = hex::encode(transaction.hash());
1089
1090        assert_eq!(expected_hash, actual_hash)
1091    }
1092}