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

Eth account support #361

Merged
merged 66 commits into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
8561dc7
Create separate execute function
JulissaDantes Jun 10, 2022
7676f32
Add is_valid_eth_signature to account library
JulissaDantes Jun 10, 2022
99446d5
Add eth_execute to account library
JulissaDantes Jun 10, 2022
1e2f521
Create eth account mock and test
JulissaDantes Jun 10, 2022
2b7b91a
Add missing dependencies
JulissaDantes Jun 10, 2022
c72c4cd
Create TestEthSigner
JulissaDantes Jun 10, 2022
b6e4701
Update used private key
JulissaDantes Jun 10, 2022
2f737db
Update implicit parameters
JulissaDantes Jun 10, 2022
a92e1de
Update execute parameters
JulissaDantes Jun 14, 2022
e507109
Update all implicit arguments
JulissaDantes Jun 14, 2022
8e0c04d
Update signature values and hash
JulissaDantes Jun 14, 2022
199d272
Update variable name
JulissaDantes Jun 14, 2022
f9cd939
Merge branch 'main' into eth-account
JulissaDantes Jun 14, 2022
2601297
Update documentation
JulissaDantes Jun 15, 2022
75f75c5
Fix merge error
JulissaDantes Jun 15, 2022
4092021
Improve format
JulissaDantes Jun 15, 2022
2e7e85a
Update tests/utils.py
JulissaDantes Jun 17, 2022
6219d02
Update docs/Account.md
JulissaDantes Jun 17, 2022
121f87d
Update docs/Account.md
JulissaDantes Jun 17, 2022
12fbf29
Rename test and fix documentation
JulissaDantes Jun 17, 2022
a88b9bd
Add documentation
JulissaDantes Jun 21, 2022
0394607
Update src/openzeppelin/account/library.cairo
JulissaDantes Jun 21, 2022
b10dbaf
Update src/openzeppelin/account/library.cairo
JulissaDantes Jun 21, 2022
e5a2f97
Update src/openzeppelin/account/library.cairo
JulissaDantes Jun 21, 2022
e62486d
Update src/openzeppelin/account/library.cairo
JulissaDantes Jun 21, 2022
2880487
Update tests/utils.py
JulissaDantes Jun 21, 2022
9d47a8c
Update tests/utils.py
JulissaDantes Jun 21, 2022
c7c685f
Update tests/mocks/eth_account.cairo
JulissaDantes Jun 21, 2022
dd74a2b
Create eth account preset
JulissaDantes Jun 21, 2022
09faeb0
Merge branch 'eth-account' of https://github.com/JulissaDantes/cairo-…
JulissaDantes Jun 21, 2022
3b71400
Create signers module
JulissaDantes Jun 22, 2022
e868715
use assert_revert to test nonce
JulissaDantes Jun 22, 2022
69ed42b
Add test for valid signature
JulissaDantes Jun 23, 2022
586ba4f
use internal hash
JulissaDantes Jun 23, 2022
43dea0f
Update validity test
JulissaDantes Jun 23, 2022
7204bf6
Update docs/Account.md
JulissaDantes Jun 24, 2022
3572442
Update docs/Account.md
JulissaDantes Jun 24, 2022
c7b2f73
Update tests/signers.py
JulissaDantes Jun 24, 2022
f357988
Update src/openzeppelin/account/library.cairo
JulissaDantes Jun 24, 2022
e002076
Merge branch 'main' into eth-account
JulissaDantes Jun 24, 2022
0123dae
Fix after merge
JulissaDantes Jun 24, 2022
397756c
Improve tests
JulissaDantes Jun 24, 2022
042c4fd
Update account library
JulissaDantes Jun 24, 2022
0171e96
Update Account.md
JulissaDantes Jun 24, 2022
d9ab11e
update format
JulissaDantes Jun 24, 2022
3e8e82d
Update src/openzeppelin/account/library.cairo
JulissaDantes Jun 27, 2022
656d460
Update tests/access/test_Ownable.py
JulissaDantes Jun 27, 2022
a195736
Update src/openzeppelin/account/library.cairo
JulissaDantes Jun 27, 2022
8cc4308
Update eth test
JulissaDantes Jun 27, 2022
866254a
Update Account.md
JulissaDantes Jun 27, 2022
cfa1713
Update test
JulissaDantes Jun 27, 2022
9dc3c31
Update tests/signers.py
JulissaDantes Jun 27, 2022
7c9291b
Fix typo
JulissaDantes Jun 27, 2022
860f756
Merge branch 'eth-account' of https://github.com/JulissaDantes/cairo-…
JulissaDantes Jun 27, 2022
b73307a
Update signers
JulissaDantes Jun 27, 2022
0d0bc1a
Update test
JulissaDantes Jun 28, 2022
47b4785
Update docs/Account.md
JulissaDantes Jun 29, 2022
346cd79
Update docs/Account.md
JulissaDantes Jun 29, 2022
650e578
Update tests/signers.py
JulissaDantes Jun 29, 2022
fd745fa
Update docs/Account.md
JulissaDantes Jun 29, 2022
78a7b7b
Merge branch 'main' into eth-account
JulissaDantes Jun 29, 2022
668a25f
Merge branch 'eth-account' of https://github.com/JulissaDantes/cairo-…
JulissaDantes Jun 29, 2022
7d78282
update test
JulissaDantes Jun 29, 2022
4e583e5
Update documenation for Account
JulissaDantes Jun 29, 2022
4302136
Update docs/Account.md
martriay Jun 29, 2022
2c121ed
Update docs/Account.md
JulissaDantes Jun 29, 2022
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
81 changes: 73 additions & 8 deletions docs/Account.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ A more detailed writeup on the topic can be found on [Perama's blogpost](https:/
* [`set_public_key`](#set_public_key)
* [`is_valid_signature`](#is_valid_signature)
* [`__execute__`](#__execute__)
* [`is_valid_eth_signature`](#is_valid_eth_signature)
* [`eth_execute`](#eth_execute)
* [`_unsafe_execute`](#_unsafe_execute)
* [Presets](#presets)
* [Eth Account](#eth-account)
JulissaDantes marked this conversation as resolved.
Show resolved Hide resolved
* [Account differentiation with ERC165](#account-differentiation-with-erc165)
* [Extending the Account contract](#extending-the-account-contract)
* [L1 escape hatch mechanism](#l1-escape-hatch-mechanism)
Expand Down Expand Up @@ -159,6 +164,13 @@ If utilizing multicall, send multiple transactions with the `send_transactions`
)
```

### MockeEthSigner utility
JulissaDantes marked this conversation as resolved.
Show resolved Hide resolved

The `MockeEthSigner` class in [utils.py](../tests/utils.py) is used to perform transactions on a given Account with a secp256k1 curve key pair, crafting the transaction and managing nonces. It differs from the `MockSigner` implementation by:

* not using the public key but its derived address instead (the last 20 bytes of the keccak256 hash of the public key and adding `0x` to the beginning)
* signing the message with a secp256k1 curve address

## Account entrypoint

`__execute__` acts as a single entrypoint for all user interaction with any contract, including managing the account contract itself. That's why if you want to change the public key controlling the Account, you would send a transaction targeting the very Account contract:
Expand Down Expand Up @@ -361,11 +373,8 @@ is_valid: felt

This is the only external entrypoint to interact with the Account contract. It:

1. Takes the input and builds a `Call` for each iterated message. See [Multicall transactions](#multicall-transactions) for more information
2. Validates the transaction signature matches the message (including the nonce)
3. Increments the nonce
4. Calls the target contract with the intended function selector and calldata parameters
5. Forwards the contract call response data as return value
1. Validates the transaction signature matches the message (including the nonce)
2. Calls _unsafe_execute.
JulissaDantes marked this conversation as resolved.
Show resolved Hide resolved

Parameters:

Expand All @@ -386,6 +395,57 @@ response_len: felt
response: felt*
```

### `is_valid_eth_signature`

Returns `TRUE` if a given signature in the secp256k1 curve is valid, otherwise it reverts. In the future it will return `FALSE` if a given signature is invalid (for more info please check [this issue](https://github.com/OpenZeppelin/cairo-contracts/issues/327)).

Parameters:

```cairo
signature_len: felt
signature: felt*
```

Returns:

```cairo
is_valid: felt
```

> returns `TRUE` if a given signature is valid. Otherwise, reverts. In the future it will return `FALSE` if a given signature is invalid (for more info please check [this issue](https://github.com/OpenZeppelin/cairo-contracts/issues/327)).

### `eth_execute`

This follows the same idea as the vanilla version of `execute` with the sole difference that signature verification is on the secp256k1 curve.

Parameters:

```cairo
call_array_len: felt
call_array: AccountCallArray*
calldata_len: felt
calldata: felt*
nonce: felt
```

> Note that the current signature scheme expects a 7-element array like `[sig_v, uint256_sig_r_low, uint256_sig_r_high, uint256_sig_s_low, uint256_sig_s_high, uint256_hash_low, uint256_hash_high]` given that the parameters of the verification are bigger than a felt.

Returns:

```cairo
response_len: felt
response: felt*
```

### `_unsafe_execute`

Is the internal method that performs the following task inside the `execute` and `eth_execute` functions:
JulissaDantes marked this conversation as resolved.
Show resolved Hide resolved

1. Increments the nonce.
2. Takes the input and builds a `Call` for each iterated message. See [Multicall transactions](#multicall-transactions) for more information.
3. Calls the target contract with the intended function selector and calldata parameters
4. Forwards the contract call response data as return value

## Account differentiation with ERC165

Certain contracts like ERC721 require a means to differentiate between account contracts and non-account contracts. For a contract to declare itself as an account, it should implement [ERC165](https://eips.ethereum.org/EIPS/eip-165) as proposed in [#100](https://github.com/OpenZeppelin/cairo-contracts/discussions/100). To be in compliance with ERC165 specifications, the idea is to calculate the XOR of `IAccount`'s EVM selectors (not StarkNet selectors). The resulting magic value of `IAccount` is 0x50b70dcb.
Expand All @@ -394,13 +454,18 @@ Our ERC165 integration on StarkNet is inspired by OpenZeppelin's Solidity implem

## Extending the Account contract

Account contracts can be extended by following the [extensibility pattern](../docs/Extensibility.md#the-pattern). The basic idea behind integrating the pattern is to import the requisite methods from the Account library and incorporate the extended logic thereafter.
Account contracts can be extended by following the [extensibility pattern](../docs/Extensibility.md#the-pattern).

To implement custom account contracts, a pair of `validate` and `execute` functions should be exposed. This is why the Account library comes with different flavors of such pairs, like the vanilla `is_valid_signature` and `execute`, or the Ethereum flavored `is_valid_eth_signature` and `eth_execute` pair.

Account contract developers are encouraged to implement the [standard Account interface](https://github.com/OpenZeppelin/cairo-contracts/discussions/41) and incorporate the custom logic thereafter.

To implement alternative `execute` functions, make sure to check their corresponding `validate` function before calling the `_unsafe_execute` building block. Do not expose `_unsafe_execute` directly.

Currently, there's only a single library/preset Account scheme, but we're looking for feedback and new presets to emerge. Some new validation schemes to look out for in the future:
Some other validation schemes to look out for in the future:

* multisig
* guardian logic like in [Argent's account](https://github.com/argentlabs/argent-contracts-starknet/blob/de5654555309fa76160ba3d7393d32d2b12e7349/contracts/ArgentAccount.cairo)
* [Ethereum signatures](https://github.com/OpenZeppelin/cairo-contracts/issues/161)
JulissaDantes marked this conversation as resolved.
Show resolved Hide resolved

## L1 escape hatch mechanism

Expand Down
5 changes: 3 additions & 2 deletions src/openzeppelin/account/Account.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

%lang starknet

from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin
from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin

from openzeppelin.account.library import Account, AccountCallArray

Expand Down Expand Up @@ -95,7 +95,8 @@ func __execute__{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
ecdsa_ptr: SignatureBuiltin*
ecdsa_ptr: SignatureBuiltin*,
bitwise_ptr: BitwiseBuiltin*
JulissaDantes marked this conversation as resolved.
Show resolved Hide resolved
}(
call_array_len: felt,
call_array: AccountCallArray*,
Expand Down
113 changes: 113 additions & 0 deletions src/openzeppelin/account/EthAccount.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# SPDX-License-Identifier: MIT
# OpenZeppelin Contracts for Cairo v0.1.0 (account/EthAccount.cairo)

%lang starknet
from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin
from openzeppelin.account.library import Account, AccountCallArray

from openzeppelin.introspection.ERC165 import ERC165

#
# Constructor
#

@constructor
func constructor{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(eth_address: felt):
Account.initializer(eth_address)
return ()
end

#
# Getters
#

@view
func get_eth_address{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (res: felt):
let (res) = Account.get_public_key()
return (res=res)
end

@view
func get_nonce{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (res: felt):
let (res) = Account.get_nonce()
return (res=res)
end

@view
func supportsInterface{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
range_check_ptr
} (interfaceId: felt) -> (success: felt):
let (success) = ERC165.supports_interface(interfaceId)
return (success)
end

#
# Setters
#

@external
func set_eth_address{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(new_eth_address: felt):
Account.set_public_key(new_eth_address)
return ()
end

#
# Business logic
#

@view
func is_valid_signature{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
ecdsa_ptr: SignatureBuiltin*,
bitwise_ptr: BitwiseBuiltin*
}(
hash: felt,
signature_len: felt,
signature: felt*
) -> (is_valid: felt):
let (is_valid) = Account.is_valid_eth_signature(hash, signature_len, signature)
return (is_valid=is_valid)
end

@external
func __execute__{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
bitwise_ptr: BitwiseBuiltin*
}(
call_array_len: felt,
call_array: AccountCallArray*,
calldata_len: felt,
calldata: felt*,
nonce: felt
) -> (response_len: felt, response: felt*):
let (response_len, response) = Account.eth_execute(
call_array_len,
call_array,
calldata_len,
calldata,
nonce
)
return (response_len=response_len, response=response)
end
Loading