Skip to content

Commit

Permalink
Merge pull request #1 from ermvrs/main
Browse files Browse the repository at this point in the history
Init validations
  • Loading branch information
ermvrs authored Oct 12, 2024
2 parents 0c64dc8 + d93b918 commit 353bfa9
Show file tree
Hide file tree
Showing 10 changed files with 363 additions and 26 deletions.
Empty file.
131 changes: 131 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,137 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "openzeppelin"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:7e77855aaba0825a2a12cad72d52d85380a9fab732007754b3c5d98908918ce7"
dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_finance",
"openzeppelin_governance",
"openzeppelin_introspection",
"openzeppelin_merkle_tree",
"openzeppelin_presets",
"openzeppelin_security",
"openzeppelin_token",
"openzeppelin_upgrades",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_access"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:541bb8fdf1ad17fe0d275b00acb9f0d7f56ea5534741e21535ac3fda2c600281"
dependencies = [
"openzeppelin_introspection",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_account"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:c4e11609fdd1f4c3d3004cd1468711bd2ea664739c9e59a4b270567fe4c23ee3"
dependencies = [
"openzeppelin_introspection",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_finance"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:9adcbec76ee8ed08be8d87c6af6014aa7080d67578816f5ba77f4376b25bc165"
dependencies = [
"openzeppelin_access",
"openzeppelin_token",
]

[[package]]
name = "openzeppelin_governance"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:b7e0142d88d69a8c367aea8c9dc7f659f27372551efc23f39a0cf71a189c1302"
dependencies = [
"openzeppelin_access",
"openzeppelin_introspection",
]

[[package]]
name = "openzeppelin_introspection"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:892433a4a1ea0fc9cf7cdb01e06ddc2782182abcc188e4ea5dd480906d006cf8"

[[package]]
name = "openzeppelin_merkle_tree"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:3c338fa07cbaba8034051a42967816800abe535ef7d709a929175616603dccf9"

[[package]]
name = "openzeppelin_presets"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:0a39e0effff133ab7fb003961ee2986438ee09b53608ce0d71aca24459879597"
dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_finance",
"openzeppelin_introspection",
"openzeppelin_token",
"openzeppelin_upgrades",
]

[[package]]
name = "openzeppelin_security"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:6e2dee39d87f9ddec2ad37e33e80cf0d8b6c6927fd7950f220dbc2baea658d43"

[[package]]
name = "openzeppelin_token"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:77997a7e217b69674c34b402dc0c7b2210540db66a56087572679c31896eaabb"
dependencies = [
"openzeppelin_account",
"openzeppelin_governance",
"openzeppelin_introspection",
]

[[package]]
name = "openzeppelin_upgrades"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:a0fa5934f2924e1e85ec8f8c5b7dcd95c25295c029d3a745ba87b3191146004d"

[[package]]
name = "openzeppelin_utils"
version = "0.17.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:36d93e353f42fd6b824abcd8b4b51c3f5d02c893c5f886ae81403b0368aa5fde"

[[package]]
name = "rosettacontracts"
version = "0.1.0"
dependencies = [
"openzeppelin",
"snforge_std",
]

[[package]]
name = "snforge_scarb_plugin"
version = "0.31.0"
source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67"

[[package]]
name = "snforge_std"
version = "0.31.0"
source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67"
dependencies = [
"snforge_scarb_plugin",
]
5 changes: 4 additions & 1 deletion Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ sierra = true


[dependencies]
starknet = "2.6.4"
starknet = "2.8.2"
openzeppelin = "0.17.0"

[dev-dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" }
121 changes: 105 additions & 16 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,52 +20,141 @@ 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]
fn constructor(ref self: ContractState) {}
fn constructor(ref self: ContractState, eth_account: EthAddress) {
self.ethereum_address.write(eth_account);
}

#[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);
// TODO: Check tx version

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

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

fn supports_interface(self: @TState, interface_id: felt252) -> bool {}
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 __validate_declare__(self: @TState, class_hash: felt252) -> felt252 {}
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:?})")
}
}
Loading

0 comments on commit 353bfa9

Please sign in to comment.