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

refactor: add EthRPC trait #937

Merged
merged 1 commit 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
54 changes: 5 additions & 49 deletions crates/contracts/src/account_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub mod AccountContract {
use contracts::components::ownable::ownable_component::InternalTrait;
use contracts::components::ownable::ownable_component;
use contracts::errors::KAKAROT_REENTRANCY;
use contracts::kakarot_core::eth_rpc::{IEthRPCDispatcher, IEthRPCDispatcherTrait};
use contracts::kakarot_core::interface::{IKakarotCoreDispatcher, IKakarotCoreDispatcherTrait};
use contracts::storage::StorageBytecode;
use core::cmp::min;
Expand All @@ -69,7 +70,7 @@ pub mod AccountContract {
use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
use super::OutsideExecution;
use utils::constants::{POW_2_32};
use utils::eth_transaction::transaction::{TransactionUnsignedTrait, Transaction};
use utils::eth_transaction::transaction::TransactionUnsignedTrait;
use utils::serialization::{deserialize_signature, deserialize_bytes, serialize_bytes};
use utils::traits::DefaultSignature;

Expand Down Expand Up @@ -256,7 +257,9 @@ pub mod AccountContract {
let address = self.Account_evm_address.read();
verify_eth_signature(unsigned_transaction.hash, signature, address);

let kakarot = IKakarotCoreDispatcher { contract_address: self.ownable.owner() };
let kakarot = IEthRPCDispatcher { contract_address: self.ownable.owner() };
//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);
let return_data = serialize_bytes(return_data).span();
Expand All @@ -278,51 +281,4 @@ pub mod AccountContract {
array![return_data]
}
}

#[generate_trait]
impl Eip1559TransactionImpl of Eip1559TransactionTrait {
//TODO: refactor into a generic tx validation function.
fn validate_eip1559_tx(ref self: ContractState, tx: Transaction,) -> bool {
// let kakarot = IKakarotCoreDispatcher { contract_address: self.ownable.owner() };
// let block_gas_limit = kakarot.get_block_gas_limit();

// if tx.gas_limit() >= block_gas_limit {
// return false;
// }

// let base_fee = kakarot.get_base_fee();
// let native_token = kakarot.get_native_token();
// let balance = IERC20CamelDispatcher { contract_address: native_token }
// .balanceOf(get_contract_address());

// let max_fee_per_gas = tx_fee_infos.max_fee_per_gas;
// let max_priority_fee_per_gas = tx_fee_infos.max_priority_fee_per_gas;

// // ensure that the user was willing to at least pay the base fee
// if base_fee >= max_fee_per_gas {
// return false;
// }

// // ensure that the max priority fee per gas is not greater than the max fee per gas
// if max_priority_fee_per_gas >= max_fee_per_gas {
// return false;
// }

// let max_gas_fee = tx.gas_limit() * max_fee_per_gas;
// let tx_cost = max_gas_fee.into() + tx_fee_infos.amount;

// if tx_cost >= balance {
// return false;
// }

// // priority fee is capped because the base fee is filled first
// let possible_priority_fee = max_fee_per_gas - base_fee;

// if max_priority_fee_per_gas >= possible_priority_fee {
// return false;
// }

return true;
}
}
}
1 change: 1 addition & 0 deletions crates/contracts/src/kakarot_core.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod eth_rpc;
pub mod interface;
mod kakarot;
pub use interface::{
Expand Down
212 changes: 212 additions & 0 deletions crates/contracts/src/kakarot_core/eth_rpc.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait};
use contracts::kakarot_core::interface::IKakarotCore;
use contracts::kakarot_core::kakarot::{KakarotCore, KakarotCore::{KakarotCoreState}};
use core::num::traits::Zero;
use core::starknet::get_tx_info;
use core::starknet::{EthAddress, get_caller_address};
use evm::backend::starknet_backend;
use evm::backend::validation::validate_eth_tx;
use evm::model::{TransactionResult, Address};
use evm::{EVMTrait};
use utils::eth_transaction::transaction::{TransactionTrait, Transaction};

#[starknet::interface]
pub trait IEthRPC<T> {
/// Returns the balance of the specified address.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `address` - The Ethereum address to get the balance from
///
/// # Returns
///
/// The balance of the address as a u256
fn eth_get_balance(self: @T, address: EthAddress) -> u256;

/// Returns the number of transactions sent from the specified address.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `address` - The Ethereum address to get the transaction count from
///
/// # Returns
///
/// The transaction count of the address as a u64
fn eth_get_transaction_count(self: @T, address: EthAddress) -> u64;

/// Returns the current chain ID.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Returns
///
/// The chain ID as a u64
fn eth_chain_id(self: @T) -> u64;

/// Executes a new message call immediately without creating a transaction on the block chain.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `origin` - The address the transaction is sent from
/// * `tx` - The transaction object
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The amount of gas used as a u64
fn eth_call(self: @T, origin: EthAddress, tx: Transaction) -> (bool, Span<u8>, u64);

/// Generates and returns an estimate of how much gas is necessary to allow the transaction to
/// complete.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `origin` - The address the transaction is sent from
/// * `tx` - The transaction object
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The estimated gas as a u64
fn eth_estimate_gas(self: @T, origin: EthAddress, tx: Transaction) -> (bool, Span<u8>, u64);

//TODO: make this an internal function. The account contract should call
//eth_send_raw_transaction.
/// Executes a transaction and possibly modifies the state.
///
/// # Arguments
///
/// * `tx` - The transaction object
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The amount of gas used as a u64
fn eth_send_transaction(ref self: T, tx: Transaction) -> (bool, Span<u8>, u64);

/// Executes an unsigned transaction.
///
/// This is a modified version of the eth_sendRawTransaction function.
/// Signature validation should be done before calling this function.
///
/// # Arguments
///
/// * `tx_data` - The unsigned transaction data as a Span<u8>
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The amount of gas used as a u64
fn eth_send_raw_unsigned_tx(ref self: T, tx_data: Span<u8>) -> (bool, Span<u8>, u64);
}


#[starknet::embeddable]
pub impl EthRPC<
TContractState, impl KakarotState: KakarotCoreState<TContractState>, +Drop<TContractState>
> of IEthRPC<TContractState> {
fn eth_get_balance(self: @TContractState, address: EthAddress) -> u256 {
panic!("unimplemented")
}

fn eth_get_transaction_count(self: @TContractState, address: EthAddress) -> u64 {
panic!("unimplemented")
}

fn eth_chain_id(self: @TContractState) -> u64 {
panic!("unimplemented")
}

fn eth_call(
self: @TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64) {
let mut kakarot_state = KakarotState::get_state();
if !is_view(@kakarot_state) {
core::panic_with_felt252('fn must be called, not invoked');
};

let origin = Address {
evm: origin, starknet: kakarot_state.compute_starknet_address(origin)
};

let TransactionResult { success, return_data, gas_used, state: _state } =
EVMTrait::process_transaction(
ref kakarot_state, origin, tx, tx.effective_gas_price(Option::None), 0
);

(success, return_data, gas_used)
}

fn eth_estimate_gas(
self: @TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64) {
panic!("unimplemented")
}

//TODO: make this one internal, and the eth_send_raw_unsigned_tx one public
fn eth_send_transaction(
ref self: TContractState, mut tx: Transaction
) -> (bool, Span<u8>, u64) {
let mut kakarot_state = KakarotState::get_state();
let (gas_price, intrinsic_gas) = validate_eth_tx(@kakarot_state, tx);

let starknet_caller_address = get_caller_address();
let account = IAccountDispatcher { contract_address: starknet_caller_address };
let origin = Address { evm: account.get_evm_address(), starknet: starknet_caller_address };

let TransactionResult { success, return_data, gas_used, mut state } =
EVMTrait::process_transaction(
ref kakarot_state, origin, tx, gas_price, intrinsic_gas
);
starknet_backend::commit(ref state).expect('Committing state failed');
(success, return_data, gas_used)
}

fn eth_send_raw_unsigned_tx(
ref self: TContractState, tx_data: Span<u8>
) -> (bool, Span<u8>, u64) {
panic!("unimplemented")
}
}

trait IEthRPCInternal<T> {
fn eth_send_transaction(
ref self: T, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64);
}

impl EthRPCInternalImpl<TContractState, +Drop<TContractState>> of IEthRPCInternal<TContractState> {
fn eth_send_transaction(
ref self: TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64) {
panic!("unimplemented")
}
}

fn is_view(self: @KakarotCore::ContractState) -> bool {
let tx_info = get_tx_info().unbox();

// If the account that originated the transaction is not zero, this means we
// are in an invoke transaction instead of a call; therefore, `eth_call` is being
// wrongly called For invoke transactions, `eth_send_transaction` must be used
if !tx_info.account_contract_address.is_zero() {
return false;
}
true
}
11 changes: 0 additions & 11 deletions crates/contracts/src/kakarot_core/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,6 @@ pub trait IKakarotCore<TContractState> {
ref self: TContractState, evm_address: EthAddress
) -> ContractAddress;

/// View entrypoint into the EVM
/// Performs view calls into the blockchain
/// It cannot modify the state of the chain
fn eth_call(
self: @TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64);

/// Transaction entrypoint into the EVM
/// Executes an EVM transaction and possibly modifies the state
fn eth_send_transaction(ref self: TContractState, tx: Transaction) -> (bool, Span<u8>, u64);

/// Upgrade the KakarotCore smart contract
/// Using replace_class_syscall
fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
Expand Down
Loading
Loading