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

Feat: Add EIP-7702 - new type transaction #960

Open
wants to merge 16 commits into
base: feat/prague-hard-fork
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 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
8 changes: 8 additions & 0 deletions engine-standalone-storage/src/sync/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ impl TransactionKind {
value,
data,
access_list: Vec::new(),
authorization_list: Vec::new(),
}
}
Self::Deploy(data) => {
Expand All @@ -238,6 +239,7 @@ impl TransactionKind {
value: Wei::zero(),
data,
access_list: Vec::new(),
authorization_list: Vec::new(),
}
}
Self::DeployErc20(_) => {
Expand All @@ -256,6 +258,7 @@ impl TransactionKind {
value: Wei::zero(),
data,
access_list: Vec::new(),
authorization_list: Vec::new(),
}
}
Self::FtOnTransfer(args) => {
Expand All @@ -277,6 +280,7 @@ impl TransactionKind {
value,
data: Vec::new(),
access_list: Vec::new(),
authorization_list: Vec::new(),
}
} else {
let from = Self::get_implicit_address(engine_account);
Expand Down Expand Up @@ -313,6 +317,7 @@ impl TransactionKind {
value: Wei::zero(),
data,
access_list: Vec::new(),
authorization_list: Vec::new(),
}
}
}
Expand Down Expand Up @@ -345,6 +350,7 @@ impl TransactionKind {
value,
data: Vec::new(),
access_list: Vec::new(),
authorization_list: Vec::new(),
}
},
|erc20_address| {
Expand Down Expand Up @@ -372,6 +378,7 @@ impl TransactionKind {
value: Wei::zero(),
data,
access_list: Vec::new(),
authorization_list: Vec::new(),
}
},
)
Expand Down Expand Up @@ -474,6 +481,7 @@ impl TransactionKind {
value: Wei::zero(),
data: method_name.as_bytes().to_vec(),
access_list: Vec::new(),
authorization_list: Vec::new(),
}
}

Expand Down
159 changes: 158 additions & 1 deletion engine-tests/src/tests/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ use crate::prelude::Wei;
use crate::prelude::{H256, U256};
use crate::utils;
use aurora_engine::parameters::SubmitResult;
use aurora_engine_transactions::eip_2930;
use aurora_engine_transactions::eip_2930::Transaction2930;
use aurora_engine_transactions::eip_7702::{AuthorizationTuple, Transaction7702};
use aurora_engine_transactions::{eip_2930, eip_7702};
use aurora_engine_types::borsh::BorshDeserialize;
use aurora_engine_types::types::Address;
use aurora_engine_types::H160;
use std::convert::TryFrom;
use std::iter;

Expand All @@ -22,6 +25,15 @@ const CONTRACT_BALANCE: Wei = Wei::new_u64(0x0de0b6b3a7640000);

const EXAMPLE_TX_HEX: &str = "02f8c101010a8207d0833d090094cccccccccccccccccccccccccccccccccccccccc8000f85bf85994ccccccccccccccccccccccccccccccccccccccccf842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000180a0d671815898b8dd34321adbba4cb6a57baa7017323c26946f3719b00e70c755c2a03528b9efe3be57ea65a933d1e6bbf3b7d0c78830138883c1201e0c641fee6464";

/*
PUSH20 0xd16292912e440956828f1a847ca6efc8412f45b6 [0x73d16292912e440956828f1a847ca6efc8412f45b6]
EXTCODESIZE [0x3B]
PUSH1 0x00 [0x6000]
SSTORE [0x55]
STOP [0x00]
*/
const CONTRACT_CODE_EIP7702: &str = "73d16292912e440956828f1a847ca6efc8412f45b63B60005500";

// Test taken from https://github.com/ethereum/tests/blob/develop/GeneralStateTests/stExample/eip1559.json
// TODO(#170): generally support Ethereum tests
#[test]
Expand Down Expand Up @@ -161,6 +173,129 @@ fn test_access_list_tx_encoding_decoding() {
);
}

#[test]
fn test_eip_7702_tx_encoding_decoding() {
let secret_key = example_signer().secret_key;
let transaction = eip7702_transaction(0);

let signed_tx = utils::sign_eip_7702_transaction(transaction, &secret_key);
let tx_bytes: Vec<u8> = iter::once(eip_7702::TYPE_BYTE)
.chain(rlp::encode(&signed_tx))
.collect();

let decoded_tx = match EthTransactionKind::try_from(tx_bytes.as_slice()) {
Ok(EthTransactionKind::Eip7702(tx)) => tx,
Ok(_) => panic!("Unexpected transaction type"),
Err(e) => panic!("Transaction parsing failed: {e:?}"),
};

assert_eq!(signed_tx, decoded_tx);
assert_eq!(
signed_tx.sender().unwrap(),
utils::address_from_secret_key(&secret_key)
);
}

#[test]
fn test_eip_7702_success() {
// 0xa52a8a2229e3c512d6ed27b6e6e7d39958ca9fb3,
let mut runner = utils::deploy_runner();
let signer = example_signer();
let signer_address = utils::address_from_secret_key(&signer.secret_key);
let contract_address = utils::address_from_hex(CONTRACT_ADDRESS);
let contract_code = hex::decode(CONTRACT_CODE_EIP7702).unwrap();

runner.create_address(signer_address, INITIAL_BALANCE, signer.nonce.into());
runner.create_address_with_code(
contract_address,
CONTRACT_BALANCE,
CONTRACT_NONCE.into(),
contract_code.clone(),
);

let mut transaction = eip7702_transaction(0);
transaction.chain_id = runner.chain_id;
let signed_tx = utils::sign_eip_7702_transaction(transaction, &signer.secret_key);
let tx_bytes: Vec<u8> = iter::once(eip_7702::TYPE_BYTE)
.chain(rlp::encode(&signed_tx))
.collect();

let sender = "relay.aurora";
let outcome = runner.call(utils::SUBMIT, sender, tx_bytes).unwrap();
// Unwrapping execution results validates outcome
let result = SubmitResult::try_from_slice(&outcome.return_data.as_value().unwrap()).unwrap();

let delegated_designator = Address::decode("d16292912e440956828f1a847ca6efc8412f45b6").unwrap();

assert_eq!(result.gas_used, 68206);
assert_eq!(runner.get_nonce(signer_address), (signer.nonce + 1).into());
assert_eq!(runner.get_balance(contract_address), CONTRACT_BALANCE);
assert_eq!(runner.get_nonce(contract_address), CONTRACT_NONCE.into());
assert_eq!(runner.get_code(contract_address), contract_code);
// `EXTCODESIZE` should return size of `EF01` = 2 for delegated designator
assert_eq!(
runner.get_storage(contract_address, H256::zero()),
H256::from(H160::from_low_u64_be(2))
);

// Authority address should increase Nonce
assert_eq!(runner.get_nonce(delegated_designator), 1.into());
// Get delegated designator address
assert_eq!(
hex::encode(runner.get_code(delegated_designator)),
"ef0100cccccccccccccccccccccccccccccccccccccccc"
);
}

#[test]
fn test_eip_7702_wrong_auth_nonce() {
mrLSD marked this conversation as resolved.
Show resolved Hide resolved
let mut runner = utils::deploy_runner();
let signer = example_signer();
let signer_address = utils::address_from_secret_key(&signer.secret_key);
let contract_address = utils::address_from_hex(CONTRACT_ADDRESS);
// Contract for auth address: 0xa52a8a2229e3c512d6ed27b6e6e7d39958ca9fb3
let contract_code =
hex::decode("73a52a8a2229e3c512d6ed27b6e6e7d39958ca9fb33B60005500").unwrap();

runner.create_address(signer_address, INITIAL_BALANCE, signer.nonce.into());
runner.create_address_with_code(
contract_address,
CONTRACT_BALANCE,
CONTRACT_NONCE.into(),
contract_code.clone(),
);

let mut transaction = eip7702_transaction(10);
transaction.chain_id = runner.chain_id;
let signed_tx = utils::sign_eip_7702_transaction(transaction, &signer.secret_key);
let tx_bytes: Vec<u8> = iter::once(eip_7702::TYPE_BYTE)
.chain(rlp::encode(&signed_tx))
.collect();

let sender = "relay.aurora";
let outcome = runner.call(utils::SUBMIT, sender, tx_bytes).unwrap();
// Unwrapping execution results validates outcome
let result = SubmitResult::try_from_slice(&outcome.return_data.as_value().unwrap()).unwrap();

let delegated_designator = Address::decode("a52a8a2229e3c512d6ed27b6e6e7d39958ca9fb3").unwrap();

assert_eq!(result.gas_used, 50806);
assert_eq!(runner.get_nonce(signer_address), (signer.nonce + 1).into());
assert_eq!(runner.get_balance(contract_address), CONTRACT_BALANCE);
assert_eq!(runner.get_nonce(contract_address), CONTRACT_NONCE.into());
assert_eq!(runner.get_code(contract_address), contract_code);
// `EXTCODESIZE` should return zero, as `authorization_list` failed validation
assert_eq!(
runner.get_storage(contract_address, H256::zero()),
H256::zero()
);

// Authority address should increase Nonce
mrLSD marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(runner.get_nonce(delegated_designator), 0.into());
// Get delegated designator address: in that particular case it should be empty
assert!(runner.get_code(delegated_designator).is_empty());
}

fn encode_tx(signed_tx: &SignedTransaction1559) -> Vec<u8> {
iter::once(eip_1559::TYPE_BYTE)
.chain(rlp::encode(signed_tx))
Expand All @@ -177,6 +312,28 @@ fn example_signer() -> utils::Signer {
}
}

fn eip7702_transaction(auth_chain_id: u64) -> Transaction7702 {
Transaction7702 {
chain_id: 1,
nonce: INITIAL_NONCE.into(),
gas_limit: U256::from(0x3d0900),
max_fee_per_gas: U256::from(0x07d0),
max_priority_fee_per_gas: U256::from(0x0a),
to: utils::address_from_hex(CONTRACT_ADDRESS),
value: Wei::zero(),
data: vec![],
access_list: vec![],
authorization_list: vec![AuthorizationTuple {
chain_id: auth_chain_id.into(),
address: utils::address_from_hex(CONTRACT_ADDRESS).raw(),
nonce: 0,
parity: 1.into(),
r: 2.into(),
s: 3.into(),
}],
}
}

fn example_transaction() -> Transaction1559 {
Transaction1559 {
chain_id: 1,
Expand Down
45 changes: 34 additions & 11 deletions engine-tests/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
#[cfg(not(feature = "ext-connector"))]
use crate::prelude::parameters::InitCallArgs;
use crate::prelude::parameters::{StartHashchainArgs, SubmitResult, TransactionStatus};
use crate::prelude::transactions::{
eip_1559::{self, SignedTransaction1559, Transaction1559},
eip_2930::{self, SignedTransaction2930, Transaction2930},
legacy::{LegacyEthSignedTransaction, TransactionLegacy},
};
use crate::prelude::{sdk, Address, Wei, H256, U256};
use crate::utils::solidity::{ContractConstructor, DeployedContract};
use aurora_engine::engine::{EngineError, EngineErrorKind, GasPaymentError};
use aurora_engine::parameters::{SubmitArgs, ViewCallArgs};
use aurora_engine_transactions::eip_7702;
use aurora_engine_transactions::eip_7702::{SignedTransaction7702, Transaction7702};
use aurora_engine_types::account_id::AccountId;
use aurora_engine_types::borsh::BorshDeserialize;
#[cfg(not(feature = "ext-connector"))]
Expand All @@ -25,17 +37,6 @@ use rlp::RlpStream;
use std::borrow::Cow;
use std::sync::Arc;

#[cfg(not(feature = "ext-connector"))]
use crate::prelude::parameters::InitCallArgs;
use crate::prelude::parameters::{StartHashchainArgs, SubmitResult, TransactionStatus};
use crate::prelude::transactions::{
eip_1559::{self, SignedTransaction1559, Transaction1559},
eip_2930::{self, SignedTransaction2930, Transaction2930},
legacy::{LegacyEthSignedTransaction, TransactionLegacy},
};
use crate::prelude::{sdk, Address, Wei, H256, U256};
use crate::utils::solidity::{ContractConstructor, DeployedContract};

pub const DEFAULT_AURORA_ACCOUNT_ID: &str = "aurora";
pub const SUBMIT: &str = "submit";
pub const SUBMIT_WITH_ARGS: &str = "submit_with_args";
Expand Down Expand Up @@ -945,6 +946,28 @@ pub fn sign_eip_1559_transaction(
}
}

pub fn sign_eip_7702_transaction(
tx: Transaction7702,
secret_key: &SecretKey,
) -> SignedTransaction7702 {
let mut rlp_stream = RlpStream::new();
rlp_stream.append(&eip_7702::TYPE_BYTE);
tx.rlp_append_unsigned(&mut rlp_stream);
let message_hash = sdk::keccak(rlp_stream.as_raw());
let message = Message::parse_slice(message_hash.as_bytes()).unwrap();

let (signature, recovery_id) = libsecp256k1::sign(&message, secret_key);
let r = U256::from_big_endian(&signature.r.b32());
let s = U256::from_big_endian(&signature.s.b32());

SignedTransaction7702 {
transaction: tx,
parity: recovery_id.serialize(),
r,
s,
}
}

pub fn address_from_secret_key(sk: &SecretKey) -> Address {
let pk = PublicKey::from_secret_key(sk);
let hash = sdk::keccak(&pk.serialize()[1..]);
Expand Down
1 change: 1 addition & 0 deletions engine-tests/src/utils/solidity/erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,6 @@ pub fn legacy_into_normalized_tx(tx: TransactionLegacy) -> NormalizedEthTransact
value: tx.value,
data: tx.data,
access_list: Vec::new(),
authorization_list: Vec::new(),
}
}
3 changes: 3 additions & 0 deletions engine-transactions/src/backwards_compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ impl EthTransactionKindAdapter {
tx.transaction.to = None;
}
}
EthTransactionKind::Eip7702(_) => {
// For Prague hard fork `tx.transaction.to` can't be `None`
}
}
}

Expand Down
Loading
Loading