Skip to content

Commit

Permalink
Merge pull request #22 from ermvrs/main
Browse files Browse the repository at this point in the history
execution
  • Loading branch information
ermvrs authored Dec 3, 2024
2 parents cb90352 + 55365f8 commit ce56e84
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 48 deletions.
56 changes: 34 additions & 22 deletions src/accounts/base.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use starknet::{EthAddress};
use starknet::{EthAddress, ContractAddress};
use rosettacontracts::accounts::utils::{RosettanetCall};


Expand All @@ -10,7 +10,8 @@ pub trait IRosettaAccount<TState> {
fn supports_interface(self: @TState, interface_id: felt252) -> bool;
fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252;
fn __validate_deploy__(
self: @TState, class_hash: felt252, contract_address_salt: felt252, eth_address: EthAddress
self: @TState, class_hash: felt252, contract_address_salt: felt252, eth_address: EthAddress,
registry: ContractAddress
) -> felt252;
fn get_ethereum_address(self: @TState) -> EthAddress;
// Camel case
Expand All @@ -22,11 +23,13 @@ pub trait IRosettaAccount<TState> {
pub mod RosettaAccount {
use core::num::traits::Zero;
use starknet::{
EthAddress, get_contract_address, get_caller_address, get_tx_info
ContractAddress, EthAddress, get_contract_address, get_caller_address, get_tx_info
};
use starknet::syscalls::{call_contract_syscall};
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
use rosettacontracts::accounts::utils::{is_valid_eth_signature, parse_transaction, RosettanetSignature, RosettanetCall, validate_target_function};
use rosettacontracts::accounts::encoding::{rlp_encode_eip1559, calculate_tx_hash};
use rosettacontracts::accounts::utils::{is_valid_eth_signature, RosettanetSignature, RosettanetCall, validate_target_function, generate_tx_hash};
use crate::rosettanet::{IRosettanetDispatcher, IRosettanetDispatcherTrait};
use openzeppelin::utils::deployments::{calculate_contract_address_from_deploy_syscall};

pub mod Errors {
pub const INVALID_CALLER: felt252 = 'Rosetta: invalid caller';
Expand All @@ -38,12 +41,14 @@ pub mod RosettaAccount {
#[storage]
struct Storage {
ethereum_address: EthAddress,
nonce: u64
nonce: u64,
registry: ContractAddress
}

#[constructor]
fn constructor(ref self: ContractState, eth_address: EthAddress) {
fn constructor(ref self: ContractState, eth_address: EthAddress, registry: ContractAddress) {
self.ethereum_address.write(eth_address);
self.registry.write(registry);
// TODO: verify on deploy that address is correct
}
// TODO: Raw transaction tx.signature da, __execute__ parametresindede bit locationlar mı olacak??
Expand All @@ -56,20 +61,19 @@ pub mod RosettaAccount {
fn __execute__(self: @ContractState, call: RosettanetCall) -> Array<Span<felt252>> {
let sender = get_caller_address();
assert(sender.is_zero(), Errors::INVALID_CALLER);
// TODO: Check tx version

// TODO: Exec call
let eth_target: EthAddress = call.to;
let sn_target: ContractAddress = IRosettanetDispatcher { contract_address: self.registry.read() }.get_starknet_address(eth_target);
assert(sn_target != starknet::contract_address_const::<0>(), 'target not registered');

// We don't use Call type
// Instead we pass raw transaction properties in each different felt. And v,r,s on signature
// So we verify that transaction is signed by correct address from generating
// Transaction again.
// There is no need to use Call struct here because all calldata will be passed as array of felts.
let entrypoint = validate_target_function(call.target_function, call.calldata);
// If its native transfer do not handle calldata
let mut calldata = call.calldata;
calldata.pop_front(); // Remove first element, it is function selector

// 1) Check if array length is higher than minimum
// Order: ChainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gas, to, value, data (Array)

array![array!['todo'].span()]
let result: Span<felt252> = call_contract_syscall(sn_target, entrypoint, calldata).unwrap();
// self.nonce.write(self.nonce.read() + 1); // Problem here ???
array![result]
}

fn __validate__(self: @ContractState, call: RosettanetCall) -> felt252 {
Expand Down Expand Up @@ -102,10 +106,19 @@ pub mod RosettaAccount {
self: @ContractState,
class_hash: felt252,
contract_address_salt: felt252,
eth_address: EthAddress
eth_address: EthAddress,
registry: ContractAddress
) -> felt252 {
// TODO validate deploy
assert(contract_address_salt == eth_address.into(), 'Salt and param mismatch');
assert(registry != starknet::contract_address_const::<0>(), 'registry zero');
let address = calculate_contract_address_from_deploy_syscall(
eth_address.into(),
class_hash.try_into().unwrap(),
array![eth_address.into(), registry.into()].span(),
0.try_into().unwrap()
);

assert(address == get_contract_address(), 'deployed address wrong');
starknet::VALIDATED
}

Expand Down Expand Up @@ -141,8 +154,7 @@ pub mod RosettaAccount {
let _ = validate_target_function(call.target_function, call.calldata);

// Validate transaction signature
let parsed_txn = parse_transaction(call); // TODO burda eksikler var
let expected_hash = calculate_tx_hash(rlp_encode_eip1559(parsed_txn));
let expected_hash = generate_tx_hash(call);

let signature = tx_info.signature; // Signature includes v,r,s
assert(self._is_valid_signature(expected_hash, signature), Errors::INVALID_SIGNATURE);
Expand Down
2 changes: 1 addition & 1 deletion src/accounts/encoding.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub fn rlp_encode_eip1559(tx: Eip1559Transaction) -> Span<u8> {
let max_fee_per_gas = RLPItem::String(deserialize_bytes_non_zeroes(tx.max_fee_per_gas.into(), 16));
let gas_limit = RLPItem::String(deserialize_bytes_non_zeroes(tx.gas_limit.into(), 8));
let to = RLPItem::String(deserialize_bytes(tx.to.into(), 20));
let value = RLPItem::String(deserialize_bytes_non_zeroes(tx.value.try_into().unwrap(), 32));
let value = RLPItem::String(deserialize_bytes_non_zeroes(tx.value.try_into().unwrap(), 32)); // this may revert 32 length is higher
let input = RLPItem::String(tx.input);

let mut access_arr = array![];
Expand Down
104 changes: 84 additions & 20 deletions src/accounts/utils.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use starknet::secp256_trait::{Signature, signature_from_vrs};
use starknet::{EthAddress};
use crate::accounts::encoding::{Eip1559Transaction, deserialize_bytes, deserialize_u256_span, bytes_from_felts};
use crate::accounts::encoding::{Eip1559Transaction, deserialize_bytes, deserialize_u256_span, bytes_from_felts, calculate_tx_hash, rlp_encode_eip1559};
use crate::utils::constants::{POW_2_250};
use crate::utils::traits::SpanDefault;
use crate::utils::bytes::{U8SpanExTrait, ByteArrayExTrait};
Expand Down Expand Up @@ -29,33 +29,66 @@ pub struct RosettanetCall {
pub max_fee_per_gas: u128,
pub gas_limit: u64,
pub value: u256, // To be used future
pub calldata: Span<felt252>,
pub calldata: Span<felt252>, // Calldata len must be +1 directive len
pub directives: Span<bool>, // We use this directives to figure out u256 splitting happened in element in same index For ex if 3rd element of this array is true, it means 3rd elem is low, 4th elem is high of u256
pub target_function: Span<felt252> // Function name and types to used to calculate eth func signature
}

pub fn generate_tx_hash(call: RosettanetCall) -> u256 {
let parsed_txn = parse_transaction(call);
calculate_tx_hash(rlp_encode_eip1559(parsed_txn))
}

pub fn parse_transaction(call: RosettanetCall) -> Eip1559Transaction {
let calldata = call.calldata;
let mut calldata = call.calldata;
let directives = call.directives;

// TODO: deserialize yapmadan once calldatadaki function signature u cikart
let mut merged_calldata = merge_u256s(calldata, directives); // Bunun içinde test yaz
let deserialized_calldata = deserialize_u256_span(ref merged_calldata); // TODO: check is correct Loop ile byte ayırmalı
// TODO: u256 span decodesi içi test yaz

let eip1559 = Eip1559Transaction {
chain_id: CHAIN_ID,
nonce: call.nonce,
max_priority_fee_per_gas: call.max_priority_fee_per_gas,
max_fee_per_gas: call.max_fee_per_gas,
gas_limit: call.gas_limit,
to: call.to,
value: call.value,
access_list: array![].span(),
input: deserialized_calldata
let function_signature: felt252 = match calldata.pop_front() {
Option::None => { 0 }, // We may remove that panic or change the logic, since native eth transfer has empty calldata
Option::Some(val) => { *val }
};

eip1559
if(function_signature != 0) {
let function_signature_bytes: Span<u8> = deserialize_bytes(function_signature, 4); // Four bytes is eth signature
let mut merged_calldata = merge_u256s(calldata, directives); // Bunun içinde test yaz
let mut deserialized_calldata = deserialize_u256_span(ref merged_calldata);

// Merge function signature bytes and deserialized calldata

let mut ba: core::byte_array::ByteArray = Default::default();
ba.append(@ByteArrayExTrait::from_bytes(function_signature_bytes));
ba.append(@ByteArrayExTrait::from_bytes(deserialized_calldata));

let calldata_bytes: Span<u8> = ba.into_bytes();
let eip1559 = Eip1559Transaction {
chain_id: CHAIN_ID,
nonce: call.nonce,
max_priority_fee_per_gas: call.max_priority_fee_per_gas,
max_fee_per_gas: call.max_fee_per_gas,
gas_limit: call.gas_limit,
to: call.to,
value: call.value,
access_list: array![].span(), // Do we need these?
input: calldata_bytes
};

eip1559
} else {
let eip1559 = Eip1559Transaction {
chain_id: CHAIN_ID,
nonce: call.nonce,
max_priority_fee_per_gas: call.max_priority_fee_per_gas,
max_fee_per_gas: call.max_fee_per_gas,
gas_limit: call.gas_limit,
to: call.to,
value: call.value,
access_list: array![].span(),
input: array![0x0].span() // Zero or empty?
};

eip1559
}

}

// Merges u256s coming from calldata according to directives
Expand Down Expand Up @@ -168,10 +201,41 @@ pub fn calculate_eth_function_signature(func: Span<u8>) -> Span<u8> {

#[cfg(test)]
mod tests {
use crate::accounts::utils::{merge_u256s, calculate_eth_function_signature, parse_function_name, eth_function_signature_from_felts, calculate_sn_entrypoint, validate_target_function};
use crate::accounts::utils::{merge_u256s, calculate_eth_function_signature, parse_function_name, eth_function_signature_from_felts, calculate_sn_entrypoint, validate_target_function, parse_transaction, RosettanetCall};
use crate::accounts::encoding::{bytes_from_felts};
use crate::utils::bytes::{ByteArrayExTrait};


#[test]
fn test_parse_transaction_usual() {
let calldata = array![0x23b872dd, 0x123123, 0x456456, 0x0, 0x666].span(); // transferFrom(0x123123,0x456456, u256 {0,0x666})
let directives = array![false, false, true, false].span(); // Directive length must be -1 bcs first is selector
let target_function = array![0x7472616E7366657246726F6D28616464726573732C616464726573732C, 0x75696E7432353629].span(); // transferFrom

let call = RosettanetCall {
to: 1.try_into().unwrap(),
nonce: 1,
max_priority_fee_per_gas: 24000,
max_fee_per_gas: 12000,
gas_limit: 21000,
value: 0,
calldata: calldata,
directives: directives,
target_function: target_function
};

let parsed_txn = parse_transaction(call);

assert_eq!(parsed_txn.chain_id, 2933);
assert_eq!(parsed_txn.nonce, 1);
assert_eq!(parsed_txn.input.len(), 100);
assert_eq!(*parsed_txn.input.at(0), 0x23);
assert_eq!(*parsed_txn.input.at(1), 0xb8);
assert_eq!(*parsed_txn.input.at(2), 0x72);
assert_eq!(*parsed_txn.input.at(3), 0xdd);
assert_eq!(*parsed_txn.input.at(4), 0x0);
}

#[test]
fn test_validate_target_function_correct() {
let calldata = array![0xa9059cbb].span(); // transfer(address,uint256)
Expand Down
10 changes: 5 additions & 5 deletions src/rosettanet.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub mod Rosettanet {
use starknet::storage::{Map};
use core::poseidon::{poseidon_hash_span};
use starknet::syscalls::{deploy_syscall};
use starknet::{ContractAddress, EthAddress, ClassHash};
use starknet::{ContractAddress, EthAddress, ClassHash, get_contract_address};
use openzeppelin::utils::deployments::{calculate_contract_address_from_deploy_syscall};

#[storage]
Expand All @@ -43,8 +43,8 @@ pub mod Rosettanet {
let eth_address_felt: felt252 = eth_address.into();

let (account, _) = deploy_syscall(
self.account_class.read(), eth_address_felt, array![eth_address_felt].span(), true
)
self.account_class.read(), eth_address_felt, array![eth_address_felt, get_contract_address().into()].span(), true
) // TODO Update contstructor params
.unwrap();

self.update_registry(account, eth_address);
Expand All @@ -65,9 +65,9 @@ pub mod Rosettanet {
calculate_contract_address_from_deploy_syscall(
eth_address_felt,
self.account_class.read(),
array![eth_address_felt].span(),
array![eth_address_felt, get_contract_address().into()].span(),
0.try_into().unwrap()
)
)// TODO Update contstructor params
}

fn account_class(self: @ContractState) -> ClassHash {
Expand Down

0 comments on commit ce56e84

Please sign in to comment.