Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ETH]: Add support for EIP-2930 access lists #4022

Merged
merged 4 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/linux-ci-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ jobs:
./tools/release-size measure-rust > release-report.json

- name: Upload release report
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: release_report
path: release-report.json

# Download previous release report, compare the release binary sizes, and post/update a comment at the Pull Request.
- name: Download previous release report
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
uses: dawidd6/action-download-artifact@v3
uses: dawidd6/action-download-artifact@v6
with:
commit: ${{github.event.pull_request.base.sha}}
path: previous
Expand Down
10 changes: 5 additions & 5 deletions rust/tw_evm/src/modules/rlp_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ pub struct RlpEncoder<Context: EvmContext> {
}

impl<Context: EvmContext> RlpEncoder<Context> {
pub fn encode<T>(val: T) -> Data
pub fn encode<T>(val: &T) -> Data
where
T: RlpEncode,
T: RlpEncode + ?Sized,
{
let mut buf = RlpBuffer::new();
val.rlp_append(&mut buf);
Expand Down Expand Up @@ -64,16 +64,16 @@ impl<Context: EvmContext> RlpEncoder<Context> {

let encoded_item = match rlp_item.item {
Item::string_item(str) => RlpEncoder::<Context>::encode(str.as_ref()),
Item::number_u64(num) => RlpEncoder::<Context>::encode(U256::from(num)),
Item::number_u64(num) => RlpEncoder::<Context>::encode(&U256::from(num)),
Item::number_u256(num_be) => {
let num = U256::from_big_endian_slice(num_be.as_ref())
.into_tw()
.context("Invalid U256 number")?;
RlpEncoder::<Context>::encode(num)
RlpEncoder::<Context>::encode(&num)
},
Item::address(addr_s) => {
let addr = Context::Address::from_str(addr_s.as_ref())?;
RlpEncoder::<Context>::encode(addr.into())
RlpEncoder::<Context>::encode(&addr.into())
},
Item::data(data) => RlpEncoder::<Context>::encode(data.as_ref()),
Item::list(proto_nested_list) => {
Expand Down
30 changes: 28 additions & 2 deletions rust/tw_evm/src/modules/tx_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ use crate::abi::prebuild::erc4337::{Erc4337SimpleAccount, ExecuteArgs};
use crate::abi::prebuild::erc721::Erc721;
use crate::address::{Address, EvmAddress};
use crate::evm_context::EvmContext;
use crate::transaction::access_list::{Access, AccessList};
use crate::transaction::transaction_eip1559::TransactionEip1559;
use crate::transaction::transaction_non_typed::TransactionNonTyped;
use crate::transaction::user_operation::UserOperation;
use crate::transaction::UnsignedTransactionBox;
use std::marker::PhantomData;
use std::str::FromStr;
use tw_coin_entry::error::prelude::*;
use tw_hash::H256;
use tw_memory::Data;
use tw_number::U256;
use tw_proto::Common::Proto::SigningError as CommonError;
Expand Down Expand Up @@ -249,6 +251,9 @@ impl<Context: EvmContext> TxBuilder<Context> {
.into_tw()
.context("Invalid max fee per gas")?;

let access_list =
Self::parse_access_list(&input.access_list).context("Invalid access list")?;

Ok(TransactionEip1559 {
nonce,
max_inclusion_fee_per_gas,
Expand All @@ -257,6 +262,7 @@ impl<Context: EvmContext> TxBuilder<Context> {
to: to_address,
amount: eth_amount,
payload,
access_list,
})
}

Expand Down Expand Up @@ -315,19 +321,39 @@ impl<Context: EvmContext> TxBuilder<Context> {
})
}

#[inline]
fn parse_address(addr: &str) -> SigningResult<Address> {
Context::Address::from_str(addr)
.map(Context::Address::into)
.map_err(SigningError::from)
}

#[inline]
fn parse_address_optional(addr: &str) -> SigningResult<Option<Address>> {
match Context::Address::from_str_optional(addr) {
Ok(Some(addr)) => Ok(Some(addr.into())),
Ok(None) => Ok(None),
Err(e) => Err(SigningError::from(e)),
}
}

fn parse_access_list(list_proto: &[Proto::Access]) -> SigningResult<AccessList> {
let mut access_list = AccessList::default();
for access_proto in list_proto.iter() {
access_list.add_access(Self::parse_access(access_proto)?);
}
Ok(access_list)
}

fn parse_access(access_proto: &Proto::Access) -> SigningResult<Access> {
let addr =
Self::parse_address(access_proto.address.as_ref()).context("Invalid access address")?;

let mut access = Access::new(addr);
for key_proto in access_proto.stored_keys.iter() {
let storage_key = H256::try_from(key_proto.as_ref())
.tw_err(|_| SigningErrorType::Error_invalid_params)
.context("Invalid storage key")?;
access.add_storage_key(storage_key);
}
Ok(access)
}
}
11 changes: 9 additions & 2 deletions rust/tw_evm/src/rlp/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use crate::address::Address;
use crate::rlp::buffer::RlpBuffer;
use crate::rlp::RlpEncode;
use tw_hash::H256;
use tw_number::U256;

impl RlpEncode for U256 {
Expand All @@ -13,6 +14,12 @@ impl RlpEncode for U256 {
}
}

impl RlpEncode for H256 {
fn rlp_append(&self, buf: &mut RlpBuffer) {
buf.append_data(self.as_slice())
}
}

impl RlpEncode for Address {
fn rlp_append(&self, buf: &mut RlpBuffer) {
buf.append_data(self.as_slice())
Expand All @@ -28,13 +35,13 @@ impl RlpEncode for Option<Address> {
}
}

impl<'a> RlpEncode for &'a [u8] {
impl RlpEncode for [u8] {
fn rlp_append(&self, buf: &mut RlpBuffer) {
buf.append_data(self)
}
}

impl<'a> RlpEncode for &'a str {
impl RlpEncode for str {
fn rlp_append(&self, buf: &mut RlpBuffer) {
buf.append_data(self.as_bytes())
}
Expand Down
4 changes: 2 additions & 2 deletions rust/tw_evm/src/rlp/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ impl RlpList {
}

/// Appends an item.
pub fn append<T>(&mut self, item: T) -> &mut Self
pub fn append<T>(&mut self, item: &T) -> &mut Self
where
T: RlpEncode,
T: RlpEncode + ?Sized,
{
item.rlp_append(&mut self.buf);
self
Expand Down
72 changes: 72 additions & 0 deletions rust/tw_evm/src/transaction/access_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use crate::address::Address;
use crate::rlp::buffer::RlpBuffer;
use crate::rlp::RlpEncode;
use tw_hash::H256;

/// A list of addresses and storage keys that the transaction plans to access.
pub struct Access {
pub address: Address,
pub storage_keys: Vec<H256>,
}

impl Access {
#[inline]
pub fn new(address: Address) -> Self {
Access {
address,
storage_keys: Vec::default(),
}
}

#[inline]
pub fn add_storage_key(&mut self, key: H256) -> &mut Self {
self.storage_keys.push(key);
self
}
}

impl RlpEncode for Access {
fn rlp_append(&self, buf: &mut RlpBuffer) {
buf.begin_list();
self.address.rlp_append(buf);

// append the list of keys
{
buf.begin_list();
for storage_key in self.storage_keys.iter() {
storage_key.rlp_append(buf);
}
buf.finalize_list();
}

buf.finalize_list();
}
}

/// [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) access list.
#[derive(Default)]
pub struct AccessList(Vec<Access>);

impl AccessList {
#[inline]
pub fn add_access(&mut self, access: Access) -> &mut Self {
self.0.push(access);
self
}
}

impl RlpEncode for AccessList {
fn rlp_append(&self, buf: &mut RlpBuffer) {
buf.begin_list();

for access in self.0.iter() {
access.rlp_append(buf);
}

buf.finalize_list();
}
}
1 change: 1 addition & 0 deletions rust/tw_evm/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use tw_keypair::ecdsa::secp256k1;
use tw_memory::Data;
use tw_number::U256;

pub mod access_list;
pub mod signature;
pub mod transaction_eip1559;
pub mod transaction_non_typed;
Expand Down
70 changes: 54 additions & 16 deletions rust/tw_evm/src/transaction/transaction_eip1559.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use crate::address::Address;
use crate::rlp::list::RlpList;
use crate::transaction::access_list::AccessList;
use crate::transaction::signature::{EthSignature, Signature};
use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction};
use tw_coin_entry::error::prelude::*;
Expand All @@ -22,6 +23,7 @@ pub struct TransactionEip1559 {
pub to: Option<Address>,
pub amount: U256,
pub payload: Data,
pub access_list: AccessList,
}

impl TransactionCommon for TransactionEip1559 {
Expand Down Expand Up @@ -86,21 +88,20 @@ fn encode_transaction(
signature: Option<&Signature>,
) -> Data {
let mut list = RlpList::new();
list.append(chain_id)
.append(tx.nonce)
.append(tx.max_inclusion_fee_per_gas)
.append(tx.max_fee_per_gas)
.append(tx.gas_limit)
.append(tx.to)
.append(tx.amount)
list.append(&chain_id)
.append(&tx.nonce)
.append(&tx.max_inclusion_fee_per_gas)
.append(&tx.max_fee_per_gas)
.append(&tx.gas_limit)
.append(&tx.to)
.append(&tx.amount)
.append(tx.payload.as_slice())
// empty `access_list`.
.append_empty_list();
.append(&tx.access_list);

if let Some(signature) = signature {
list.append(signature.v());
list.append(signature.r());
list.append(signature.s());
list.append(&signature.v());
list.append(&signature.r());
list.append(&signature.s());
}

let tx_encoded = list.finish();
Expand All @@ -114,23 +115,60 @@ fn encode_transaction(
#[cfg(test)]
mod tests {
use super::*;
use crate::transaction::access_list::Access;
use tw_encoding::hex;
use tw_hash::H256;

#[test]
fn test_encode_transaction_eip1559() {
let tx = TransactionEip1559 {
nonce: U256::from(6u64),
max_inclusion_fee_per_gas: U256::from(2_000_000_000u64),
max_fee_per_gas: U256::from(3_000_000_000u64),
gas_limit: U256::from(21100u32),
nonce: U256::from(6_u64),
max_inclusion_fee_per_gas: U256::from(2_000_000_000_u64),
max_fee_per_gas: U256::from(3_000_000_000_u64),
gas_limit: U256::from(21100_u64),
to: Some(Address::from("0x6b175474e89094c44da98b954eedeac495271d0f")),
amount: U256::zero(),
payload: hex::decode("a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1").unwrap(),
access_list: AccessList::default(),
};
let chain_id = U256::from(10u64);
let actual = tx.encode(chain_id);

let expected = "02f86c0a06847735940084b2d05e0082526c946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1c0";
assert_eq!(hex::encode(actual, false), expected);
}

#[test]
fn test_encode_transaction_eip1559_with_access_list() {
let mut access = Access::new(Address::from("0xdAC17F958D2ee523a2206206994597C13D831ec7"));

#[rustfmt::skip]
access
.add_storage_key(H256::from("0x76c8f33bcdf467e4f1313522c10a40512a867cdcd34f2b898232ad4669200764"))
.add_storage_key(H256::from("0x000000000000000000000000000000000000000000000000000000000000000a"))
.add_storage_key(H256::from("0x0000000000000000000000000000000000000000000000000000000000000003"))
.add_storage_key(H256::from("0x0000000000000000000000000000000000000000000000000000000000000004"))
.add_storage_key(H256::from("0xb12459e057d0da4389f95b7ff0ce45a52ad71b02913a5466ffaab252e7ce918a"))
.add_storage_key(H256::from("0x1bba044274699cc8c429fbe84bdad5d5a49519e29430f25309cbbab31dc63043"))
.add_storage_key(H256::from("0x0000000000000000000000000000000000000000000000000000000000000000"));

let mut access_list = AccessList::default();
access_list.add_access(access);

let tx = TransactionEip1559 {
nonce: U256::from(1u64),
max_inclusion_fee_per_gas: U256::from(2_000_000_000_u64),
max_fee_per_gas: U256::from(3_000_000_000_u64),
gas_limit: U256::from(100_000_u64),
to: Some(Address::from("0xdAC17F958D2ee523a2206206994597C13D831ec7")),
amount: U256::zero(),
payload: hex::decode("a9059cbb000000000000000000000000b2fb4372e663b2e53da97d98100433d1fd06ca5500000000000000000000000000000000000000000000000000000000000f4240").unwrap(),
access_list,
};
let chain_id = U256::from(1_u64);
let actual = tx.encode(chain_id);

let expected = "02f9016f0101847735940084b2d05e00830186a094dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000b2fb4372e663b2e53da97d98100433d1fd06ca5500000000000000000000000000000000000000000000000000000000000f4240f90100f8fe94dac17f958d2ee523a2206206994597c13d831ec7f8e7a076c8f33bcdf467e4f1313522c10a40512a867cdcd34f2b898232ad4669200764a0000000000000000000000000000000000000000000000000000000000000000aa00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004a0b12459e057d0da4389f95b7ff0ce45a52ad71b02913a5466ffaab252e7ce918aa01bba044274699cc8c429fbe84bdad5d5a49519e29430f25309cbbab31dc63043a00000000000000000000000000000000000000000000000000000000000000000";
assert_eq!(hex::encode(actual, false), expected);
}
}
12 changes: 6 additions & 6 deletions rust/tw_evm/src/transaction/transaction_non_typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,18 @@ fn encode_transaction(
signature: Option<&SignatureEip155>,
) -> Data {
let mut list = RlpList::new();
list.append(tx.nonce)
.append(tx.gas_price)
.append(tx.gas_limit)
.append(tx.to)
.append(tx.amount)
list.append(&tx.nonce)
.append(&tx.gas_price)
.append(&tx.gas_limit)
.append(&tx.to)
.append(&tx.amount)
.append(tx.payload.as_slice());

let (v, r, s) = match signature {
Some(sign) => (sign.v(), sign.r(), sign.s()),
None => (chain_id, U256::zero(), U256::zero()),
};
list.append(v).append(r).append(s);
list.append(&v).append(&r).append(&s);
list.finish()
}

Expand Down
Loading
Loading