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

Bump to cairo v0.10 #449

Merged
merged 59 commits into from
Sep 11, 2022
Merged

Bump to cairo v0.10 #449

merged 59 commits into from
Sep 11, 2022

Conversation

andrew-fleming
Copy link
Collaborator

@andrew-fleming andrew-fleming commented Sep 3, 2022

This PR proposes to:

On top of the many changes, the new Cairo version's testing framework hasn't accounted for all the updates. This PR proposes to account for this by refactoring the MockSigner. Some things to note:

  • fetching the return values for signer calls is different i.e. return_val.result.response is now return_val.call_info.retdata[]
  • assert_event_emitted makes assertions against the OrderedEvent object instead of Event
  • signers.py includes a local mock Nile. This should be removed once Nile supports Cairo v0.10
  • cairo-nile is removed as a dep in tox. This should also be removed upon its update.

Copy link
Contributor

@martriay martriay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that's not documented on the issue is updating documentation. Considering we aim to safely launch as soon as possible I suggest to leave it outside of scope.

Copy link
Member

@ericnordelo ericnordelo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Andrew, I left a couple of question and comments.

src/openzeppelin/account/library.cairo Outdated Show resolved Hide resolved
tests/mocks/AccessControl.cairo Show resolved Hide resolved
let (success) = ERC165.supports_interface(interfaceId)
return (success)
end
func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we refactor to avoid returning tuples but single values in cases like this? (0.10 introduction of single return values)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh yes, good call. This is just what cairo-migrate spit out. I'll update

tests/mocks/ERC721SafeMintableMock.cairo Show resolved Hide resolved
@martriay martriay mentioned this pull request Sep 7, 2022
src/openzeppelin/access/accesscontrol/library.cairo Outdated Show resolved Hide resolved
src/openzeppelin/access/ownable/library.cairo Show resolved Hide resolved
src/openzeppelin/account/library.cairo Show resolved Hide resolved
Comment on lines 33 to 34
let (res) = Account.get_public_key();
return (res=res);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let (res) = Account.get_public_key();
return (res=res);
return Account.get_public_key();

let's try to return a single value. if not, at least not res:

Suggested change
let (res) = Account.get_public_key();
return (res=res);
let (publicKey) = Account.get_public_key();
return (publicKey);

Comment on lines 30 to 31
let (res) = Account.get_public_key();
return (res=res);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as the other

@martriay martriay mentioned this pull request Sep 7, 2022
# Account
const IACCOUNT_ID = 0xf10dbd44
// Account
const IACCOUNT_ID = 0xf10dbd44;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update that value according to the new IAccount interface.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good call.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniset @martriay we're including isValidSignature in the interface, correct? If so, this is how it looks. Let me know if there are any objections.

interface IAccount {
    function isValidSignature(bytes32, bytes32, bytes32) external;    // hash, r, s
    function __execute__(address, bytes4, bytes calldata) external;   // to, selector, calldata
    function __validate__(address, bytes4, bytes calldata) external;  // to, selector, calldata
    function __validate_declare__(bytes32) external;                  // class hash
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrew-fleming I don't think that properly represents the IAccount interface. We need to take into account that we can have multiple signatures in isValidSignature and multicalls in __execute__ and __validate__.

I would propose to use

interface IAccount {

    struct Call {
        address to;
        bytes4 selector;
        uint256 data;
    }

    function isValidSignature(bytes32, bytes32[] calldata) external;
    function __execute__(Call[] calldata) external;
    function __validate__(Call[] calldata) external;
    function __validate_declare__(bytes32) external;
}

contract Selector {

    // returns 0x56ad812f
    function calculateSelector() public pure returns (bytes4) {
        IAccount i;
        return i.isValidSignature.selector ^
        i.__execute__.selector ^
        i.__validate__.selector ^
        i.__validate_declare__.selector;
    }
}

which gives IACCOUNT_ID = 0x56ad812f.

What do you think?

Copy link
Contributor

@juniset juniset Sep 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, shouldn't we include supportsInterface to the IAccount interface? It's the only way for a contract to detect that an address is an account hence it should be mandatory.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right.But I think it should be bytes then.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect. And I just spoke to @martriay and we're all in agreement with adding supportsInterface to IAccount; therefore:

interface IAccount {

    struct Call {
        address to;
        bytes4 selector;
        bytes data;
    }

    function supportsInterface(bytes4) external;
    function isValidSignature(bytes32, bytes32[] calldata) external;
    function __execute__(Call[] calldata) external;
    function __validate__(Call[] calldata) external;
    function __validate_declare__(bytes32) external;
}

contract Selector {

    // returns 0xa66bd575
    function calculateSelector() public pure returns (bytes4) {
        IAccount i;
        return i.supportsInterface.selector^
        i.isValidSignature.selector ^
        i.__execute__.selector ^
        i.__validate__.selector ^
        i.__validate_declare__.selector;
    }
}

which gives us IACCOUNT = 0xa66bd575. What does everyone think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @andrew-fleming , that looks good to me!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! I still have two doubts:

  • should bytes4 be bytes32 considering that selectors are way longer (1 felt) in Cairo?
  • our current interface doesn't actually take Call[] because we can't handle arrays of complex structs yet. I guess we want to be future proof but do we agree this isn't the current interface?

other than that, LGTM

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should bytes4 be bytes32 considering that selectors are way longer (1 felt) in Cairo?

I agree that bytes32 is a more accurate type for SN selectors; however, bytes4 seems more compliant with EIP165. I vote for keeping bytes4

our current interface doesn't actually take Call[] because we can't handle arrays of complex structs yet. I guess we want to be future proof but do we agree this isn't the current interface?

And yes, I agree the proposed interface is the interface-to-be, not the interface right now

Copy link
Contributor

@martriay martriay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getting there 🥺

src/openzeppelin/token/erc721/library.cairo Show resolved Hide resolved
src/openzeppelin/token/erc721/library.cairo Show resolved Hide resolved
src/openzeppelin/account/library.cairo Outdated Show resolved Hide resolved
src/openzeppelin/account/library.cairo Outdated Show resolved Hide resolved
src/openzeppelin/token/erc20/presets/ERC20.cairo Outdated Show resolved Hide resolved
Copy link
Contributor

@martriay martriay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super small changes requested, otherwise good to go!

Comment on lines 99 to 100
ERC20.transfer_from(sender, recipient, amount);
return ERC20.transfer_from(sender, recipient, amount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doublespend! is this passing the tests?

src/openzeppelin/account/presets/Account.cairo Outdated Show resolved Hide resolved
@martriay martriay merged commit a9894b4 into OpenZeppelin:main Sep 11, 2022
@andrew-fleming andrew-fleming deleted the cairo-v0.10 branch September 12, 2022 03:39
Copy link
Member

@ericnordelo ericnordelo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@martriay @andrew-fleming I left some comments but overall the PR LGTM (I know is already merged, but I added the last review as requested by @martriay).

//

func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
_public_key: felt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This underscore at the beginning of the argument name has some purpose?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. It did serve a purpose early on when public_key itself was a storage variable

src/openzeppelin/account/library.cairo Show resolved Hide resolved
#
# Getters
#
func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We changing the implicit arguments to lay in one line (instead of multiline as before) implies that we will follow the Cairo provided formatter to format the code?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of now, this is temporary^. We just wanted to ship the cairo v0.10 update

ecdsa_ptr: SignatureBuiltin*,
range_check_ptr,
}(hash: felt, signature_len: felt, signature: felt*) -> (is_valid: felt) {
let (_public_key) = Account_public_key.read();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another leading underscore.

#
# Business logic
#
func setPublicKey{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
Copy link
Member

@ericnordelo ericnordelo Sep 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method can make the contract unusable if a wrong public key is set by mistake. Maybe it would be good to add a two steps process allowing to claim the new public key before removing the old one? (Similar to the ownership two-steps transfers process in Solidity contracts).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agreed. If we're all in agreement, it's probably better suited for the next release

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue for this #469

Account.set_public_key(new_eth_address)
return ()
end
func setEthAddress{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment for two steps process as mentioned in Account.cairo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned in #469

#
# Getters
#
func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
Copy link
Member

@ericnordelo ericnordelo Sep 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a reminder of the upgradeAndCall suggestion to keep the atomicity if the new implementation needs initialization (this is in general for the upgrade function in upgradeable contracts).

@@ -14,549 +14,458 @@ from openzeppelin.introspection.erc165.IERC165 import IERC165
from openzeppelin.security.safemath.library import SafeUint256
from openzeppelin.token.erc721.IERC721Receiver import IERC721Receiver
from openzeppelin.utils.constants.library import (
IERC721_ID, IERC721_METADATA_ID, IERC721_RECEIVER_ID, IACCOUNT_ID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A reminder for myself to double check Interface IDs.

func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
proxy_admin: felt
) {
let (initialized) = Proxy_initialized.read();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we be using here the initializable library instead? I think importing libraries from libraries is not against the Extensibility Pattern right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we be using here the initializable library instead?

Agreed

I think importing libraries from libraries is not against the Extensibility Pattern right?

Correct :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue for this #470


# ERC165
const IERC165_ID = 0x01ffc9a7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remainder for double-checking Interfaces Ids is better left here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants