domain_test_utils/
test_ethereum_tx.rs

1//! Code in this file is copied from the frontier repository. Full path is:
2//! https://github.com/subspace/frontier/blob/1c667eb43c3d087ac66dc9ed0aa44128373f5b0a/frame/ethereum/src/mock.rs
3//! If monorepo points to new commit, this file need to be in sync.
4//!
5//! Minor changes were made to comments, derives, visibility, and the address_build() seed size.
6
7pub use ethereum::{
8    AccessListItem, BlockV2 as Block, LegacyTransactionMessage, Log, ReceiptV3 as Receipt,
9    TransactionAction, TransactionSignature, TransactionV2 as Transaction,
10};
11use frame_support::parameter_types;
12use rlp::RlpStream;
13use sp_core::crypto::AccountId32;
14use sp_core::{H160, H256, U256, keccak_256};
15
16parameter_types! {
17    // `870` is the genesis evm domain chain id
18    pub const ChainId: u64 = 870;
19}
20
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct AccountInfo {
23    pub address: H160,
24    pub account_id: AccountId32,
25    pub private_key: H256,
26}
27
28/// Returns an AccountInfo struct with a deterministic address and private key.
29/// Seed zero produces an invalid private key, so we add one to the supplied seed.
30pub fn address_build(mut seed: u128) -> AccountInfo {
31    seed += 1;
32
33    let mut seed_bytes = [0u8; 32];
34    seed_bytes[0..16].copy_from_slice(&seed.to_be_bytes());
35    let private_key = H256::from_slice(&seed_bytes);
36    // The above lines were modified from this original code:
37    //let private_key = H256::from_slice(&[(seed as u8 + 1); 32]); //H256::from_low_u64_be((i + 1) as u64);
38
39    let secret_key = libsecp256k1::SecretKey::parse_slice(&private_key[..]).unwrap();
40    let public_key = &libsecp256k1::PublicKey::from_secret_key(&secret_key).serialize()[1..65];
41    let address = H160::from(H256::from(keccak_256(public_key)));
42
43    let mut data = [0u8; 32];
44    data[0..20].copy_from_slice(&address[..]);
45
46    AccountInfo {
47        private_key,
48        account_id: AccountId32::from(Into::<[u8; 32]>::into(data)),
49        address,
50    }
51}
52
53/// Returns the contract address for the given sender and nonce.
54/// Use `U256::as_u64()` to convert the nonce to `u64`.
55pub fn contract_address(sender: H160, nonce: u64) -> H160 {
56    let mut rlp = RlpStream::new_list(2);
57    rlp.append(&sender);
58    rlp.append(&nonce);
59
60    H160::from_slice(&keccak_256(&rlp.out())[12..])
61}
62
63pub struct LegacyUnsignedTransaction {
64    pub nonce: U256,
65    pub gas_price: U256,
66    pub gas_limit: U256,
67    pub action: TransactionAction,
68    pub value: U256,
69    pub input: Vec<u8>,
70}
71
72impl LegacyUnsignedTransaction {
73    fn signing_rlp_append(&self, s: &mut RlpStream) {
74        s.begin_list(9);
75        s.append(&self.nonce);
76        s.append(&self.gas_price);
77        s.append(&self.gas_limit);
78        s.append(&self.action);
79        s.append(&self.value);
80        s.append(&self.input);
81        s.append(&ChainId::get());
82        s.append(&0u8);
83        s.append(&0u8);
84    }
85
86    fn signing_hash(&self) -> H256 {
87        let mut stream = RlpStream::new();
88        self.signing_rlp_append(&mut stream);
89        H256::from(keccak_256(&stream.out()))
90    }
91
92    pub fn sign(&self, key: &H256) -> Transaction {
93        self.sign_with_chain_id(key, ChainId::get())
94    }
95
96    pub fn sign_with_chain_id(&self, key: &H256, chain_id: u64) -> Transaction {
97        let hash = self.signing_hash();
98        let msg = libsecp256k1::Message::parse(hash.as_fixed_bytes());
99        let s = libsecp256k1::sign(
100            &msg,
101            &libsecp256k1::SecretKey::parse_slice(&key[..]).unwrap(),
102        );
103        let sig = s.0.serialize();
104
105        let sig = TransactionSignature::new(
106            s.1.serialize() as u64 % 2 + chain_id * 2 + 35,
107            H256::from_slice(&sig[0..32]),
108            H256::from_slice(&sig[32..64]),
109        )
110        .unwrap();
111
112        Transaction::Legacy(ethereum::LegacyTransaction {
113            nonce: self.nonce,
114            gas_price: self.gas_price,
115            gas_limit: self.gas_limit,
116            action: self.action,
117            value: self.value,
118            input: self.input.clone(),
119            signature: sig,
120        })
121    }
122}
123
124pub struct EIP2930UnsignedTransaction {
125    pub nonce: U256,
126    pub gas_price: U256,
127    pub gas_limit: U256,
128    pub action: TransactionAction,
129    pub value: U256,
130    pub input: Vec<u8>,
131}
132
133impl EIP2930UnsignedTransaction {
134    pub fn sign(&self, secret: &H256, chain_id: Option<u64>) -> Transaction {
135        let secret = {
136            let mut sk: [u8; 32] = [0u8; 32];
137            sk.copy_from_slice(&secret[0..]);
138            libsecp256k1::SecretKey::parse(&sk).unwrap()
139        };
140        let chain_id = chain_id.unwrap_or(ChainId::get());
141        let msg = ethereum::EIP2930TransactionMessage {
142            chain_id,
143            nonce: self.nonce,
144            gas_price: self.gas_price,
145            gas_limit: self.gas_limit,
146            action: self.action,
147            value: self.value,
148            input: self.input.clone(),
149            access_list: vec![],
150        };
151        let signing_message = libsecp256k1::Message::parse_slice(&msg.hash()[..]).unwrap();
152
153        let (signature, recid) = libsecp256k1::sign(&signing_message, &secret);
154        let rs = signature.serialize();
155        let r = H256::from_slice(&rs[0..32]);
156        let s = H256::from_slice(&rs[32..64]);
157        Transaction::EIP2930(ethereum::EIP2930Transaction {
158            chain_id: msg.chain_id,
159            nonce: msg.nonce,
160            gas_price: msg.gas_price,
161            gas_limit: msg.gas_limit,
162            action: msg.action,
163            value: msg.value,
164            input: msg.input.clone(),
165            access_list: msg.access_list,
166            odd_y_parity: recid.serialize() != 0,
167            r,
168            s,
169        })
170    }
171}
172
173pub struct EIP1559UnsignedTransaction {
174    pub nonce: U256,
175    pub max_priority_fee_per_gas: U256,
176    pub max_fee_per_gas: U256,
177    pub gas_limit: U256,
178    pub action: TransactionAction,
179    pub value: U256,
180    pub input: Vec<u8>,
181}
182
183impl EIP1559UnsignedTransaction {
184    pub fn sign(&self, secret: &H256, chain_id: Option<u64>) -> Transaction {
185        let secret = {
186            let mut sk: [u8; 32] = [0u8; 32];
187            sk.copy_from_slice(&secret[0..]);
188            libsecp256k1::SecretKey::parse(&sk).unwrap()
189        };
190        let chain_id = chain_id.unwrap_or(ChainId::get());
191        let msg = ethereum::EIP1559TransactionMessage {
192            chain_id,
193            nonce: self.nonce,
194            max_priority_fee_per_gas: self.max_priority_fee_per_gas,
195            max_fee_per_gas: self.max_fee_per_gas,
196            gas_limit: self.gas_limit,
197            action: self.action,
198            value: self.value,
199            input: self.input.clone(),
200            access_list: vec![],
201        };
202        let signing_message = libsecp256k1::Message::parse_slice(&msg.hash()[..]).unwrap();
203
204        let (signature, recid) = libsecp256k1::sign(&signing_message, &secret);
205        let rs = signature.serialize();
206        let r = H256::from_slice(&rs[0..32]);
207        let s = H256::from_slice(&rs[32..64]);
208        Transaction::EIP1559(ethereum::EIP1559Transaction {
209            chain_id: msg.chain_id,
210            nonce: msg.nonce,
211            max_priority_fee_per_gas: msg.max_priority_fee_per_gas,
212            max_fee_per_gas: msg.max_fee_per_gas,
213            gas_limit: msg.gas_limit,
214            action: msg.action,
215            value: msg.value,
216            input: msg.input.clone(),
217            access_list: msg.access_list,
218            odd_y_parity: recid.serialize() != 0,
219            r,
220            s,
221        })
222    }
223}