Skip to content
This repository has been archived by the owner on Jan 8, 2025. It is now read-only.

refactor: eth_send_raw_transaction #1015

Merged
merged 4 commits into from
Oct 4, 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
13 changes: 4 additions & 9 deletions crates/contracts/src/account_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub mod AccountContract {
use crate::storage::StorageBytecode;
use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
use super::OutsideExecution;
use utils::eth_transaction::transaction::TransactionUnsignedTrait;
use utils::eth_transaction::transaction::TransactionTrait;
use utils::serialization::{deserialize_signature, deserialize_bytes, serialize_bytes};
use utils::traits::DefaultSignature;

Expand Down Expand Up @@ -249,18 +249,13 @@ pub mod AccountContract {
let mut encoded_tx_data = deserialize_bytes((*outside_execution.calls[0]).calldata)
.expect('conversion to Span<u8> failed')
.span();
let unsigned_transaction = TransactionUnsignedTrait::decode_enveloped(
ref encoded_tx_data
)
.expect('EOA: could not decode tx');
let unsigned_transaction_hash = TransactionTrait::compute_hash(encoded_tx_data);

let address = self.Account_evm_address.read();
verify_eth_signature(unsigned_transaction.hash, signature, address);
verify_eth_signature(unsigned_transaction_hash, signature, address);

//TODO: refactor this to call eth_send_raw_unsigned_tx. Only the transactions bytes are
//passed.
let (success, return_data, gas_used) = kakarot
.eth_send_transaction(unsigned_transaction.transaction);
.eth_send_raw_unsigned_tx(encoded_tx_data);
let return_data = serialize_bytes(return_data).span();

// See Argent account
Expand Down
9 changes: 6 additions & 3 deletions crates/contracts/src/kakarot_core/eth_rpc.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use evm::model::{TransactionResult, Address};
use evm::{EVMTrait};
use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
use utils::constants::POW_2_53;
use utils::eth_transaction::transaction::Transaction;
use utils::eth_transaction::transaction::{Transaction, TransactionTrait};

#[starknet::interface]
pub trait IEthRPC<T> {
Expand Down Expand Up @@ -190,10 +190,13 @@ pub impl EthRPC<
(success, return_data, gas_used)
}

//TODO: we can't really unit-test this with foundry because we can't generate the RLP-encoding
//in Cairo Find another way - perhaps test-data gen with python?
fn eth_send_raw_unsigned_tx(
ref self: TContractState, tx_data: Span<u8>
ref self: TContractState, mut tx_data: Span<u8>
) -> (bool, Span<u8>, u64) {
panic!("unimplemented")
let tx = TransactionTrait::decode_enveloped(ref tx_data).expect('EOA: could not decode tx');
Self::eth_send_transaction(ref self, tx)
}
}

Expand Down
4 changes: 4 additions & 0 deletions crates/contracts/src/kakarot_core/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ pub trait IExtendedKakarotCore<TContractState> {
/// Executes an EVM transaction and possibly modifies the state
fn eth_send_transaction(ref self: TContractState, tx: Transaction) -> (bool, Span<u8>, u64);

fn eth_send_raw_unsigned_tx(
ref self: TContractState, encoded_tx_data: Span<u8>
) -> (bool, Span<u8>, u64);

// Returns the transaction count (nonce) of the specified address
fn eth_get_transaction_count(self: @TContractState, address: EthAddress) -> u64;

Expand Down
29 changes: 0 additions & 29 deletions crates/contracts/tests/test_execution_from_outside.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -251,35 +251,6 @@ fn test_execute_from_outside_invalid_signature() {
tear_down(contract_account);
}

#[test]
#[should_panic(expected: 'EOA: could not decode tx')]
fn test_execute_from_outside_invalid_tx() {
let (kakarot_core, contract_account, _) = set_up();

let mut faulty_eip_2930_tx = eip_2930_encoded_tx();
let signature = Signature {
r: 0x5c4ae1ed01c8df4277f02aa3443f8183ed44627217fd7f27badaed8795906e78,
s: 0x4d2af576441428d47c174ffddc6e70b980527a57795b3c87a71878f97ecef274,
y_parity: true
};
let _ = faulty_eip_2930_tx.pop_front();

let outside_execution = OutsideExecutionBuilderTrait::new(kakarot_core.contract_address)
.with_calls(
[
CallBuilderTrait::new(kakarot_core.contract_address)
.with_calldata(faulty_eip_2930_tx)
.build()
].span()
)
.build();

let signature = serialize_transaction_signature(signature, TxType::Eip2930, chain_id()).span();

let _ = contract_account.execute_from_outside(outside_execution, signature);

tear_down(contract_account);
}

#[test]
#[should_panic(expected: 'KKRT: Multicall not supported')]
Expand Down
3 changes: 2 additions & 1 deletion crates/contracts/tests/test_kakarot_core.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ fn test_eth_send_transaction_deploy_tx() {
let value = 0;

// When
// Set the contract address to the EOA address, so that the caller of the `eth_send_transaction`
// Set the contract address to the EOA address, so that the caller of the
// `eth_send_transaction`
// is an eoa
let tx = TxLegacy {
chain_id: Option::Some(chain_id()),
Expand Down
114 changes: 46 additions & 68 deletions crates/utils/src/eth_transaction/transaction.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,8 @@ pub impl _Transasction of TransactionTrait {
Transaction::Eip1559(tx) => tx.input,
}
}
}


#[derive(Copy, Drop, Debug, PartialEq)]
pub struct TransactionUnsigned {
/// Transaction hash
pub hash: u256,
/// Raw transaction info
pub transaction: Transaction,
}

#[generate_trait]
pub impl _TransactionUnsigned of TransactionUnsignedTrait {
/// Decodes the "raw" format of transaction (similar to `eth_sendRawTransaction`).
///
/// This should be used for any method that accepts a raw transaction.
Expand All @@ -201,9 +190,7 @@ pub impl _TransactionUnsigned of TransactionUnsignedTrait {
///
/// Both for legacy and EIP-2718 transactions, an error will be returned if there is an excess
/// of bytes in input data.
fn decode_enveloped(
ref tx_data: Span<u8>,
) -> Result<TransactionUnsigned, EthTransactionError> {
fn decode_enveloped(ref tx_data: Span<u8>,) -> Result<Transaction, EthTransactionError> {
if tx_data.is_empty() {
return Result::Err(EthTransactionError::RLPError(RLPError::InputTooShort));
}
Expand Down Expand Up @@ -233,9 +220,7 @@ pub impl _TransactionUnsigned of TransactionUnsignedTrait {
/// chainId, 0, 0]
/// Note: this function assumes that tx_type has been checked to make sure it is a legacy
/// transaction
fn decode_legacy_tx(
ref encoded_tx_data: Span<u8>
) -> Result<TransactionUnsigned, EthTransactionError> {
fn decode_legacy_tx(ref encoded_tx_data: Span<u8>) -> Result<Transaction, EthTransactionError> {
let rlp_decoded_data = RLPTrait::decode(encoded_tx_data);
let mut rlp_decoded_data = rlp_decoded_data.map_err()?;

Expand All @@ -256,11 +241,7 @@ pub impl _TransactionUnsigned of TransactionUnsignedTrait {
}
};

let tx_hash = Self::compute_hash(encoded_tx_data);

Result::Ok(
TransactionUnsigned { transaction: Transaction::Legacy(legacy_tx), hash: tx_hash, }
)
Result::Ok(Transaction::Legacy(legacy_tx))
}

/// Decodes an enveloped EIP-2718 typed transaction.
Expand All @@ -275,7 +256,7 @@ pub impl _TransactionUnsigned of TransactionUnsignedTrait {
/// CAUTION: this expects that `data` is `tx-type || rlp(tx-data)`
fn decode_enveloped_typed_transaction(
ref encoded_tx_data: Span<u8>
) -> Result<TransactionUnsigned, EthTransactionError> {
) -> Result<Transaction, EthTransactionError> {
// keep this around so we can use it to calculate the hash
let original_data = encoded_tx_data;

Expand Down Expand Up @@ -316,8 +297,7 @@ pub impl _TransactionUnsigned of TransactionUnsignedTrait {
}
};

let tx_hash = Self::compute_hash(original_data);
Result::Ok(TransactionUnsigned { transaction, hash: tx_hash })
Result::Ok(transaction)
}

/// Returns the hash of the unsigned transaction
Expand Down Expand Up @@ -355,7 +335,7 @@ mod tests {
legacy_rlp_encoded_tx, legacy_rlp_encoded_deploy_tx, eip_2930_encoded_tx,
eip_1559_encoded_tx
};
use super::{TransactionTrait, TransactionUnsignedTrait};
use super::{TransactionTrait};


#[test]
Expand All @@ -368,18 +348,18 @@ mod tests {
// message_hash: 0xcf71743e6e25fef715398915997f782b95554c8bbfb7b3f7701e007332ed31b4
// chain id used: 'KKRT'
let mut encoded_tx_data = legacy_rlp_encoded_tx();
let decoded = TransactionUnsignedTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(decoded.transaction.nonce(), 0);
assert_eq!(decoded.transaction.max_fee_per_gas(), 0x3b9aca00);
assert_eq!(decoded.transaction.gas_limit(), 0x1e8480);
let transaction = TransactionTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(transaction.nonce(), 0);
assert_eq!(transaction.max_fee_per_gas(), 0x3b9aca00);
assert_eq!(transaction.gas_limit(), 0x1e8480);
assert_eq!(
decoded.transaction.kind(),
transaction.kind(),
TxKind::Call(0x1f9840a85d5af5bf1d1762f925bdaddc4201f984.try_into().unwrap())
);
assert_eq!(decoded.transaction.value(), 0x016345785d8a0000);
assert_eq!(decoded.transaction.input(), [0xab, 0xcd, 0xef].span());
assert_eq!(decoded.transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(decoded.transaction.transaction_type(), TxType::Legacy);
assert_eq!(transaction.value(), 0x016345785d8a0000);
assert_eq!(transaction.input(), [0xab, 0xcd, 0xef].span());
assert_eq!(transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(transaction.transaction_type(), TxType::Legacy);
}

#[test]
Expand All @@ -389,18 +369,18 @@ mod tests {
// expected rlp decoding:
// ["0x","0x0a","0x061a80","0x","0x0186a0","0x600160010a5060006000f3","0x4b4b5254","0x","0x"]
let mut encoded_tx_data = legacy_rlp_encoded_deploy_tx();
let decoded = TransactionUnsignedTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(decoded.transaction.nonce(), 0);
assert_eq!(decoded.transaction.max_fee_per_gas(), 0x0a);
assert_eq!(decoded.transaction.gas_limit(), 0x061a80);
assert_eq!(decoded.transaction.kind(), TxKind::Create);
assert_eq!(decoded.transaction.value(), 0x0186a0);
let transaction = TransactionTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(transaction.nonce(), 0);
assert_eq!(transaction.max_fee_per_gas(), 0x0a);
assert_eq!(transaction.gas_limit(), 0x061a80);
assert_eq!(transaction.kind(), TxKind::Create);
assert_eq!(transaction.value(), 0x0186a0);
assert_eq!(
decoded.transaction.input(),
transaction.input(),
[0x60, 0x01, 0x60, 0x01, 0x0a, 0x50, 0x60, 0x00, 0x60, 0x00, 0xf3].span()
);
assert_eq!(decoded.transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(decoded.transaction.transaction_type(), TxType::Legacy);
assert_eq!(transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(transaction.transaction_type(), TxType::Legacy);
}

#[test]
Expand All @@ -416,18 +396,18 @@ mod tests {
// chain id used: 'KKRT'

let mut encoded_tx_data = eip_2930_encoded_tx();
let decoded = TransactionUnsignedTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(decoded.transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(decoded.transaction.nonce(), 0);
assert_eq!(decoded.transaction.max_fee_per_gas(), 0x3b9aca00);
assert_eq!(decoded.transaction.gas_limit(), 0x1e8480);
let transaction = TransactionTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(transaction.nonce(), 0);
assert_eq!(transaction.max_fee_per_gas(), 0x3b9aca00);
assert_eq!(transaction.gas_limit(), 0x1e8480);
assert_eq!(
decoded.transaction.kind(),
transaction.kind(),
TxKind::Call(0x1f9840a85d5af5bf1d1762f925bdaddc4201f984.try_into().unwrap())
);
assert_eq!(decoded.transaction.value(), 0x016345785d8a0000);
assert_eq!(decoded.transaction.input(), [0xab, 0xcd, 0xef].span());
assert_eq!(decoded.transaction.transaction_type(), TxType::Eip2930);
assert_eq!(transaction.value(), 0x016345785d8a0000);
assert_eq!(transaction.input(), [0xab, 0xcd, 0xef].span());
assert_eq!(transaction.transaction_type(), TxType::Eip2930);
}

#[test]
Expand All @@ -443,17 +423,17 @@ mod tests {
// chain id used: 'KKRT'

let mut encoded_tx_data = eip_1559_encoded_tx();
let decoded = TransactionUnsignedTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(decoded.transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(decoded.transaction.nonce(), 0);
assert_eq!(decoded.transaction.max_fee_per_gas(), 0x3b9aca00);
assert_eq!(decoded.transaction.gas_limit(), 0x1e8480);
let transaction = TransactionTrait::decode_enveloped(ref encoded_tx_data).unwrap();
assert_eq!(transaction.chain_id(), Option::Some(0x4b4b5254));
assert_eq!(transaction.nonce(), 0);
assert_eq!(transaction.max_fee_per_gas(), 0x3b9aca00);
assert_eq!(transaction.gas_limit(), 0x1e8480);
assert_eq!(
decoded.transaction.kind(),
transaction.kind(),
TxKind::Call(0x1f9840a85d5af5bf1d1762f925bdaddc4201f984.try_into().unwrap())
);
assert_eq!(decoded.transaction.value(), 0x016345785d8a0000);
assert_eq!(decoded.transaction.input(), [0xab, 0xcd, 0xef].span());
assert_eq!(transaction.value(), 0x016345785d8a0000);
assert_eq!(transaction.input(), [0xab, 0xcd, 0xef].span());
let expected_access_list = [
AccessListItem {
ethereum_address: 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984.try_into().unwrap(),
Expand All @@ -463,32 +443,30 @@ mod tests {
].span()
}
].span();
assert_eq!(
decoded.transaction.access_list().expect('access_list is none'), expected_access_list
);
assert_eq!(decoded.transaction.transaction_type(), TxType::Eip1559);
assert_eq!(transaction.access_list().expect('access_list is none'), expected_access_list);
assert_eq!(transaction.transaction_type(), TxType::Eip1559);
}

#[test]
fn test_is_legacy_tx_eip_155_tx() {
let encoded_tx_data = legacy_rlp_encoded_tx();
let result = TransactionUnsignedTrait::is_legacy_tx(encoded_tx_data);
let result = TransactionTrait::is_legacy_tx(encoded_tx_data);

assert(result, 'is_legacy_tx expected true');
}

#[test]
fn test_is_legacy_tx_eip_1559_tx() {
let encoded_tx_data = eip_1559_encoded_tx();
let result = TransactionUnsignedTrait::is_legacy_tx(encoded_tx_data);
let result = TransactionTrait::is_legacy_tx(encoded_tx_data);

assert(!result, 'is_legacy_tx expected false');
}

#[test]
fn test_is_legacy_tx_eip_2930_tx() {
let encoded_tx_data = eip_2930_encoded_tx();
let result = TransactionUnsignedTrait::is_legacy_tx(encoded_tx_data);
let result = TransactionTrait::is_legacy_tx(encoded_tx_data);

assert(!result, 'is_legacy_tx expected false');
}
Expand Down
Loading