diff --git a/crates/contracts/src/account_contract.cairo b/crates/contracts/src/account_contract.cairo index 9391ac4d..5de99dd1 100644 --- a/crates/contracts/src/account_contract.cairo +++ b/crates/contracts/src/account_contract.cairo @@ -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; @@ -249,18 +249,13 @@ pub mod AccountContract { let mut encoded_tx_data = deserialize_bytes((*outside_execution.calls[0]).calldata) .expect('conversion to Span 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 diff --git a/crates/contracts/src/kakarot_core/eth_rpc.cairo b/crates/contracts/src/kakarot_core/eth_rpc.cairo index 8e98b20e..9c5ffbec 100644 --- a/crates/contracts/src/kakarot_core/eth_rpc.cairo +++ b/crates/contracts/src/kakarot_core/eth_rpc.cairo @@ -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 { @@ -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 + ref self: TContractState, mut tx_data: Span ) -> (bool, Span, u64) { - panic!("unimplemented") + let tx = TransactionTrait::decode_enveloped(ref tx_data).expect('EOA: could not decode tx'); + Self::eth_send_transaction(ref self, tx) } } diff --git a/crates/contracts/src/kakarot_core/interface.cairo b/crates/contracts/src/kakarot_core/interface.cairo index 4bce029d..826ffdf7 100644 --- a/crates/contracts/src/kakarot_core/interface.cairo +++ b/crates/contracts/src/kakarot_core/interface.cairo @@ -88,6 +88,10 @@ pub trait IExtendedKakarotCore { /// Executes an EVM transaction and possibly modifies the state fn eth_send_transaction(ref self: TContractState, tx: Transaction) -> (bool, Span, u64); + fn eth_send_raw_unsigned_tx( + ref self: TContractState, encoded_tx_data: Span + ) -> (bool, Span, u64); + // Returns the transaction count (nonce) of the specified address fn eth_get_transaction_count(self: @TContractState, address: EthAddress) -> u64; diff --git a/crates/contracts/tests/test_execution_from_outside.cairo b/crates/contracts/tests/test_execution_from_outside.cairo index aa771a6d..a6962a8b 100644 --- a/crates/contracts/tests/test_execution_from_outside.cairo +++ b/crates/contracts/tests/test_execution_from_outside.cairo @@ -249,35 +249,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')] diff --git a/crates/contracts/tests/test_kakarot_core.cairo b/crates/contracts/tests/test_kakarot_core.cairo index 90388547..2ecd3c38 100644 --- a/crates/contracts/tests/test_kakarot_core.cairo +++ b/crates/contracts/tests/test_kakarot_core.cairo @@ -314,7 +314,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()), diff --git a/crates/utils/src/eth_transaction/transaction.cairo b/crates/utils/src/eth_transaction/transaction.cairo index 9e1d57f9..959ab463 100644 --- a/crates/utils/src/eth_transaction/transaction.cairo +++ b/crates/utils/src/eth_transaction/transaction.cairo @@ -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. @@ -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, - ) -> Result { + fn decode_enveloped(ref tx_data: Span,) -> Result { if tx_data.is_empty() { return Result::Err(EthTransactionError::RLPError(RLPError::InputTooShort)); } @@ -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 - ) -> Result { + fn decode_legacy_tx(ref encoded_tx_data: Span) -> Result { let rlp_decoded_data = RLPTrait::decode(encoded_tx_data); let mut rlp_decoded_data = rlp_decoded_data.map_err()?; @@ -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. @@ -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 - ) -> Result { + ) -> Result { // keep this around so we can use it to calculate the hash let original_data = encoded_tx_data; @@ -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 @@ -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] @@ -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] @@ -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] @@ -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] @@ -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(), @@ -463,16 +443,14 @@ 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'); } @@ -480,7 +458,7 @@ mod tests { #[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'); } @@ -488,7 +466,7 @@ mod tests { #[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'); }