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

Add EthAccount #853

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cd13579
feat: add eth_account main logic
ericnordelo Dec 12, 2023
19ce07d
Merge branch 'main' of github.com:OpenZeppelin/cairo-contracts into f…
ericnordelo Dec 12, 2023
463d663
feat: add tests
ericnordelo Dec 15, 2023
6ce5a08
fix: workflow
ericnordelo Dec 15, 2023
e2ee218
feat: change EthPublicKey to Secp256k1Point
ericnordelo Dec 19, 2023
3fdaf15
feat: add more tests
ericnordelo Dec 20, 2023
6cdbac9
Merge branch 'main' of github.com:OpenZeppelin/cairo-contracts into f…
ericnordelo Dec 20, 2023
16427fc
feat: add entry to CHANGELOG
ericnordelo Dec 20, 2023
135e8f1
feat: add tests for preset
ericnordelo Dec 20, 2023
f9c811a
docs: add API Reference entries
ericnordelo Dec 20, 2023
b883c6b
Update src/account/eth_account/eth_account.cairo
ericnordelo Dec 21, 2023
3c396de
feat: apply review updates
ericnordelo Dec 21, 2023
ba60238
Merge branch 'feat/migrate-eth-account-#573' of github.com:ericnordel…
ericnordelo Dec 21, 2023
34e3010
refactor: test
ericnordelo Dec 21, 2023
b40970b
feat: apply review updates
ericnordelo Dec 28, 2023
e960a4a
feat: update error messages
ericnordelo Jan 3, 2024
79c64a2
feat: apply review updates
ericnordelo Jan 5, 2024
facf8ab
Update docs/modules/ROOT/pages/api/account.adoc
ericnordelo Jan 17, 2024
ebc2e9d
feat: add tests for upgrade
ericnordelo Jan 17, 2024
1ef85b9
feat: add tests for signature
ericnordelo Jan 17, 2024
9c99ffb
Merge branch 'feat/migrate-eth-account-#573' of github.com:ericnordel…
ericnordelo Jan 17, 2024
a723d53
refactor: drop X.Y.Z in favor of last released version
ericnordelo Jan 17, 2024
f17f151
Merge branch 'main' of github.com:OpenZeppelin/cairo-contracts into f…
ericnordelo Jan 22, 2024
ef4128c
feat: updated class hashes
ericnordelo Jan 22, 2024
de0c40c
Merge branch 'main' of github.com:OpenZeppelin/cairo-contracts into f…
ericnordelo Jan 29, 2024
73144d5
Merge branch 'main' of github.com:OpenZeppelin/cairo-contracts into f…
ericnordelo Jan 29, 2024
42abd67
fix: casm target
ericnordelo Jan 29, 2024
26651b3
fix: versions
ericnordelo Jan 30, 2024
97151fd
refactor: account directory
ericnordelo Jan 30, 2024
c9d50e4
Merge branch 'main' of github.com:OpenZeppelin/cairo-contracts into f…
ericnordelo Jan 31, 2024
8a3939b
fix: comments
ericnordelo Jan 31, 2024
0f9b41c
fix: class hashes
ericnordelo Jan 31, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v3
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.3.1"
scarb-version: "2.4.0"
- name: Markdown lint
uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9
with:
Expand Down
5 changes: 3 additions & 2 deletions Scarb.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[package]
name = "openzeppelin"
version = "0.8.0"
cairo-version = "2.3.1"
edition = "2023_01"
cairo-version = "2.4.0"
authors = ["OpenZeppelin Community <[email protected]>"]
description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup"
documentation = "https://docs.openzeppelin.com/contracts-cairo"
Expand All @@ -11,7 +12,7 @@ license-file = "LICENSE"
keywords = ["openzeppelin", "starknet", "cairo", "contracts", "security", "standards"]

[dependencies]
starknet = "2.3.1"
starknet = "2.4.0"

[lib]

Expand Down
8 changes: 4 additions & 4 deletions src/account.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod account;
mod dual_account;
mod interface;
mod eth_account;
mod utils;

use account::AccountComponent;
use interface::AccountABIDispatcher;
use interface::AccountABIDispatcherTrait;
use account::dual_account;
use account::interface;
272 changes: 4 additions & 268 deletions src/account/account.cairo
Original file line number Diff line number Diff line change
@@ -1,269 +1,5 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts for Cairo v0.8.0 (account/account.cairo)
mod account;
mod dual_account;
mod interface;

/// # Account Component
///
/// The Account component enables contracts to behave as accounts.
#[starknet::component]
mod AccountComponent {
use ecdsa::check_ecdsa_signature;
use openzeppelin::account::interface;
use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait;
use openzeppelin::introspection::src5::SRC5Component;
use starknet::account::Call;
use starknet::get_caller_address;
use starknet::get_contract_address;
use starknet::get_tx_info;

const TRANSACTION_VERSION: felt252 = 1;
// 2**128 + TRANSACTION_VERSION
const QUERY_VERSION: felt252 = 0x100000000000000000000000000000001;

#[storage]
struct Storage {
Account_public_key: felt252
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
OwnerAdded: OwnerAdded,
OwnerRemoved: OwnerRemoved
}

#[derive(Drop, starknet::Event)]
struct OwnerAdded {
new_owner_guid: felt252
}

#[derive(Drop, starknet::Event)]
struct OwnerRemoved {
removed_owner_guid: felt252
}

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

#[embeddable_as(SRC6Impl)]
impl SRC6<
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of interface::ISRC6<ComponentState<TContractState>> {
/// Executes a list of calls from the account.
///
/// Requirements:
///
/// - The transaction version must be `TRANSACTION_VERSION` for actual transactions.
/// For simulations, the version must be `QUERY_VERSION`.
fn __execute__(
self: @ComponentState<TContractState>, mut calls: Array<Call>
) -> Array<Span<felt252>> {
// Avoid calls from other contracts
// https://github.com/OpenZeppelin/cairo-contracts/issues/344
let sender = get_caller_address();
assert(sender.is_zero(), Errors::INVALID_CALLER);

// Check tx version
let tx_info = get_tx_info().unbox();
let version = tx_info.version;
if version != TRANSACTION_VERSION {
assert(version == QUERY_VERSION, Errors::INVALID_TX_VERSION);
}

_execute_calls(calls)
}

/// Verifies the validity of the signature for the current transaction.
/// This function is used by the protocol to verify `invoke` transactions.
fn __validate__(self: @ComponentState<TContractState>, mut calls: Array<Call>) -> felt252 {
self.validate_transaction()
}

/// Verifies that the given signature is valid for the given hash.
fn is_valid_signature(
self: @ComponentState<TContractState>, hash: felt252, signature: Array<felt252>
) -> felt252 {
if self._is_valid_signature(hash, signature.span()) {
starknet::VALIDATED
} else {
0
}
}
}

#[embeddable_as(DeclarerImpl)]
impl Declarer<
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of interface::IDeclarer<ComponentState<TContractState>> {
/// Verifies the validity of the signature for the current transaction.
/// This function is used by the protocol to verify `declare` transactions.
fn __validate_declare__(
self: @ComponentState<TContractState>, class_hash: felt252
) -> felt252 {
self.validate_transaction()
}
}

#[embeddable_as(DeployableImpl)]
impl Deployable<
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of interface::IDeployable<ComponentState<TContractState>> {
/// Verifies the validity of the signature for the current transaction.
/// This function is used by the protocol to verify `deploy_account` transactions.
fn __validate_deploy__(
self: @ComponentState<TContractState>,
class_hash: felt252,
contract_address_salt: felt252,
public_key: felt252
) -> felt252 {
self.validate_transaction()
}
}

#[embeddable_as(PublicKeyImpl)]
impl PublicKey<
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of interface::IPublicKey<ComponentState<TContractState>> {
/// Returns the current public key of the account.
fn get_public_key(self: @ComponentState<TContractState>) -> felt252 {
self.Account_public_key.read()
}

/// Sets the public key of the account to `new_public_key`.
///
/// Requirements:
///
/// - The caller must be the contract itself.
///
/// Emits an `OwnerRemoved` event.
fn set_public_key(ref self: ComponentState<TContractState>, new_public_key: felt252) {
self.assert_only_self();
self.emit(OwnerRemoved { removed_owner_guid: self.Account_public_key.read() });
self._set_public_key(new_public_key);
}
}

/// Adds camelCase support for `ISRC6`.
#[embeddable_as(SRC6CamelOnlyImpl)]
impl SRC6CamelOnly<
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of interface::ISRC6CamelOnly<ComponentState<TContractState>> {
fn isValidSignature(
self: @ComponentState<TContractState>, hash: felt252, signature: Array<felt252>
) -> felt252 {
self.is_valid_signature(hash, signature)
}
}

/// Adds camelCase support for `PublicKeyTrait`.
#[embeddable_as(PublicKeyCamelImpl)]
impl PublicKeyCamel<
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of interface::IPublicKeyCamel<ComponentState<TContractState>> {
fn getPublicKey(self: @ComponentState<TContractState>) -> felt252 {
self.Account_public_key.read()
}

fn setPublicKey(ref self: ComponentState<TContractState>, newPublicKey: felt252) {
self.set_public_key(newPublicKey);
}
}

#[generate_trait]
impl InternalImpl<
TContractState,
+HasComponent<TContractState>,
impl SRC5: SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of InternalTrait<TContractState> {
/// Initializes the account by setting the initial public key
/// and registering the ISRC6 interface Id.
fn initializer(ref self: ComponentState<TContractState>, public_key: felt252) {
let mut src5_component = get_dep_component_mut!(ref self, SRC5);
src5_component.register_interface(interface::ISRC6_ID);
self._set_public_key(public_key);
}

/// Validates that the caller is the account itself. Otherwise it reverts.
fn assert_only_self(self: @ComponentState<TContractState>) {
let caller = get_caller_address();
let self = get_contract_address();
assert(self == caller, Errors::UNAUTHORIZED);
}

/// Validates the signature for the current transaction.
/// Returns the short string `VALID` if valid, otherwise it reverts.
fn validate_transaction(self: @ComponentState<TContractState>) -> 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
}

/// Sets the public key without validating the caller.
/// The usage of this method outside the `set_public_key` function is discouraged.
///
/// Emits an `OwnerAdded` event.
fn _set_public_key(ref self: ComponentState<TContractState>, new_public_key: felt252) {
self.Account_public_key.write(new_public_key);
self.emit(OwnerAdded { new_owner_guid: new_public_key });
}

/// Returns whether the given signature is valid for the given hash
/// using the account's current public key.
fn _is_valid_signature(
self: @ComponentState<TContractState>, hash: felt252, signature: Span<felt252>
) -> bool {
let valid_length = signature.len() == 2_u32;

if valid_length {
check_ecdsa_signature(
hash, self.Account_public_key.read(), *signature.at(0_u32), *signature.at(1_u32)
)
} else {
false
}
}
}

fn _execute_calls(mut calls: Array<Call>) -> Array<Span<felt252>> {
let mut res = ArrayTrait::new();
loop {
match calls.pop_front() {
Option::Some(call) => {
let _res = _execute_single_call(call);
res.append(_res);
},
Option::None(_) => { break (); },
};
};
res
}

fn _execute_single_call(call: Call) -> Span<felt252> {
let Call{to, selector, calldata } = call;
starknet::call_contract_syscall(to, selector, calldata.span()).unwrap()
}
}
use account::AccountComponent;
Loading