diff --git a/src/test_utils/mod.rs b/src/test_utils/mod.rs index 827d00ed3..760306705 100644 --- a/src/test_utils/mod.rs +++ b/src/test_utils/mod.rs @@ -17,7 +17,10 @@ use crate::parameters::{InitCallArgs, NewCallArgs, SubmitResult}; use crate::prelude::Address; use crate::storage; use crate::test_utils::solidity::{ContractConstructor, DeployedContract}; -use crate::transaction::{LegacyEthSignedTransaction, LegacyEthTransaction}; +use crate::transaction::{ + access_list::{self, AccessListEthSignedTransaction, AccessListEthTransaction}, + LegacyEthSignedTransaction, LegacyEthTransaction, +}; use crate::types; use crate::types::AccountId; @@ -424,6 +427,28 @@ pub(crate) fn sign_transaction( } } +pub(crate) fn sign_access_list_transaction( + tx: AccessListEthTransaction, + secret_key: &SecretKey, +) -> AccessListEthSignedTransaction { + let mut rlp_stream = RlpStream::new(); + rlp_stream.append(&access_list::TYPE_BYTE); + tx.rlp_append_unsigned(&mut rlp_stream); + let message_hash = types::keccak(rlp_stream.as_raw()); + let message = Message::parse_slice(message_hash.as_bytes()).unwrap(); + + let (signature, recovery_id) = secp256k1::sign(&message, secret_key); + let r = U256::from_big_endian(&signature.r.b32()); + let s = U256::from_big_endian(&signature.s.b32()); + + AccessListEthSignedTransaction { + transaction_data: tx, + parity: recovery_id.serialize(), + r, + s, + } +} + pub(crate) fn address_from_secret_key(sk: &SecretKey) -> Address { let pk = PublicKey::from_secret_key(sk); let hash = types::keccak(&pk.serialize()[1..]); @@ -456,3 +481,13 @@ pub fn new_context() -> Context { apparent_value: Default::default(), } } + +pub(crate) fn address_from_hex(address: &str) -> Address { + let bytes = if address.starts_with("0x") { + hex::decode(&address[2..]).unwrap() + } else { + hex::decode(address).unwrap() + }; + + Address::from_slice(&bytes) +} diff --git a/src/tests/access_lists.rs b/src/tests/access_lists.rs new file mode 100644 index 000000000..5676e39a3 --- /dev/null +++ b/src/tests/access_lists.rs @@ -0,0 +1,65 @@ +use crate::prelude::{H256, U256}; +use crate::test_utils; +use crate::transaction::access_list::{self, AccessListEthTransaction, AccessTuple}; +use crate::transaction::EthTransaction; +use crate::types::Wei; +use std::convert::TryFrom; +use std::iter; + +// Test taken from https://github.com/ethereum/tests/blob/develop/GeneralStateTests/stExample/accessListExample.json +// TODO(#170): generally support Ethereum tests +#[test] +fn test_access_list_tx_encoding_decoding() { + let secret_key = secp256k1::SecretKey::parse_slice( + &hex::decode("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8").unwrap(), + ) + .unwrap(); + let transaction = AccessListEthTransaction { + chain_id: 1, + nonce: U256::zero(), + gas_price: U256::from(0x0a), + gas_limit: U256::from(0x061a80), + to: Some(test_utils::address_from_hex( + "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", + )), + value: Wei::new_u64(0x0186a0), + data: vec![0], + access_list: vec![ + AccessTuple { + address: test_utils::address_from_hex("0x095e7baea6a6c7c4c2dfeb977efac326af552d87"), + storage_keys: vec![H256::zero(), one()], + }, + AccessTuple { + address: test_utils::address_from_hex("0x195e7baea6a6c7c4c2dfeb977efac326af552d87"), + storage_keys: vec![H256::zero()], + }, + ], + }; + + let signed_tx = test_utils::sign_access_list_transaction(transaction, &secret_key); + let bytes: Vec = iter::once(access_list::TYPE_BYTE) + .chain(rlp::encode(&signed_tx).into_iter()) + .collect(); + let expected_bytes = hex::decode("01f8f901800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a000f893f85994095e7baea6a6c7c4c2dfeb977efac326af552d87f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001f794195e7baea6a6c7c4c2dfeb977efac326af552d87e1a0000000000000000000000000000000000000000000000000000000000000000080a011c97e0bb8a356fe4f49b37863d059c6fe8cd3214a6ac06a8387a2f6f0b75f60a0212368a1097da30806edfd13d9c35662e1baee939235eb25de867980bd0eda26").unwrap(); + + assert_eq!(bytes, expected_bytes); + + let decoded_tx = match EthTransaction::try_from(expected_bytes.as_slice()) { + Ok(EthTransaction::AccessList(tx)) => tx, + Ok(EthTransaction::Legacy(_)) => panic!("Unexpected transaction type"), + Err(_) => panic!("Transaction parsing failed"), + }; + + assert_eq!(signed_tx, decoded_tx); + + assert_eq!( + signed_tx.sender().unwrap(), + test_utils::address_from_secret_key(&secret_key) + ) +} + +fn one() -> H256 { + let mut x = [0u8; 32]; + x[31] = 1; + H256(x) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 110ff5d45..a5ce75ce2 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,3 +1,4 @@ +mod access_lists; mod contract_call; mod erc20; mod erc20_connector;