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

EIP-1480: Access Control Standard #1481

Closed
ben-kaufman opened this issue Oct 9, 2018 · 2 comments
Closed

EIP-1480: Access Control Standard #1481

ben-kaufman opened this issue Oct 9, 2018 · 2 comments
Labels

Comments

@ben-kaufman
Copy link
Contributor


eip: 1480
title: Modular Access Control Mechanism
author: Matan Tsuberi [email protected], Ben Kaufman [email protected], Adam Levi [email protected], Oren Sokolowsky [email protected]
discussions-to:
status: Draft
type: Standards Track
category: ERC
created: 2018-10-08
requires: 165

Simple Summary

Standard access control mechanism for smart contracts.

Abstract

This EIP presents a generalized mechanism for access control on smart contracts, enabling the use of complex boolean expressions for limiting access to contract's functions. The mechanism utilizes the idea of "keys" for the access limitations. Keys could be transferable, expirable, limited to certain amount of uses or limited to certain fucntion parameters use.

Motivation

Access control is one of the basic components most smart contract applications and frameworks need to have. The ability to limit the access for calling a function to a specific EOA or smart contract account is vital for most systems. The core logic for the access control of a smart contract has a great importance as it is usually the main security risk a contract may have, and if compromised, it can cause fatal issue for the entire system.
There is a vast number of use cases requiring access management for smart contracts. A few popular examples can be:

  • Ownable - This is probably the most popular access control mechanism used in the Ethereum space. OpenZeppelin's implementation can be found here.

  • Membership Management - There are much efforts for managing membership on the Ethereum blockchain. A full detailed rationale for that can be found on EIP-1261 - Membership Verification Token. However, this creates a duplication of effort as membership management is just a single aspect of access control. In addition, the current effort lacks some basic properties such as expiration and transferability of memberships.

  • DAO operations - There are multiple teams working in the DAO space, all facing the problem of access control in a DAO. Thus, there is a lot of duplicated work on the subject with each having its own pros and cons. However, non of them has found a mechanism generalized enough to answer all possible future needs of DAOs.

There is a strong need for an effective generalized way of managing access rights, most importantly in a trustless manner. We would like to propose a generalized mechanism for access control in smart contracts to provide easier interoperability, reduce security risks, and minimize the duplicated effort of teams working in the subject.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Every ERC-1480 compliant contract MUST implement the ERC1480 and ERC165 interfaces (subject to "caveats" below):

pragma solidity ^0.4.24;


/// @title ERC1480Interface - Access Control Interface
/// @dev basic inteface for access control mechanism
/// Note: the ERC-165 identifier for this interface is 0x33f9cb64.
interface ERC1480Interface {

    event AssignKey(
        bytes32 indexed _id,
        address indexed _from,
        address indexed _to,
        bool _assignable,
        uint _start,
        uint _expiration,
        uint _uses
    );

    event RevokeKey(bytes32 indexed _id, address indexed _owner);

    /// @dev assign partial or all capabilities from the sender to an account
    /// @param _id lock id
    /// @param _to recipient
    /// @param _assignable can the recipient further assign capabilities to other accounts?
    /// @param _start the key's start time (block number)
    /// @param _expiration the key's expiration time (block number)
    /// @param _uses number of times this key can be used (in `unlock(..)`)
    function assignKey(
        bytes32 _id,
        address _to,
        bool _assignable,
        uint _start,
        uint _expiration,
        uint _uses
        ) external;

    /// @dev assign all capabilities from the sender to an account
    /// @param _id lock id
    /// @param _to recipient
    function assignFullKey(bytes32 _id, address _to) external;

    /// @dev revoke the sender's key
    /// @param _id lock id
    function revokeKey(bytes32 _id) external;

    /// @dev does the owner have a valid key for the lock id
    /// @param _id lock id
    /// @param _owner owner address
    function unlockable(bytes32 _id, address _owner) external view returns (bool);

    /// @dev does the owner have a valid key for the lock id
    /// @param _id lock id
    /// @param _owner owner address
    /// @return the properties of the requested key as a tuple
    function getKey(bytes32 _id, address _owner) external view returns (bool, bool, uint, uint, uint);
}

/// @title ERC1480 - Access Control Interface
/// @dev contract for access control mechanism
contract ERC1480 is ERC165, ERC1480Interface {
    struct Key {
        bool exists;
        bool assignable;
        uint start;
        uint expiration;
        uint uses;
    }

    /// @dev Grant capabilities to account (overwrites existing key)
    /// @param _id lock id
    /// @param _to recipient
    /// @param _assignable can the recipient further assignKey his capabilities to other accounts?
    /// @param _start the key's start time (block timestamp)
    /// @param _expiration the key's expiration time (block timestamp)
    /// @param _uses number of times this key can be used (in `unlock(..)`)
    function grantKey(
        bytes32 _id,
        address _to,
        bool _assignable,
        uint _start,
        uint _expiration,
        uint _uses
        ) internal;

    /// @dev Grant full capabilities to account (assignable, no start time, no expiration, infinite uses)
    /// @param _id lock id
    /// @param _to recipient
    function grantFullKey(bytes32 _id, address _to) internal;

    /// @dev unlock a lock if sender has a valid key.
    /// @param _id lock id
    function unlock(bytes32 _id) internal returns (bool);
}

interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

The storage extention is RECOMMENDED for ERC-1480 smart contracts (see "caveats", below). This contains the RECOMMENDED data structure for storing the access "keys".

/// @title ERC1480Storage - Access Control, RECOMMENDED data structure
contract ERC1480Storage is ERC1480 {
    mapping(bytes32 => mapping(address => Key)) public keys;
}

Caveats

The 0.4.24 Solidity interface grammar is not expressive enough to document the ERC-1480 standard. A contract which complies with ERC-1480 MUST also abide by the following:

  • Solidity issue 3368: Fixes typo of = instead of == #3412: The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: payable, implicit nonpayable, view, and pure. Your implementation MUST meet the mutability guarantee in this interface and you MAY meet a stronger guarantee. For example, a payable function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.24 is that you can edit this interface to add stricter mutability before inheriting from your contract.
  • Solidity issue Added EIP-2330: EXTSLOAD #2330: If a function is shown in this specification as external then a contract will be compliant if it uses public visibility. As a workaround for version 0.4.20, you can edit this interface to switch to public before inheriting from your contract.

If a newer version of Solidity allows the caveats to be expressed in code, then this EIP MAY be updated and the caveats removed, such will be equivalent to the original specification.

Rationale

There are many approaches which were developed to create an access control mechanism, but each focuses on a relatively specific use case. We tried to create a generalized mechanism allowing for all uses cases to be implemented, while keeping the gas efficiency similar to a more dedicated solution.
We chose to use the concept of "Keys" with the certain properties of: uses limit, expiration time, and (re-)assignablity. This approach allows the use of complex boolean expressions for limiting access to a function such as allowing an account to call a certain function (or multiple functions) 2 times until the end of next month and possibly assign that right to another account.
A more concrete example could be for "Ownership" of a contract. A common use case for the ("Ownable" contract)[https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol] are "safty backdoors" - functions which enable the "owner" of the contract do controversial changes in crisis times. Most teams promise to eliminate their access to those backdoors after their project gets mature enogh, but this requires to trust the team to stand up for this promise. The "Ownable" contract pattern, thus, could benefit from the addition of a trustless expiration date, removing the need of trusting the teams to give up on their "safety backdoors" access when their project matures.

The proposed interface contains functions to allow utilizing the full capabilities of the properties of a key. Which are the ability to grant (by the contract), assign to other account, revoke, and use a key. This also keeps the option for implementations to have their own characteristics and suitable behaviour. For example, it is possible for an implementation to use block number for keys expiration, instead of timestamps. For gas optimizations, we also used 0 values to "disable" the use of certain features, this makes keys which doesn't have, for example, expiration time to have similar gas consumption to another solution with no expiration parameter at all.

Backwards Compatibility

There are no backwards compatibility concerns.

Test Cases

DAOstack ERC-1480 implementation includes test cases written using Truffle.

Implementation

DAOstack full implementation is available here.

Copyright

Copyright and related rights waived via CC0.

@github-actions
Copy link

github-actions bot commented Dec 4, 2021

There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Dec 4, 2021
@github-actions
Copy link

This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment.

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

No branches or pull requests

1 participant