diff --git a/EIPS/eip-725.md b/EIPS/eip-725.md index ee0b8c9b..063a0674 100644 --- a/EIPS/eip-725.md +++ b/EIPS/eip-725.md @@ -10,283 +10,158 @@ created: 2017-10-02 --- ## Simple Summary -A proxy contract for key management and execution, to establish a Blockchain identity. +A standard interface for a simple proxy account. ## Abstract -The following describes standard functions for a unique identity for humans, groups, objects and machines. -This identity can hold keys to sign actions (transactions, documents, logins, access, etc), and claims, which are attested from third parties (issuers) and self-attested ([#ERC735](https://github.com/ethereum/EIPs/issues/735)), as well as a proxy function, to act directly on the blockchain. + +The following describes standard functions for a unique identifiable proxy account to be used by humans, groups, organisations, objects and machines. The proxy has 2 abilities: (1) it can execute arbitrary contract calls, and (2) it can hold arbitrary data through a generic key/value store. One of these keys should hold the owner of the contract. The owner may be an address or a key manager contract for more complex management logic. Most importantly, this contract should be the reference point for a long-lasting identifiable profiles. ## Motivation -This standardized identity interface will allow Dapps, smart contracts and third parties to check the validity of a person, organization, object or machine through 2 steps as described in the function XXX. Trust is here transferred to the issuers of claims. - -The most important functions to verify an identity are: `XXX` - -The most important functions to manage an identity are: `XXX` - - -## Definitions - -- `keys`: Keys are public keys from either external accounts, or contracts' addresses. -- `claim issuer`: is another smart contract or external account, which issues claims about this identity. The claim issuer can be an identity contract itself. -- `claim`: For details about claims see [#ERC735](https://github.com/ethereum/EIPs/issues/735) - +Standardizing a minimal interface for an proxy account allows third parties to interact with various proxy accounts contracts in a consistent manner. +the benefit is a persistent account that is independed from single keys and can attach an arbitrary amount of information to verifiy, or enhance the accounts purpose. ## Specification -### Key Management +### Methods -Keys are cryptographic public keys, or contract addresses associated with this identity. -The structure should be as follows: - -- `key`: A public key owned by this identity - - `purpose`: `uint256` The key purpose. e.g., 1 = MANAGEMENT, 2 = ACTION, 3 = CLAIM, 4 = ENCRYPTION - - `keyType`: The type of key used, which would be a `uint256` for different key types. e.g. 1 = ECDSA, 2 = RSA, etc. - - `key`: `bytes32` The public key. // for non-hex and long keys, its the Keccak256 hash of the key +#### owner +Returns the current owner ```js -struct Key { - uint256 purpose; - uint256 keyType; - bytes32 key; -} +function address public owner; ``` -#### getKey +#### changeOwner -Returns the full key data, if present in the identity. +Changes the current owner. MUST only be called by the current owner of the contract. -``` js -function getKey(bytes32 _key) constant returns(uint256 purpose, uint256 keyType, bytes32 key); +```js +function changeOwner(address _owner); ``` -#### keyHasPurpose +**Triggers Event:** [OwnerChanged](#ownerchanged) -Returns `TRUE` if a key is present and has the given purpose. If the key is not present it returns `FALSE`. +#### getData -``` js -function keyHasPurpose(bytes32 _key, uint256 purpose) constant returns(bool exists); +Returns the data at the specified key. + +```js +function getData(bytes32 _key) external view returns (bytes _value); ``` +#### setData -#### getKeysByPurpose +Sets the data at a specific key. MUST only be called by the current owner of the contract. -Returns an array of public key bytes32 held by this identity. +**Triggers Event:** [DataChanged](#datachanged) -``` js -function getKeysByPurpose(uint256 _purpose) constant returns(bytes32[] keys); +```js +function setData(bytes32 _key, bytes _value) external; ``` - -#### addKey - -Adds a `_key` to the identity. The `_purpose` specifies the purpose of the key. Initially, we propose four purposes: - -- `1`: MANAGEMENT keys, which can manage the identity -- `2`: ACTION keys, which perform actions in this identity's name (signing, logins, transactions, etc.) -- `3`: CLAIM signer keys, used to sign claims on other identities which need to be revocable. -- `4`: ENCRYPTION keys, used to encrypt data e.g. hold in claims. - -MUST only be done by keys of purpose `1`, or the identity itself. If it's the identity itself, the approval process will determine its approval. - -**Triggers Event:** [KeyAdded](#keyadded) - -``` js -function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) returns (bool success) -``` - - -#### removeKey - -Removes `_key` from the identity. - -MUST only be done by keys of purpose `1`, or the identity itself. If it's the identity itself, the approval process will determine its approval. - -**Triggers Event:** [KeyRemoved](#keyremoved) - -``` js -function removeKey(bytes32 _key, uint256 _purpose) returns (bool success) -``` - - --------------------------------------------------------- - -### Identity usage - - #### execute -Executes an action on other contracts, or itself, or a transfer of ether. -SHOULD require `approve` to be called with one or more keys of purpose `1` or `2` to approve this execution. +Executes an action on other contracts or a transfer of the blockchains native cryptocurrency. MUST only be called by the current owner of the contract. -Execute COULD be used as the only accessor for `addKey`, `removeKey` and `replaceKey` and `removeClaim`. - -**Returns `executionId`:** SHOULD be sent to the `approve` function, to approve or reject this execution. - -**Triggers Event:** [ExecutionRequested](#executionrequested) -**Triggers on direct execution Event:** [Executed](#executed) - -``` js -function execute(address _to, uint256 _value, bytes _data) returns (uint256 executionId) +```js +function execute(uint256 _operationType, address _to, uint256 _value, bytes _data) external; ``` +The `operationType` should represent the assembly operation as follows: +- `0` for `call` +- `1` for `create` -#### approve - -Approves an execution or claim addition. -This SHOULD require `n` of `m` approvals of keys purpose `1`, if the `_to` of the execution is the identity contract itself, to successfully approve an execution. -And COULD require `n` of `m` approvals of keys purpose `2`, if the `_to` of the execution is another contract, to successfully approve an execution. - -**Triggers Event:** [Approved](#approved) -**Triggers on successfull execution Event:** [Executed](#executed) -**Triggers on successfull claim addition Event:** [ClaimAdded](#claimadded) - -``` js -function approve(uint256 _id, bool _approve) returns (bool success) -``` - - --------------------------------------------------------- - - -### Identity verification - -Requires: [ERC 735](https://github.com/ethereum/EIPs/issues/735) - -##### The following changes to [ERC 735](https://github.com/ethereum/EIPs/issues/735) are REQUIRED: - -#### addClaim - -This SHOULD create a pending claim, which SHOULD be approved or rejected by `n` of `m` `approve` calls from keys of purpose `1`. - -Only Events: -**Triggers if the claim is new Event and approval process exists:** [ClaimRequested](#claimrequested) -**Triggers if the claim index existed Event:** [ClaimChanged](https://github.com/ethereum/EIPs/issues/735) - - -#### removeClaim - -MUST only be done by the `issuer` of the claim, or keys of purpose `1`, or the identity itself. If it's the identity itself, the approval process will determine its approval. - - --------------------------------------------------------- - +Others may be added in the future. Inspired by [ERC1077](https://eips.ethereum.org/EIPS/eip-1077) and [Gnosis](https://github.com/gnosis/safe-contracts/blob/master/contracts/Enum.sol#L7) ### Events -#### KeyAdded +#### DataChanged -MUST be triggered when `addKey` was successfully called. +MUST be triggered when `setData` was successfully called. -``` js -event KeyAdded(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType) +```js +event DataChanged(bytes32 indexed key, bytes value); +``` + +#### ContractCreated + +MUST be triggered when `execute` creates a new contract using the `_operationType` `1`. + +```js +event ContractCreated(address indexed contractAddress); +``` + +#### OwnerChanged + +MUST be triggered when `changeOwner` was successfully called. + +```js +event OwnerChanged(address indexed ownerAddress); ``` -#### KeyRemoved +### Ownership -MUST be triggered when `removeKey` was successfully called. +This contract is controlled by the owner. The owner can be a smart contract or an address, or itself. -``` js -event KeyRemoved(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType) -``` +### Data keys +Data keys, should be the keccak256 hash of a type name. +e.g. `myNewKeyType` is `0xa94996022594f93c34a730df0ae89d1ecd69dff98c17d0387e69ce58346323a4` +#### Multiple keys of the same type -#### ExecutionRequested +Multiple keys for the same key type must add a `keyTypeName-1` at the end of the key type. -MUST be triggered when `execute` was successfully called. - -``` js -event ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data) -``` - - -#### Executed - -MUST be triggered when `approve` was called and the execution was successfully approved. - -``` js -event Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data) -``` - - -#### Approved - -MUST be triggered when `approve` was successfully called. - -``` js -event Approved(uint256 indexed executionId, bool approved) -``` - -##### The following changes to [ERC 735](https://github.com/ethereum/EIPs/issues/735) are REQUIRED: - -#### ClaimRequested - -MUST be triggered when `addClaim` was successfully called. - - -#### ClaimAdded - -MUST be triggered when `approve` was called and the claim was successfully added. - - -## Constraints - -- A claim can only be one type per type per issuer. +This would looks as follows for `myNewKeyType`: +version 0 `myNewKeyType`: `0xa94996022594f93c34a730df0ae89d1ecd69dff98c17d0387e69ce58346323a4` +version 1 `myNewKeyType-1`: `0xb6dace1ed14874742c4d1b8cd9b270305176f769e0ae22118a02c2db4e620f29` +version 2 `myNewKeyType-2`: `0x6cc96a01de588f4550e8c3a821aed065ae7897f8dfb61836c78c0389e499d9ed` +... +Anyone that would like to standardize a new data key should make a pull request to update the table below. +| Name | Description | Key | value | +| --- | --- | --- | --- | +| owner | The owner of the proxy account | 0x0000000000000000000000000000000000000000000000000000000000000000 | left padded owner address, e.g. `0x000000000000000000000000de0B295669a9FD93d5F28D9Ec85E40f4cb697BAe` | +| 735 | The proxy accounts claim holder contract (per [ERC735](https://github.com/ethereum/EIPs/issues/735)) | 0xb0f23aea7d77ce19f9393243a7b50a3bcaac893c7d68a5a309dea7cacf035fd0 | left padded address of the claim holder contract, e.g. `0x000000000000000000000000de0B295669a9FD93d5F28D9Ec85E40f4cb697BAe` | +| 780 | The proxy accounts claim holder contract (per [ERC735](https://github.com/ethereum/EIPs/issues/735)) | 0xdaf52dba5981246bcf8fd7c6b00dce587fdcf5e2a95b281eea95dcd1376afdcd | left padded address of the claim registry contract, e.g. `0x000000000000000000000000de0B295669a9FD93d5F28D9Ec85E40f4cb697BAe` | ## Rationale -This specification was chosen to allow most flexibility and experimentation around identity. By having each identity in a separate contract it allows for cross identity compatibility, but at the same time extra and altered functionality for new use cases. -The main critic of this standard is the verification where each identity that issues a claim, also should have a separate CLAIM signing key attached. While [#ERC780](https://github.com/ethereum/EIPs/issues/780) uses a standardized registry to assign claims to addresses. -Both systems could work in conjunction and should be explored. -While also off-chain claims using DID verifiable claims and merkle tries can be added as claims and should be explored. +The purpose of an identity proxy is to allow an entity to exist as a first-class citizen in Ethereum, with the ability to execute arbitrary contract calls. At that same time the proxy account should be managed by an arbitrary simple or complex logic. -The rationale of this standard is to function as an open and very flexible container for identity. +It also opens up the possibility of [meta transactions](https://medium.com/@austin_48503/ethereum-meta-transactions-90ccf0859e84), where a third party can send a transaction to the owner contract, that then verifies the execution permission based on a signed message. + +It further allows any information to be attached to that proxy accounts which can be in the forms of claims via [ERC735](https://github.com/ethereum/EIPs/issues/735) or [ERC780](https://github.com/ethereum/EIPs/issues/780), or any arbitrary new systems and protocols. + +This specification was chosen to allow the most flexibility and experimentation around verifiable manageable accounts. ## Implementation -- [DID resolver specification](https://github.com/WebOfTrustInfo/rebooting-the-web-of-trust-spring2018/blob/master/topics-and-advance-readings/DID-Method-erc725.md) -- [Implementation by mirceapasoi](https://github.com/mirceapasoi/erc725-735) -- [Implementation by Nick Poulden](https://github.com/OriginProtocol/identity-playground), interface at: https://erc725.originprotocol.com/ +- [Implementation by ERC725Alliance](https://github.com/ERC725Alliance/erc725/tree/master/contracts/contracts) ### Solidity Interface ```js -pragma solidity ^0.4.18; +pragma solidity ^0.5.4; -contract ERC725 { +interface ERC725 { + event DataChanged(bytes32 indexed key, bytes32 indexed value); + event OwnerChanged(address indexed ownerAddress); + event ContractCreated(address indexed contractAddress); - uint256 constant MANAGEMENT_KEY = 1; - uint256 constant ACTION_KEY = 2; - uint256 constant CLAIM_SIGNER_KEY = 3; - uint256 constant ENCRYPTION_KEY = 4; + // address public owner; - event KeyAdded(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType); - event KeyRemoved(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType); - event ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data); - event Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data); - event Approved(uint256 indexed executionId, bool approved); - - struct Key { - uint256 purpose; //e.g., MANAGEMENT_KEY = 1, ACTION_KEY = 2, etc. - uint256 keyType; // e.g. 1 = ECDSA, 2 = RSA, etc. - bytes32 key; - } - - function getKey(bytes32 _key) public constant returns(uint256 purpose, uint256 keyType, bytes32 key); - function keyHasPurpose(bytes32 _key, uint256 _purpose) public constant returns (bool exists); - function getKeysByPurpose(uint256 _purpose) public constant returns (bytes32[] keys); - function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success); - function removeKey(bytes32 _key, uint256 _purpose) public returns (bool success); - function execute(address _to, uint256 _value, bytes _data) public returns (uint256 executionId); - function approve(uint256 _id, bool _approve) public returns (bool success); + function changeOwner(address _owner) external; + function getData(bytes32 _key) external view returns (bytes32 _value); + function setData(bytes32 _key, bytes32 _value) external; + function execute(uint256 _operationType, address _to, uint256 _value, bytes calldata _data) external; } ```