Skip to content

Commit

Permalink
account function and validation
Browse files Browse the repository at this point in the history
  • Loading branch information
ermvrs committed Oct 12, 2024
1 parent 5a2808e commit 3f5bc2f
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 18 deletions.
1 change: 0 additions & 1 deletion src/accounts.cairo

This file was deleted.

109 changes: 94 additions & 15 deletions src/accounts/base.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub type EthPublicKey = starknet::secp256k1::Secp256k1Point;
#[starknet::interface]
pub trait IRosettaAccount<TState> {
fn __execute__(self: @TState, calls: Array<felt252>) -> Array<Span<felt252>>;
fn __validate__(self: @TState, calls: Array<Call>) -> felt252;
fn __validate__(self: @TState, calls: Array<felt252>) -> felt252;
fn is_valid_signature(self: @TState, hash: felt252, signature: Array<felt252>) -> felt252;
fn supports_interface(self: @TState, interface_id: felt252) -> bool;
fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252;
Expand All @@ -20,12 +20,23 @@ pub trait IRosettaAccount<TState> {
#[starknet::contract(account)]
mod RosettaAccount {
use super::EthPublicKey;
use starknet::{EthAddress, get_execution_info, get_contract_address};
use core::num::traits::Zero;
use starknet::{EthAddress, get_execution_info, get_contract_address, get_caller_address, get_tx_info};
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
use rosettacontracts::accounts::utils::{is_valid_eth_signature, Secp256k1PointStorePacking};

pub mod Errors {
pub const INVALID_CALLER: felt252 = 'Rosetta: invalid caller';
pub const INVALID_SIGNATURE: felt252 = 'Rosetta: invalid signature';
pub const INVALID_TX_VERSION: felt252 = 'Rosetta: invalid tx version';
pub const UNAUTHORIZED: felt252 = 'Rosetta: unauthorized';
}


#[storage]
struct Storage {
ethereum_address: EthAddress
ethereum_address: EthAddress,
ethereum_public_key: EthPublicKey
}

#[constructor]
Expand All @@ -35,39 +46,107 @@ mod RosettaAccount {

#[abi(embed_v0)]
impl AccountImpl of super::IRosettaAccount<ContractState> {
fn __execute__(self: @TState, calls: Array<felt252>) -> Array<Span<felt252>> {}
// Instead of Array<Call> we use Array<felt252> since we pass different values to the parameter
fn __execute__(self: @ContractState, calls: Array<felt252>) -> Array<Span<felt252>> {
let sender = get_caller_address();
assert(sender.is_zero(), Errors::INVALID_CALLER);

fn __validate__(self: @TState, calls: Array<Call>) -> felt252 {}
// TODO: Check tx version

fn is_valid_signature(self: @TState, hash: felt252, signature: Array<felt252>) -> felt252 {}
// TODO: Exec calls
}

fn supports_interface(self: @TState, interface_id: felt252) -> bool {}
fn __validate__(self: @ContractState, calls: Array<felt252>) -> felt252 {
// TODO: check if validations enough
self.validate_transaction()
}

fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252 {}
fn is_valid_signature(self: @ContractState, hash: felt252, signature: Array<felt252>) -> felt252 {
if self._is_valid_signature(hash, signature.span()) {
starknet::VALIDATED
} else {
0
}
}

fn supports_interface(self: @ContractState, interface_id: felt252) -> bool {
true
}

fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 {
// TODO: check if validations enough
self.validate_transaction()
}

fn __validate_deploy__(
self: @TState,
self: @ContractState,
class_hash: felt252,
contract_address_salt: felt252,
public_key: EthPublicKey
) -> felt252 {}
) -> felt252 {
// TODO: check if validations enough
self.validate_transaction()
}

fn get_public_key(self: @TState) -> EthPublicKey {}
fn get_public_key(self: @ContractState) -> EthPublicKey {
self.ethereum_public_key.read()
}

// We dont need that function
fn set_public_key(
ref self: TState, new_public_key: EthPublicKey, signature: Span<felt252>
ref self: ContractState, new_public_key: EthPublicKey, signature: Span<felt252>
) {}

fn isValidSignature(self: @TState, hash: felt252, signature: Array<felt252>) -> felt252 {
fn isValidSignature(self: @ContractState, hash: felt252, signature: Array<felt252>) -> felt252 {
self.is_valid_signature(hash, signature)
}

fn getPublicKey(self: @TState) -> EthPublicKey {
fn getPublicKey(self: @ContractState) -> EthPublicKey {
self.get_public_key()
}

fn setPublicKey(ref self: TState, newPublicKey: EthPublicKey, signature: Span<felt252>) {
// We dont need that function
fn setPublicKey(ref self: ContractState, newPublicKey: EthPublicKey, signature: Span<felt252>) {
self.set_public_key(newPublicKey, signature)
}
}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn initializer(ref self: ContractState, ethPubKey: EthPublicKey) {
// Write pubkey to storage
self._set_public_key(ethPubKey);
}

fn assert_only_self(self: @ContractState) {
let caller = get_caller_address();
let self = get_contract_address();
assert(self == caller, Errors::UNAUTHORIZED);
}

// Overwrites ethereum public key. We may remove that function since we only need to
// write during initialization.
fn _set_public_key(ref self: ContractState, new_public_key: EthPublicKey) {
self.ethereum_public_key.write(new_public_key);
}

/// Validates the signature for the current transaction.
/// Returns the short string `VALID` if valid, otherwise it reverts.
fn validate_transaction(self: @ContractState) -> felt252 {
let tx_info = get_tx_info().unbox();
let tx_hash = tx_info.transaction_hash;
let signature = tx_info.signature;
assert(self._is_valid_signature(tx_hash, signature), Errors::INVALID_SIGNATURE);
starknet::VALIDATED
}

/// Returns whether the given signature is valid for the given hash
/// using the account's current public key.
fn _is_valid_signature(
self: @ContractState, hash: felt252, signature: Span<felt252>
) -> bool {
let public_key: EthPublicKey = self.ethereum_public_key.read();
is_valid_eth_signature(hash, public_key, signature)
}
}
}
77 changes: 77 additions & 0 deletions src/accounts/utils.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use starknet::secp256_trait;
use rosettacontracts::accounts::base::{EthPublicKey};

#[derive(Copy, Drop, Serde)]
pub struct EthSignature {
pub r: u256,
pub s: u256,
}

pub fn is_valid_eth_signature(
msg_hash: felt252, public_key: EthPublicKey, signature: Span<felt252>
) -> bool {
let mut signature = signature;
let signature: EthSignature = Serde::deserialize(ref signature)
.expect('Signature: Invalid format.');

secp256_trait::is_valid_signature(msg_hash.into(), signature.r, signature.s, public_key)
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts for Cairo v0.17.0 (account/utils/secp256k1.cairo)

use core::fmt::{Formatter, Error};
use starknet::SyscallResultTrait;
use starknet::secp256_trait::{Secp256Trait, Secp256PointTrait};
use starknet::secp256k1::Secp256k1Point;
use starknet::storage_access::StorePacking;

/// Packs a Secp256k1Point into a (felt252, felt252).
///
/// The packing is done as follows:
/// - First felt contains x.low (x being the x-coordinate of the point).
/// - Second felt contains x.high and the parity bit, at the least significant bits (2 * x.high +
/// parity).
pub impl Secp256k1PointStorePacking of StorePacking<Secp256k1Point, (felt252, felt252)> {
fn pack(value: Secp256k1Point) -> (felt252, felt252) {
let (x, y) = value.get_coordinates().unwrap_syscall();

let parity = y % 2;
let xhigh_and_parity = 2 * x.high.into() + parity.try_into().unwrap();

(x.low.into(), xhigh_and_parity)
}

fn unpack(value: (felt252, felt252)) -> Secp256k1Point {
let (xlow, xhigh_and_parity) = value;
let xhigh_and_parity: u256 = xhigh_and_parity.into();

let x = u256 {
low: xlow.try_into().unwrap(), high: (xhigh_and_parity / 2).try_into().unwrap(),
};
let parity = xhigh_and_parity % 2 == 1;

// Expects parity odd to be true
Secp256Trait::secp256_ec_get_point_from_x_syscall(x, parity)
.unwrap_syscall()
.expect('Secp256k1Point: Invalid point.')
}
}

pub impl Secp256k1PointPartialEq of PartialEq<Secp256k1Point> {
#[inline(always)]
fn eq(lhs: @Secp256k1Point, rhs: @Secp256k1Point) -> bool {
(*lhs).get_coordinates().unwrap_syscall() == (*rhs).get_coordinates().unwrap_syscall()
}
#[inline(always)]
fn ne(lhs: @Secp256k1Point, rhs: @Secp256k1Point) -> bool {
!(lhs == rhs)
}
}

pub impl DebugSecp256k1Point of core::fmt::Debug<Secp256k1Point> {
fn fmt(self: @Secp256k1Point, ref f: Formatter) -> Result<(), Error> {
let (x, y) = (*self).get_coordinates().unwrap_syscall();
write!(f, "({x:?},{y:?})")
}
}
9 changes: 7 additions & 2 deletions src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
mod factory;
mod lens;
pub mod factory;
pub mod lens;

pub mod accounts {
pub mod base;
pub mod utils;
}

0 comments on commit 3f5bc2f

Please sign in to comment.