Skip to content

Commit

Permalink
Merge branch 'develop' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
juniset committed Jun 24, 2022
2 parents 7225b83 + 5ab1518 commit 5373e0d
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 83 deletions.
Binary file added audit/NM-0052-FINAL-REPORT.pdf
Binary file not shown.
92 changes: 64 additions & 28 deletions contracts/ArgentAccount.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,21 @@

from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin
from starkware.cairo.common.signature import verify_ecdsa_signature
from starkware.cairo.common.registers import get_fp_and_pc
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.memcpy import memcpy
from starkware.cairo.common.math import assert_not_zero, assert_le, assert_nn
from starkware.starknet.common.syscalls import (
call_contract, get_tx_info, get_contract_address, get_caller_address, get_block_timestamp
)
from starkware.cairo.common.hash_state import (
hash_init, hash_finalize, hash_update, hash_update_single
library_call, call_contract, get_tx_info, get_contract_address, get_caller_address, get_block_timestamp
)
from starkware.cairo.common.bool import (TRUE, FALSE)

from contracts.Upgradable import _set_implementation

@contract_interface
namespace IAccount:
func supportsInterface(interfaceId: felt) -> (success : felt):
end
end

####################
# CONSTANTS
####################

const VERSION = '0.2.1'
const VERSION = '0.2.2'

const CHANGE_SIGNER_SELECTOR = 1540130945889430637313403138889853410180247761946478946165786566748520529557
const CHANGE_GUARDIAN_SELECTOR = 1374386526556551464817815908276843861478960435557596145330240747921847320237
Expand All @@ -34,17 +25,15 @@ const TRIGGER_ESCAPE_SIGNER_SELECTOR = 65189126576298695489877423686052356045715
const ESCAPE_GUARDIAN_SELECTOR = 1662889347576632967292303062205906116436469425870979472602094601074614456040
const ESCAPE_SIGNER_SELECTOR = 578307412324655990419134484880427622068887477430675222732446709420063579565
const CANCEL_ESCAPE_SELECTOR = 992575500541331354489361836180456905167517944319528538469723604173440834912
const SUPPORTS_INTERFACE_SELECTOR = 1184015894760294494673613438913361435336722154500302038630992932234692784845

const ESCAPE_SECURITY_PERIOD = 7*24*60*60 # set to e.g. 7 days in prod

const ESCAPE_TYPE_GUARDIAN = 0
const ESCAPE_TYPE_SIGNER = 1
const ESCAPE_TYPE_GUARDIAN = 1
const ESCAPE_TYPE_SIGNER = 2

const ERC165_ACCOUNT_INTERFACE = 0xf10dbd44

const TRUE = 1
const FALSE = 0

####################
# STRUCTS
####################
Expand Down Expand Up @@ -106,6 +95,10 @@ end
func signer_escaped(new_signer: felt):
end

@event
func account_created(account: felt, key: felt, guardian: felt):
end

@event
func account_upgraded(new_implementation: felt):
end
Expand Down Expand Up @@ -163,6 +156,9 @@ func initialize{
# initialize the contract
_signer.write(signer)
_guardian.write(guardian)
# emit event
let (self) = get_contract_address()
account_created.emit(account=self, key=signer, guardian=guardian)
return ()
end

Expand All @@ -185,6 +181,11 @@ func __execute__{
):
alloc_locals

# make sure the account is initialized
assert_initialized()
# no reentrant call to prevent signature reutilization
assert_non_reentrant()

############### TMP #############################
# parse inputs to an array of 'Call' struct
let (calls : Call*) = alloc()
Expand Down Expand Up @@ -246,8 +247,15 @@ func upgrade{
assert_only_self()
# make sure the target is an account
with_attr error_message("implementation invalid"):
let (success) = IAccount.supportsInterface(contract_address=implementation, interfaceId=ERC165_ACCOUNT_INTERFACE)
assert success = TRUE
let (calldata: felt*) = alloc()
assert calldata[0] = ERC165_ACCOUNT_INTERFACE
let (retdata_size: felt, retdata: felt*) = library_call(
class_hash=implementation,
function_selector=SUPPORTS_INTERFACE_SELECTOR,
calldata_size=1,
calldata=calldata)
assert retdata_size = 1
assert [retdata] = TRUE
end
# change implementation
_set_implementation(implementation)
Expand Down Expand Up @@ -415,6 +423,7 @@ func escape_guardian{
let (block_timestamp) = get_block_timestamp()
with_attr error_message("escape is not valid"):
# assert there is an active escape
assert_not_zero(current_escape.active_at)
assert_le(current_escape.active_at, block_timestamp)
# assert the escape was triggered by the signer
assert current_escape.type = ESCAPE_TYPE_GUARDIAN
Expand Down Expand Up @@ -451,6 +460,7 @@ func escape_signer{
let (block_timestamp) = get_block_timestamp()
with_attr error_message("escape is not valid"):
# validate there is an active escape
assert_not_zero(current_escape.active_at)
assert_le(current_escape.active_at, block_timestamp)
# assert the escape was triggered by the guardian
assert current_escape.type = ESCAPE_TYPE_SIGNER
Expand Down Expand Up @@ -482,10 +492,14 @@ func is_valid_signature{
hash: felt,
sig_len: felt,
sig: felt*
) -> ():
validate_signer_signature(hash, sig, sig_len)
validate_guardian_signature(hash, sig + 2, sig_len - 2)
return ()
) -> (is_valid: felt):
alloc_locals

let (is_signer_sig_valid) = validate_signer_signature(hash, sig, sig_len)
let (is_guardian_sig_valid) = validate_guardian_signature(hash, sig + 2, sig_len - 2)

# Cairo's way of doing `&&` is by multiplying the two booleans.
return (is_valid=is_signer_sig_valid * is_guardian_sig_valid)
end

@view
Expand Down Expand Up @@ -578,6 +592,28 @@ func assert_only_self{
return()
end

func assert_initialized{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
range_check_ptr
} ():
let (signer) = _signer.read()
with_attr error_message("account not initialized"):
assert_not_zero(signer)
end
return()
end

func assert_non_reentrant{
syscall_ptr: felt*
} () -> ():
let (caller) = get_caller_address()
with_attr error_message("no reentrant call"):
assert caller = 0
end
return()
end

func assert_guardian_set{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
Expand Down Expand Up @@ -627,7 +663,7 @@ func validate_signer_signature{
message: felt,
signatures: felt*,
signatures_len: felt
) -> ():
) -> (is_valid: felt):
with_attr error_message("signer signature invalid"):
assert_nn(signatures_len - 2)
let (signer) = _signer.read()
Expand All @@ -637,7 +673,7 @@ func validate_signer_signature{
signature_r=signatures[0],
signature_s=signatures[1])
end
return()
return(is_valid=TRUE)
end

func validate_guardian_signature{
Expand All @@ -649,11 +685,11 @@ func validate_guardian_signature{
message: felt,
signatures: felt*,
signatures_len: felt
) -> ():
) -> (is_valid: felt):
alloc_locals
let (guardian) = _guardian.read()
if guardian == 0:
return()
return(is_valid=TRUE)
else:
with_attr error_message("guardian signature invalid"):
if signatures_len == 2:
Expand Down Expand Up @@ -681,7 +717,7 @@ func validate_guardian_signature{
tempvar pedersen_ptr: HashBuiltin* = pedersen_ptr
end
end
return()
return(is_valid=TRUE)
end
end

Expand Down
21 changes: 14 additions & 7 deletions contracts/Proxy.cairo
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
%lang starknet

from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.math import assert_not_zero
from starkware.starknet.common.syscalls import delegate_call, delegate_l1_handler
from starkware.starknet.common.syscalls import library_call, library_call_l1_handler

from contracts.Upgradable import _get_implementation, _set_implementation

Expand All @@ -16,9 +15,17 @@ func constructor{
pedersen_ptr: HashBuiltin*,
range_check_ptr
} (
implementation: felt
implementation: felt,
selector: felt,
calldata_len: felt,
calldata: felt*
):
_set_implementation(implementation)
library_call(
class_hash=implementation,
function_selector=selector,
calldata_size=calldata_len,
calldata=calldata)
return ()
end

Expand All @@ -43,8 +50,8 @@ func __default__{
):
let (implementation) = _get_implementation()

let (retdata_size : felt, retdata : felt*) = delegate_call(
contract_address=implementation,
let (retdata_size : felt, retdata : felt*) = library_call(
class_hash=implementation,
function_selector=selector,
calldata_size=calldata_size,
calldata=calldata)
Expand All @@ -64,8 +71,8 @@ func __l1_default__{
):
let (implementation) = _get_implementation()

delegate_l1_handler(
contract_address=implementation,
library_call_l1_handler(
class_hash=implementation,
function_selector=selector,
calldata_size=calldata_size,
calldata=calldata)
Expand Down
1 change: 0 additions & 1 deletion contracts/Upgradable.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.math import assert_not_zero
from starkware.starknet.common.syscalls import delegate_call, delegate_l1_handler

####################
# STORAGE VARIABLES
Expand Down
16 changes: 6 additions & 10 deletions contracts/lib/ERC20.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ from starkware.cairo.common.math import assert_not_zero
from starkware.cairo.common.uint256 import (
Uint256, uint256_add, uint256_sub, uint256_le, uint256_lt, uint256_check
)
from starkware.cairo.common.bool import (TRUE, FALSE)

#
# Storage
Expand Down Expand Up @@ -135,8 +136,7 @@ func transfer{
let (sender) = get_caller_address()
_transfer(sender, recipient, amount)

# Cairo equivalent to 'return (true)'
return (1)
return (TRUE)
end

@external
Expand All @@ -163,8 +163,7 @@ func transferFrom{
let (new_allowance: Uint256) = uint256_sub(caller_allowance, amount)
allowances.write(sender, caller, new_allowance)

# Cairo equivalent to 'return (true)'
return (1)
return (TRUE)
end

@external
Expand All @@ -176,8 +175,7 @@ func approve{
let (caller) = get_caller_address()
_approve(caller, spender, amount)

# Cairo equivalent to 'return (true)'
return (1)
return (TRUE)
end

@external
Expand All @@ -197,8 +195,7 @@ func increaseAllowance{

_approve(caller, spender, new_allowance)

# Cairo equivalent to 'return (true)'
return (1)
return (TRUE)
end

@external
Expand All @@ -219,8 +216,7 @@ func decreaseAllowance{

_approve(caller, spender, new_allowance)

# Cairo equivalent to 'return (true)'
return (1)
return (TRUE)
end

#
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cairo-lang>=0.8.1
cairo-nile>=0.5.0
cairo-lang>=0.9.0
cairo-nile>=0.6.0
pytest
pytest-asyncio
22 changes: 22 additions & 0 deletions scripts/calculate-class-hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
import json
from starkware.starknet.core.os.class_hash import compute_class_hash
from starkware.starknet.services.api.contract_class import ContractClass

path_to_json = './artifacts/'
json_files = [pos_json for pos_json in os.listdir(
path_to_json) if pos_json.endswith('.json')]


def print_class_hash(class_location):
location = path_to_json + class_location
with open(location) as f:
class_data = json.load(f)
contract_class = ContractClass.load(class_data)
contract_hash = compute_class_hash(
contract_class=contract_class,
)
print("{} class hash: {}".format(class_location, hex(contract_hash)))


list(map(print_class_hash, json_files))
15 changes: 10 additions & 5 deletions test/argent_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
DEFAULT_TIMESTAMP = 1640991600
ESCAPE_SECURITY_PERIOD = 24*7*60*60

VERSION = str_to_felt('0.2.1')
VERSION = str_to_felt('0.2.2')

IACCOUNT_ID = 0xf10dbd44

ESCAPE_TYPE_GUARDIAN = 0
ESCAPE_TYPE_SIGNER = 1
ESCAPE_TYPE_GUARDIAN = 1
ESCAPE_TYPE_SIGNER = 2

@pytest.fixture(scope='module')
def event_loop():
Expand All @@ -36,7 +36,11 @@ async def get_starknet():
return starknet

def update_starknet_block(starknet, block_number=1, block_timestamp=DEFAULT_TIMESTAMP):
starknet.state.state.block_info = BlockInfo(block_number=block_number, block_timestamp=block_timestamp, gas_price=0)
starknet.state.state.block_info = BlockInfo(
block_number=block_number,
block_timestamp=block_timestamp,
gas_price=0,
sequencer_address=starknet.state.state.block_info.sequencer_address)

def reset_starknet_block(starknet):
update_starknet_block(starknet=starknet)
Expand Down Expand Up @@ -516,4 +520,5 @@ async def test_is_valid_signature(account_factory):
for sig in [signer, guardian]:
signatures += list(sig.sign(hash))

await account.is_valid_signature(hash, signatures).call()
res = (await account.is_valid_signature(hash, signatures).call()).result
assert (res.is_valid == 1)
Loading

0 comments on commit 5373e0d

Please sign in to comment.