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

Migrate security/pausable #589

Closed
Closed
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
327 changes: 248 additions & 79 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ members = [
]

[workspace.package]
version = "1.0.0-alpha.3"
version = "1.0.0-alpha.6"
edition = "2021"
repository = "https://github.com/starkware-libs/cairo/"
license = "Apache-2.0"
Expand Down Expand Up @@ -71,8 +71,10 @@ path-clean = "0.1.0"
pretty_assertions = "1.2.1"
proc-macro2 = "1.0"
quote = "1.0.21"
rayon = "0.9.0"
rayon = "1.7.0"
rstest = "0.16.0"
salsa = "0.16.1"
scarb-metadata = "1.0.1"
serde = { version = "1.0.130", features = ["derive"] }
serde_json = "1.0"
sha3 = "0.10.6"
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ build:
cargo build

test:
cargo run --bin cairo-test -- --starknet --path src/openzeppelin
cargo run --bin cairo-test -- --starknet --path $(SOURCE_FOLDER)

format:
cargo run --bin cairo-format -- --recursive $(SOURCE_FOLDER) --print-parsing-errors
Expand Down
2 changes: 1 addition & 1 deletion cairo
Submodule cairo updated 484 files
218 changes: 218 additions & 0 deletions src/openzeppelin/account.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
use serde::Serde;
use starknet::ContractAddress;
use starknet::contract_address::ContractAddressSerde;
use array::ArrayTrait;
use array::SpanTrait;

// to do: update ID
const ACCOUNT_ID: felt252 = 0x4;

struct Call {
to: ContractAddress,
selector: felt252,
calldata: Array<felt252>
}

#[account_contract]
mod Account {
use array::SpanTrait;
use array::ArrayTrait;
use ecdsa::check_ecdsa_signature;
use starknet::contract_address::ContractAddressPartialEq;
use starknet::get_caller_address;
use starknet::get_contract_address;
use starknet::get_tx_info;

use option::OptionTrait;
use super::Call;
use super::ArrayCallSerde;
use super::ArrayCallDrop;
use super::ACCOUNT_ID;

use openzeppelin::introspection::erc165::ERC165Contract;

//
// Storage and Constructor
//

struct Storage {
public_key: felt252,
}

#[constructor]
fn constructor(_public_key: felt252) {
ERC165Contract::register_interface(ACCOUNT_ID);
public_key::write(_public_key);
}

//
// Externals
//

#[external]
fn __execute__(mut calls: Array<Call>) -> Array<Array<felt252>> {
assert_valid_transaction();
let mut res = ArrayTrait::new();
_execute_calls(calls, res)
}

#[external]
fn __validate__(mut calls: Array<Call>) {
assert_valid_transaction()
}

#[external]
fn __validate_declare__(class_hash: felt252) {
assert_valid_transaction()
}

#[external]
fn __validate_deploy__(
class_hash: felt252,
contract_address_salt: felt252,
_public_key: felt252
) {
assert_valid_transaction()
}

#[external]
fn set_public_key(new_public_key: felt252) {
assert_only_self();
public_key::write(new_public_key);
}

//
// View
//

#[view]
fn get_public_key() -> felt252 {
public_key::read()
}

#[view]
fn is_valid_signature(message: felt252, sig_r: felt252, sig_s: felt252) -> bool {
let _public_key: felt252 = public_key::read();
check_ecdsa_signature(message, _public_key, sig_r, sig_s)
// to do:
// return magic value or false
}

#[view]
fn supports_interface(interface_id: felt252) -> bool {
ERC165Contract::supports_interface(interface_id)
}

//
// Internals
//

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

fn _execute_single_call(mut call: Call) -> Array<felt252> {
let Call{to, selector, calldata } = call;
starknet::call_contract_syscall(to, selector, calldata).unwrap_syscall()
}

fn assert_only_self() {
let caller = get_caller_address();
let self = get_contract_address();
assert(self == caller, 'Account: unauthorized.');
}

fn assert_valid_transaction() {
let tx_info = unbox(get_tx_info());
let tx_hash = tx_info.transaction_hash;
let signature = tx_info.signature;

assert(signature.len() == 2_u32, 'bad signature length');

let is_valid = is_valid_signature(
tx_hash,
*signature.at(0_u32),
*signature.at(1_u32)
);

assert(is_valid, 'Invalid signature.');
}
}

impl ArrayCallDrop of Drop::<Array<Call>>;

impl CallSerde of Serde::<Call> {
fn serialize(ref output: Array<felt252>, input: Call) {
let Call{to, selector, calldata } = input;
Serde::serialize(ref output, to);
Serde::serialize(ref output, selector);
Serde::serialize(ref output, calldata);
}

fn deserialize(ref serialized: Span<felt252>) -> Option<Call> {
let to = Serde::<ContractAddress>::deserialize(ref serialized)?;
let selector = Serde::<felt252>::deserialize(ref serialized)?;
let calldata = Serde::<Array::<felt252>>::deserialize(ref serialized)?;
Option::Some(Call { to, selector, calldata })
}
}

impl ArrayCallSerde of Serde::<Array<Call>> {
fn serialize(ref output: Array<felt252>, mut input: Array<Call>) {
Serde::<usize>::serialize(ref output, input.len());
serialize_array_call_helper(ref output, input);
}

fn deserialize(ref serialized: Span<felt252>) -> Option<Array<Call>> {
let length = *serialized.pop_front()?;
let mut arr = ArrayTrait::new();
deserialize_array_call_helper(ref serialized, arr, length)
}
}

fn serialize_array_call_helper(ref output: Array<felt252>, mut input: Array<Call>) {
match gas::get_gas() {
Option::Some(_) => {},
Option::None(_) => {
let mut data = ArrayTrait::new();
data.append('Out of gas');
panic(data);
},
}
match input.pop_front() {
Option::Some(value) => {
Serde::<Call>::serialize(ref output, value);
serialize_array_call_helper(ref output, input);
},
Option::None(_) => {},
}
}

fn deserialize_array_call_helper(
ref serialized: Span<felt252>, mut curr_output: Array<Call>, remaining: felt252
) -> Option<Array<Call>> {
if remaining == 0 {
return Option::Some(curr_output);
}

match gas::get_gas() {
Option::Some(_) => {},
Option::None(_) => {
let mut data = ArrayTrait::new();
data.append('Out of gas');
panic(data);
},
}

curr_output.append(Serde::<Call>::deserialize(ref serialized)?);
deserialize_array_call_helper(ref serialized, curr_output, remaining - 1)
}
1 change: 1 addition & 0 deletions src/openzeppelin/introspection.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod erc165;
40 changes: 40 additions & 0 deletions src/openzeppelin/introspection/erc165.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const IERC165_ID: felt252 = 0x01ffc9a7;
const INVALID_ID: felt252 = 0xffffffff;

trait IERC165 {
fn supports_interface(interface_id: felt252) -> bool;
fn register_interface(interface_id: felt252);
}

#[contract]
mod ERC165Contract {
use openzeppelin::introspection::erc165;

struct Storage {
supported_interfaces: LegacyMap::<felt252, bool>,
}

impl ERC165 of erc165::IERC165 {
fn supports_interface(interface_id: felt252) -> bool {
if interface_id == erc165::IERC165_ID {
return true;
}
supported_interfaces::read(interface_id)
}

fn register_interface(interface_id: felt252) {
assert(interface_id != erc165::INVALID_ID, 'Invalid id');
supported_interfaces::write(interface_id, true);
}
}

#[view]
fn supports_interface(interface_id: felt252) -> bool {
ERC165::supports_interface(interface_id)
}

#[external]
fn register_interface(interface_id: felt252) {
ERC165::register_interface(interface_id)
}
}
4 changes: 4 additions & 0 deletions src/openzeppelin/lib.cairo
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
mod introspection;
mod account;
mod security;
mod mocks;
mod tests;
1 change: 1 addition & 0 deletions src/openzeppelin/mocks.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod mock_pausable;
37 changes: 37 additions & 0 deletions src/openzeppelin/mocks/mock_pausable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[contract]
mod MockPausable {
use openzeppelin::security::pausable::Pausable;

use starknet::ContractAddress;
use starknet::get_caller_address;

struct Storage {
_counter: felt252
}

#[view]
fn is_paused() -> bool {
Pausable::is_paused()
}

#[view]
fn get_count() -> felt252 {
_counter::read()
}

#[external]
fn assert_unpaused_and_increment() {
Pausable::assert_not_paused();
_counter::write(_counter::read() + 1);
}

#[external]
fn pause() {
Pausable::pause();
}

#[external]
fn unpause() {
Pausable::unpause();
}
}
1 change: 1 addition & 0 deletions src/openzeppelin/security.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod pausable;
39 changes: 39 additions & 0 deletions src/openzeppelin/security/pausable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#[contract]
mod Pausable {
use starknet::ContractAddress;
use starknet::get_caller_address;

struct Storage {
_paused: bool,
}

#[event]
fn Paused(account: ContractAddress) {}

#[event]
fn Unpaused(account: ContractAddress) {}

fn is_paused() -> bool {
_paused::read()
}

fn assert_not_paused() {
assert(!is_paused(), 'Pausable: paused');
}

fn assert_paused() {
assert(is_paused(), 'Pausable: not paused');
}

fn pause() {
assert_not_paused();
_paused::write(true);
Paused(get_caller_address());
}

fn unpause() {
assert_paused();
_paused::write(false);
Unpaused(get_caller_address());
}
}
4 changes: 3 additions & 1 deletion src/openzeppelin/tests.cairo
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
mod test_mytest;
mod test_erc165;
mod test_account;
mod test_pausable;
Loading