ethereum / EIPs

The Ethereum Improvement Proposal repository
https://eips.ethereum.org/
Creative Commons Zero v1.0 Universal
12.73k stars 5.17k forks source link

ERC: Proxy Account #725

Closed frozeman closed 2 years ago

frozeman commented 6 years ago

This proposal has moved to ERC-725 (source).

This issue exists as an archive only.

Original Text ``` eip: title: ERC-725 Smart Contract Based Account author: Fabian Vogelsteller , Tyler Yasaka (@tyleryasaka) discussions-to: https://github.com/ethereum/EIPs/issues/725 status: Draft type: Standards Track category: ERC requires: ERC165, ERC173, ERC1271 (optional) created: 2017-10-02 updated: 2020-07-02 ``` **This is the new 725 v2 standard, that is radically different from [ERC 725 v1](https://github.com/ethereum/EIPs/blob/ede8c26a77eb1ac8fa2d01d8743a8cf259d8d45b/EIPS/eip-725.md). ERC 725 v1 is be moved to #734 as a new key manager standard.** -------- ## Simple Summary A standard interface for a smart contract based account with attachable key value store. ## Abstract The following describes standard functions for a unique smart contract based account that can be used by humans, groups, organisations, objects and machines. The standard is divided into two sub standards: `ERC725X`: Can execute arbitrary smart contracts using and deploy other smart contracts. `ERC725Y`: Can hold arbitrary data through a generic key/value store. ## Motivation Standardizing a minimal interface for a smart contract based account allows any interface to operate through these account types. Smart contact based accounts following this standard have the following advantages: - can hold any asset (native token, e.g. ERC20 like tokens) - can execute any smart contract and deploy smart contracts - have upgradeable security (through owner change, e.g. to a gnosis safe) - are basic enough to work for for a long time - are extensible though additional standardisation of the key/value data. - can function as an owner/controller or proxy of other smart contracts ## Specification ### ERC725X ERC165 identifier: `0x44c028fe` #### execute Executes a call on any other smart contracts, transfers the blockchains native token, or deploys a new smart contract. MUST only be called by the current owner of the contract. ```js function execute(uint256 operationType, address to, uint256 value, bytes data) ``` The `operationType` can execute the following operations: - `0` for `call` - `1` for `delegatecall` - `2` for `create2` - `3` for `create` Others may be added in the future. **Triggers Event:** [ContractCreated](#contractcreated) if a contract was created ### Events #### ContractCreated MUST be triggered when `execute` creates a new contract using the `_operationType` `1`. ```js event ContractCreated(address indexed contractAddress) ``` ### ERC725Y ERC165 identifier: `0x2bd57b73` #### getData Returns the data at the specified key. ```js function getData(bytes32 key) external view returns(bytes value) ``` #### setData Sets the data at a specific key. MUST only be called by the current owner of the contract. **Triggers Event:** [DataChanged](#datachanged) ```js function setData(bytes32 _key, bytes memory _value) external ``` ### Events #### DataChanged MUST be triggered when `setData` was successfully called. ```js event DataChanged(bytes32 indexed key, bytes value) ``` ### Ownership This contract is controlled by an owner. The owner can be a smart contract or an external account. This standard requires [ERC173](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-173.md) and should implement the functions: - `owner() view` - `transferOwnership(address newOwner)` - and the Event `event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)` ### Data keys Data keys, should be the keccak256 hash of a type name. e.g. `keccak256('ERCXXXMyNewKeyType')` is `0x6935a24ea384927f250ee0b954ed498cd9203fc5d2bf95c735e52e6ca675e047` #### Multiple keys of the same type If you require multiple keys of the same key type they MUST be defined as follows: - The keytype name MUST have a `[]` add and then hashed - The key hash MUST contain the number of all elements, and is required to be updated when a new key element is added. For all other elements: - The first 16 bytes are the first 16 bytes of the key hash - The second 16 bytes is a `uint128` of the number of the element - Elements start at number `0` ##### Example This would looks as follows for `ERCXXXMyNewKeyType[]` (keccak256: `0x4f876465dbe22c8495f4e4f823d846957ddb8ce6006afe66ddc5bac4f0626767`): - element number: key: `0x4f876465dbe22c8495f4e4f823d846957ddb8ce6006afe66ddc5bac4f0626767`, value: `0x0000000000000000000000000000000000000000000000000000000000000002` (2 elements) - element 1: key: `0x4f876465dbe22c8495f4e4f823d8469500000000000000000000000000000000`, value: `0x123...` (element 0) - element 2: key: `0x4f876465dbe22c8495f4e4f823d8469500000000000000000000000000000001`, value: `0x321...` (element 1) ... #### Default key values ERC725 key standards need to be defined within new standards, we suggest the following defaults: | Name | Description | Key | value | | --- | --- | --- | --- | | SupportedStandards | Allows to determine standards supported by this contract | `0xeafec4d89fa9619884b6b89135626455000000000000000000000000xxxxxxxx`, where `xxxxxxxx` is the 4 bytes identifier of the standard supported | Value can be defined by the standard, by default it should be the 4 bytes identifier e.g. `0x7a30e6fc` | | SupportedStandards > ERC725Account | Allows to determine standards supported by this contract | `0xeafec4d89fa9619884b6b89135626455000000000000000000000000afdeb5d6`, where `afdeb5d6` is the 4 bytes part of the hash of `keccak256('ERC725Account')` | Value is the 4 bytes identifier `0xafdeb5d6` | ##### ERC725Account An ERC725Account is an ERC725 smart contract based account for storing of assets and execution of other smart contracts. - `ERC173` to be controllable by an owner, that could be am external account, or smart contract - `ERC725X` to interact with other smart contracts - `ERC725Y` to attach data to the account for future extensibility - COULD have `ERC1271` to be able to verify signatures from owners. - Should fire the `event ValueReceived(address indexed sender, uint256 indexed value)` if ETH is received. A full implementation of an `ERC725Account` can be found [found here](https://github.com/ERC725Alliance/ERC725/tree/master/implementations/contracts). ## Rationale The purpose of an smart contract account is to allow an entity to exist as a first-class citizen with the ability to execute arbitrary contract calls. ## Implementation - [Latest implementation](https://github.com/ERC725Alliance/ERC725/tree/master/implementations/contracts) ### Solidity Interfaces ```solidity // SPDX-License-Identifier: CC0-1.0 pragma solidity >=0.5.0 <0.7.0; //ERC165 identifier: `0x44c028fe` interface IERC725X /* is ERC165, ERC173 */ { event ContractCreated(address indexed contractAddress); event Executed(uint256 indexed operation, address indexed to, uint256 indexed value, bytes data); function execute(uint256 operationType, address to, uint256 value, bytes memory data) external; } //ERC165 identifier: `0x2bd57b73` interface IERC725Y /* is ERC165, ERC173 */ { event DataChanged(bytes32 indexed key, bytes value); function getData(bytes32 key) external view returns (bytes memory value); function setData(bytes32 key, bytes memory value) external; } interface IERC725 /* is IERC725X, IERC725Y */ { } interface IERC725Account /* is IERC725, IERC725Y, IERC1271 */ { event ValueReceived(address indexed sender, uint256 indexed value); } ``` ## Flow chart ![ERC725v2-flow](https://user-images.githubusercontent.com/232662/57334038-996a8b00-70ec-11e9-9179-4dda3f30e09d.PNG) ## Additional References - [Slides of the ERC Identity presentation](https://www.slideshare.net/FabianVogelsteller/erc-725-identity) - [In-contract claim VS claim registry](https://github.com/ethereum/wiki/wiki/ERC-735:-Claim-Holder-Registry-vs.-in-contract) - [Identity related reports](https://www.weboftrust.info/specs.html) - [W3C Verifiable Claims Use Cases](https://w3c.github.io/vc-use-cases/) - [Decentralised Identity Foundation](https://identity.foundation) - [Sovrin Foundation Self Sovereign Identity](https://sovrin.org/wp-content/uploads/2017/06/The-Inevitable-Rise-of-Self-Sovereign-Identity.pdf) ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
overdrev commented 6 years ago

How about calling them 'Chink'. To look through a chink in a doorway give a tiny limited view, but nevertheless a true view. . 'Can you send me your passport chink please ' Key confuses to much in technical / crypto context IMO. And it's a word that people in the (English) street will be able to say. And it's just as good as a 'tweet' or other thing that has entered everyday speech...

frozeman commented 6 years ago

@tjayrush it kind of goes in this direction, yes. This standard itself doesn’t care if the claim issuer is centralized or decentralized. It only and will depend on how people will use it.

@andrewrd you interestingly use this standard very different. I for example would see the keys not as identities on the object itself. I would rather add a claim to the objects identity, which is then containing the ID.

RexShinka commented 6 years ago

@frozeman 1) Might I suggest combos rather than keys. It means the same thing but in a different way. A chink is a weakness (chink in the armor)

2) Would this make ENS names much more relevant? It seems so. Also, on the ENS gitter they mentioned adding an identity text string to the resolver (email, etc)

3 Am I correct in reading that if you lose your "combo" but not your wallet keys you can recover with new combos like in uport

4) I wrote an article on identity on my website (http://ethereumusecases.com/2017/10/05/identity-applications-use-case/). Would this standard complement or partially replace what uPort and Civic are doing? I think I read complement.

5) Finally by keeping a bit more on chain this may be a gas guzzler (relatively)

Fully open to speaking off line or whatever

kimdhamilton commented 6 years ago

@frozeman I'm co-chair of the group writing the DID spec and method spec template (W3C Credentials CG). I'll PM you to offer our help integrating this as a DID method spec.

frozeman commented 6 years ago

@kimdhamilton thanks, but the next three weeks will be busy (devcon3 upcoming)

@RexShinka i will take a look at your article. yes the idea is that some keys can replace keys, and others not, but are able to sign. This can only be a recommendation, as people aren't forced to use the standard as describes, they will still be compatible if they name the functions right, so security is only something this standard can recommend, not enforce.

Concerning gas cost. It shouldn't be more expensive than other on and off chain actions. The only cast costs incurred are here the adding and removing of keys and claims, which i don't expect to happen often.

In a few cases you will actually through your identity, but as this would be super visible, you probably wouldn't want to do that all the time, but just in some cases, like adding claims to others (e.g. if you're an institution)

Concerning uport and civic. Its up to them to adopt or support a standard or not, but they doing a great job when it comes to building UI and tools for identities. So if this standard (which ultimately is just an empty shell) gets adopted and used, due to its ease of use and functionality this will be adopted by them anyway.

frozeman commented 6 years ago

For reference, i create a wiki for collaboration on advantages and problems of claim registry vs attached claims: https://github.com/ethereum/wiki/wiki/ERC-735:-Claim-Holder-Registry-vs.-in-contract. This is mainly interesting for #735, but also relates to here obviously.

3esmit commented 6 years ago

Regarding approve function, its described to approve a claim or execution by n of m managers and maybe n of m actors. This makes Identity look like a MultisigWallet, while this is not a problem, I find it out of scope. Also, this would make all Identity following this ERC be cluggered by need of approvals. Having this design is good for many use-cases, specifically because most interactions envolves actions on behalf of an Identity.

In order to make Identity less specific, I would prefer isolating the permissioning rules, such as multisignaure, daily maximum values, and more (anything) in a separated contract for interfacing the permissions for that app/actor, for this I suggested a new ERC #745 The approve requirement would be specific from a MultisignedPermission.

Having this abstraction gives more freedom to dapps developers develop their own permission contracts, which need to be trusted (and open-sourced) by the dapps, while other logic could still be closed source without shading the permissions of that contract.

===================================
|  Dapp request permission to:    |
|  - call dapp.eth:buyService()   |
| up to 1 UNI per day.            |
|  - call otherapp.eth:doStuff()  |
|---------------------------------|
|  [Accept] [Read Src] [Reject]   |
===================================
3esmit commented 6 years ago

I see that specification wants that the same approve function approving different things, but the interface defined is incompatible. Should be all bytes32.

contract ERC725 {

    uint256 constant MANAGEMENT_KEY = 1;
    uint256 constant ACTION_KEY = 2;
    uint256 constant CLAIM_SIGNER_KEY = 3;
    uint256 constant ENCRYPTION_KEY = 4;

    event KeyAdded(address indexed key, uint256 indexed keyType);
    event KeyRemoved(address indexed key, uint256 indexed keyType);
    event KeyReplaced(address indexed oldKey, address indexed newKey, uint256 indexed keyType);
    event ExecutionRequested(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
    event Executed(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
    event Approved(bytes32 indexed executionId, bool approved);

    function getKeyType(address _key) public constant returns(uint256 keyType);
    function getKeysByType(uint256 _type) public constant returns(address[]);
    function addKey(address _key, uint256 _type) public returns (bool success);
    function removeKey(address _key) public returns (bool success);
    function replaceKey(address _oldKey, address _newKey) public returns (bool success);
    function execute(address _to, uint256 _value, bytes _data) public returns (bytes32 executionId);
    function approve(bytes32 _id, bool _approve) public returns (bool success);
}
alexvandesande commented 6 years ago

Guilherme: while I agree that the specifics of approval process should not be part of the standard and are out of scope I believe some “approve/execute” interfaces belong here.

Mostly I want, as a dapp developer, to answer the question: “how does my user executes/requires the execution of a contract call on behalf of his company/his identity/his artist name, etc

On 21 Oct 2017, at 03:31, Ricardo Guilherme Schmidt notifications@github.com wrote:

I see that specification wants that the same approve function approving different things, but the interface defined is incompatible. Should be all bytes32.

contract ERC725 {

uint256 constant MANAGEMENT_KEY = 1;
uint256 constant ACTION_KEY = 2;
uint256 constant CLAIM_SIGNER_KEY = 3;
uint256 constant ENCRYPTION_KEY = 4;

event KeyAdded(address indexed key, uint256 indexed keyType);
event KeyRemoved(address indexed key, uint256 indexed keyType);
event KeyReplaced(address indexed oldKey, address indexed newKey, uint256 indexed keyType);
event ExecutionRequested(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
event Executed(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
event Approved(bytes32 indexed executionId, bool approved);

function getKeyType(address _key) public constant returns(uint256 keyType);
function getKeysByType(uint256 _type) public constant returns(address[]);
function addKey(address _key, uint256 _type) public returns (bool success);
function removeKey(address _key) public returns (bool success);
function replaceKey(address _oldKey, address _newKey) public returns (bool success);
function execute(address _to, uint256 _value, bytes _data) public returns (bytes32 executionId);
function approve(bytes32 _id, bool _approve) public returns (bool success);

} — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

3esmit commented 6 years ago

@alexvandesande , I imagine that we would need something like this https://screenshots.firefox.com/FtDXF9fRryIfVBnD/github.com Each one of those 3 permission would be one smart contract (* or one to all 3) specifically to handle those permissions. That's because each one have totally different behavior, and they work like adapters between Identity and DApps. I'm sure this design can be adapted even with having a execute-approve in the Identity, but I see that all approve/execute would be possible to be done in a separated contract. For example, if we need the Management of Keys to be multisigned, aswell for the Claims.

In the case of Claims I would agree that would be handy to have inside ERC735 the approval of claims added by third parties, so if an allowed claimer start claiming you are something you don't approve, then this won't be added to your Identity. Of course this could also be done in a separate contract, but the process of addClaim->approveClaim are 1. not often, 2. not related to Dapp interaction, 3. fixed behavior.

frozeman commented 6 years ago

The way this standard is written allows for a lot of flexibility. In the approval process it only says SHOULD and COULD, but not MUST. This way this Standard can be implemented having no approval, having a n of m approval. Also 1 of 1 is the same as passing directly, e.g, for key additions and executions.

The claim addition itself in the context of an identity must definitely have at least one approval. While in ERC #735 it is not necessarily required. This is because a contract can allow claims about himself, without needing an approval.

Also this standard does not necessarily need to be implemented in exact detail, as long as the functions which other contracts interact are correct, it will be a working ERC 725. As described right now any interface should be able to deal with it automatically, if it has one/multiple or non-approval.

So therefore I am not 100 % convinced that #745 is necessary (also the ERC title is a bit to generic?)

frozeman commented 6 years ago

@3esmit Could you be more specific about the execution ID? Where do you say it is not the same? Thanks for your effort by the way.

frozeman commented 6 years ago

Could you be more specific about the execution ID? Where do you say it is not the same?

3esmit commented 6 years ago

@frozeman What is the function to approve a Claim? The same used for approval of executions? If so, would be better to have the execution id encoded as a bytes32, so I can check internally at approve what call regarding it is about. Otherwise, how approve function will be sure that the approve is about a Claim, and not by a coincidentally same pending id of an execute of addKey? Or will the uint256 addClaimId share the same nonce as the uint256 executionId? In bytes32 we could do something like keccak256("ClaimApproval",issuer,type") and keccak256("Execution", to, value, data), the strings in the encoding could be safely removed as would never collide even without them due different number of parameters.

About ERC745 the objective of it is removing the approve logic from this contract, or from any other, and standardize a permissioning contract that is understood by UI clients and even it's extendability would be better understandable. Encapsulating the permissioning logic make easier to audit the external permissions at our identity, and makes easier to have complex permissioning rules. The main problem with having the approval process inside identity is because the needs of complex permissioning will lead to users setting their security to low because the other options don't work or having extremely complex Identity contracts that deal with function signatures and different number of signatures needed depending on the call. One example of the problem of having these rules in ERC725 is multisig-wallet.sol with daily limits that was written before ERC20, and we have daily limits only for ETH.

I see that there is possible to support both a simple internal action-key permission logic, and use it as key into a complex permission contract as I'm describing in #745

3esmit commented 6 years ago

About Keys.. Can a Key (address) be simutaniously MANAGEMENT_KEY and ACTION_KEY or any other key? Or once an address is defined as a certain type of Key, it can only be that type of key? If so, why? If not, why not?

If willing to support one-to-many address->key we would need to change the functions removeKey and replaceKey to include aswell the key type. If not willing to support, it should be clear in the ERC that some error or other behavior would happen when trying to define a key to an address that is already an other type of key.

3esmit commented 6 years ago

I want to implement a Identity contract that contains no Multisig on it (instead I'll be using a Permission Contract is a Action Key), I also want to support this standard.

The problem is that function execute(address _to, uint256 _value, bytes _data) public returns (bytes32 executionId); does not return boolean. I could return executionId as 0x1 if success? approve function will have no behavior (maybe only approve claims).

Will my Identity contract still be compilant if it returns 0x1 at execute on success and 0x0 on error, and approve does nothing about transactions?

3esmit commented 6 years ago

Also, ERC725 is describing about function execute(address _to, uint256 _value, bytes _data) returns (uint256 executionId)

SHOULD require approve to be called with one or more keys of type 1 or 2 to approve this execution.

Returns executionId: MUST be send to the approve function, to approve or reject this execution.

It SHOULD require approval or it MUST require approval?

0xc1c4da commented 6 years ago

Hey guys! We've started the draft implementation of #725 and #735 in both Solidity and Viper. Special thanks to @jacqueswww and @3esmit for whipping them up!

https://github.com/status-im/contracts/tree/master/contracts

supertyler commented 6 years ago

Hi folks. Great to see this moving forward! We've done quite a bit of related research/dev over at Deloitte (https://github.com/SmartIdentity/smartId-contracts). Standardisation across identity implementations will be very useful, keen to contribute.

A few initial comments (and apologies in advance for not having mastered github formatting...):

Overall Scope Is this standard intended for use to represent corporate (or other non-human, non-machine identities)? I believe it is (or could be), and so I suggest extending the the initial abstract to include other entities, particularly organisations/groups.

Key loss management Agree with the approach to role specific keys. Could you outline thinking on mitigation of key loss? An action enabling recovery in the case of lost management/identity-owner keys may be needed. Something like the ability to nominate a set of override keys (m of n) which can invoke special actions such as "freeze" (in case the integrity of one or more keys is in doubt). Then for example if frozen, the only available action is "unfreeze", "replace management key", or "migrate" entire contract to new identity key (which requires further elaboration). I also like the uPort proxy model which overcomes some of these concerns.

Claims What is the rationale behind requiring approval of claims?

Negative claims: By requiring approval for externally submitted claims (and by allowing the identity owner to remove claims submitted by others) we go some way to preventing the storage of unwanted or negative claims. But we should recognise it will be trivial to develop alternative methods for asserting tracking and verifying negative claims in relation to an identity (indeed the revocation of a previously approved claim is a form of negative claim).

Claim data structure This is one area that concerns me. It looks like the proposed on chain data in relation to a claim could be used to identify:

IMO this is too much information to expose. I think the intent is to enable fully in-chain assertion and verification. There are scenarios where this may be required, but I think in most cases this model is not needed (and in some cases it could be a risk). Bear in mind we assume each user is carrying their key (probably on a phone), so why not store the sensitive attribute data in the same secure off chain location, with just the (merkle) hash of each claim on chain, then allow the user to choose when and where they reveal claims (either partially or in full).

In Europe we have regulation due to land in Early 2018 (GDPR) which will impose some considerable obligations on organisations in relation to customer data, one of which is the right of a customer to request removal/deletion of any personal data a company has stored in relation to them. Given claim transaction data will remain in the blockchain indefinitely, the question of how much data is revealed on chain needs to be very carefully evaluated. I'm not convinced an enterprise could comfortably adopt the currently proposed claim format.

Identity spam Any thoughts on the risk/impact of spam (claim) transactions? Options we considered included the ability to set a user defined, possibly refundable, fee for certain interactions with a contract.

Backward/forward compatability This also touches on the need for a migration action. The contract spec will likely evolve, people will want to upgrade and a way of linking existing claims with a new contract will likely be needed. Again would be good to understand thoughts/approach to this.

Would love to discuss in more detail...

naddison36 commented 6 years ago

How does someone reviewing an identity’s claims know what the claim types are? I suggest linking the identity contract to a claims registry with the following property.

claimTypeRegistry: address of the contract that registers claim types

The claimTypeRegistry can be a very simple contract like the following that emits an event rather than store data in the registry contract.

contract ClaimTypeRegistry
{
    uint256 claimTypeId = 0;

    event EmitClaimType(uint256 claimTypeId, string description);

    function registerClaimType(string description) public {
        EmitClaimType(claimTypeId++, description);
    }
}

The claimTypeRegistry could optionally include the schema of the claim data being signed.

The point here is different networks/communities/jurisdictions can agree on different claim types.

supertyler commented 6 years ago

@naddison36 (Re: How does someone reviewing an identity’s claims know what the claim types are?)

Unsure if this was a question to me, but if so, it's because - unless I am missing something - it is recorded in the claim structure (see ERC735) and could therefore be obtained through interrogation of the chain, or via getclaim () etc:

struct Claim {
    **uint256 claimType;**
    address issuer; // msg.sender
    uint256 signatureType; // The type of signature
    bytes signature; // this.address + claimType + data
    bytes data;
    string uri;
}

@frozeman to add to my prev comments, I've tried to re-summarise the point around privacy.

In general - It should not be possible for someone interrogating my identity to be able to identify either the signer of a claim, or the claim type, unless I have explicitly chosen to make this information to be available. I think the baseline should be 'very private' with options to reduce privacy where desirable within the same standard.

  1. Avoiding unwanted interrogation of claim types: I recommend claim type is not stored on chain, or is optional. If mandatory it will be useful if this can be set to either a generic or hashed value which can be used where claim type is private.

  2. Avoiding unwanted linkage between a claim issuer and receiver: Again I suggest 'signer' should not be mandatorily on-chain. Further, to avoid observation of the claims sent from 'a known claims issuer', it may be necessary to stipulate that a claim can only be received if it comes from a previously unused address (or the owning address). Since address pre-funding may also be an identifier, in future this would likely make use of 'contract pays' functionality.

Finally - can I check, would the handshake for authentication (receiving a challenge string, then signing and returning it) occur off chain (i.e. without introducing any new transactions into the blockchain), I think/hope this is the case, however at the London talk you mentioned the nightclub pays gas costs in this event. I would expect this to be an off-chain, zero-gas event. The alternative which I hope is not the case is that you are proposing each use of identity requires one or more tx's, which will introduce problems of scale, latency, cost, and most of all, privacy (all of which I think are avoidable for most usage scenarios)

frozeman commented 6 years ago

Thanks for all the contributions and sorry for the delay, but the last 4 weeks were rather busy with devcon3 in the middle. I will try to address all the issues mentioned above.

@3esmit concerning a required approve ID: This could certainly be also a bytes32, but i think a uint does the job as well, as it could be even prefixed with 10000000 or 2000000 for different approve types. E.g. key removal/additions vs executions or claim additions (10000000xxxx vs. 20000000xxxxx vs. 30000000xxx) If bytes32 provide a cheaper way of determining the approval type we can switch.

Concerning the #745 externalising the permissions could be a good idea, but would be also supported by the current draft as well, as you can set your approval to the 1 of 1 of a permission contract. A standard is also just a suggestion not an eforcement.

One key could have multiple roles, but i wouldn't recommend that, as you don't want to loose your ACTION_KEY and then end up loosing your MANAGEMENT_KEY too which can lock you out. But an ACTION_KEY could also be a CLAIM_KEY. Which is see as less of a problem.

The addition of the keytype is a good idea. i will change that.

Yes actually we can return a boolean and give the execution ID in the event. But as of now no return values of transaction can be read anyway using the receipts, as they aren't contained in there. The idea was that in the future we might add return values in the receipts and then it would be convenient. If you want to check if a execution was successful you should look for the event Executed anyway.

Concerning the MUST in the execute, it should be SHOULD :) will change that.

@jarradh thank you so much for that!! I made the change above (adding uint256 _type to removeKey and replaceKey) could you add that?

@supertyler thanks for your comments. Yes the standard should work for everything which can be identified. I will add organisations to the abstract.

Concerning key loss: Only one or multiple MANAGEMENT_KEYs could add or remove keys, action keys shouldn't, as these are the ones you carry around in your daily live. Also MANAGEMENT_KEYs could be a multisig of your council/family members, so that access loss is even impossible (as long as they don't loose theirs :) ) Freezing could happen when all action keys are removed.

About the negative claims. This is one of the key aspects of a self sovereign identity, that YOU are in control of it and you determine what goes on there. If a system for negative claims is needed this can be easily build as an external reputation system with different rules, or have registries for that. But if everybody can just add claims you have no control and there is no point of having a self controlled identity in the first place. Though you can always add more functionality and a whitelist of claim issues who go on without approval without being incompatible with this standard. Ultimately what counts is the interface and events to make this standard compatible with UIs and other smart contracts, there can be all kind of extra functionality. If the current spec is to strict we should loosen it to make it as flexible as possible.

Concerning privacy This is a good point. I see it as follows: If certain claims could lead to a privacy issue, there could be claim issuer aggregators which groups certain claim types. E.g. if you don't want anybody to know that you are a UK citizen, instead of accepting a claim from the UK government that they have your biometric data, you can choose to use one of newly created aggregator services, which are trusted (or ideally trustless) which claim that they have a claim from somebody that you have biometric data, and that they can route to the data storage securely if needed (or provide proof if you send biometric data points, like scanned eyes or fingers)

The need for the claim data structure is to allow on chain verification, yes.

But one of the claims can easily be pointing to your secrets claim storage via a merkle hash, which can be a DID JSON structure. So this standard allows for secrect as well as public claims, as well as public links. But ultimately it depends on the person or organisation of what he wants to be public.

I suggest also seeing this standard as less serious, Maybe my identity will be just a gamer profile, where i do want things to be public about me, and games and leagues to give me claims which are fully open. Or people do want to link their linkedin, or facebook via claims.

For all more long lasting claims which you want to be secret, off chain claim storages are the only solution currently. Maybe in the future this can be replaced by zero knowledge proof contracts.

Concerning GDPR, which is a good move the user gets all the control of what to put and what to remove, though the right to be forgotten is certainly not something the blockchain can provide. And these laws were made in the context of other holding and controlling our user data to force them to "really" delete data on request. This law will still be needed for claim issuer, as they will keep the actual verified data. The blockchain identity acts only as the reference pointer.

Concerning spam This is something the blockchain itself deals with due to the cost of transactions, but we could certainly set a limit of stored claim requests, maybe it will pop out the oldest one when a new one comes in. But i am not sure if that needs to be standardised. It can be suggested in the spec though.

Concerning upgradeability This is something which can be part of the contract (upgradeable contract), but you could also move to a new identity and link your old one, or go to the claim issuer again and ask for new claims to be attached to the new one. But also this doesn't need to be part of the standard. If we are talking about upgrading the standard itself, i would assume there will be a future standard which supersedes this one at one point.

@naddison36 this is an interesting idea, but i am not sure if thats needed on chain. In most cases you probably know which claim type and issuer you require to authenticate somebody. I wouldn't see a use case right now where you don't know the type and issuer but still want to put verify. But certainly something to be discussed.

@supertyler To the last comments: There is a use case for fully public and publicly linked claims, so it needs at least the structure i suggest above. But it probably shouldn't be mandatory, so then a claimType 10 could be secretClaims, which has no signer, but links to the encrypted datastore and the root hash of the storage. Or there are multiple storages. Issuer here could be yourself.

Though i see we have a problem here, as we can't have multiple claims with the same claimType and issuer. So either we would change the claim index structure, or allow subtypes through claimType high number prefixes 1000001 and 1000002 ...

Thanks for all the discussion and please keep in mind this standard can be used for serious and less serious cases (.e.g. gamer identity vs. real idenity). So we have to find a standard which is flexible enough to cover both.

naddison36 commented 6 years ago

@supertyler yes, you can get the claim type but that is just an integer. How would someone checking a claim know what claim type 34543 is? And what if the claimant changes what they think claim type 34543 is? My preference is to have an immutable record of what the claim types are.

3esmit commented 6 years ago

@frozenman thanks for the answer. I think, as we are adding the key type to all add, replace and remove key, we can let MANAGEMENT_KEY account also be ACTOR_KEY or other, as on removeal we would need to set the type aswell. For the replace would be interesting to not have type, instead move all keys in that acount to other account, or doing that when type is set to zero at replace.. Addkey and remove key type zero should error.

I see that using bytes32 in approval would be easier than checking arbitrary limited ranges in uint256, while in approval you need to check only which mapping contains that bytes32 value. Anyway, the best practice in programming is to one function do specifically one thing, and approving transactions is a completely different thing then approving a claim. I would find better to have a approveClaim in the ERC735 standard, then using the approve from ERC725 to ERC735 claims.

ERC745 can be used with this pattern yes, but having an approve method signature in ERC725 makes it looks like the code should be implemented at 725. And then if there some Identity using 745 some UI (like Mist) would not know what to do.

@alexvandesande for not having the approval in 725, the UI does not have to understand about approval regarding the identity, but only about reading the details about the Identity (such as management and actor key list); Usually human-individual-identities will use an externally owned account to interface with their identity, because they don't want to need the call of other to sign it (Most identites will be for persons), although paranoid users might want to use multiple signatures which is generally good for security.

The way UI will deal with it is user that want a MultiSig Permission would add (or create it through Mist) the Permission Contracts which their EOA are owner and included in the Actor Key list of Identity. Then is just like the Multisig Wallet by Foundation that is supported by Mist will look like, btw, it could be a Multisig Wallet. The UI would be able to understand the approval request coming from that MultiSig and also understand that it goes to that Identity, so, it would build a nice UI explaining what user is authorizing. This also would give UI a better understanding, while it could understand several of permission contract classes such as a "dead man switch" or "friends recovery" or "authority-based e-mail/sms recovering", etc. having custom build UIs for each class.

RexShinka commented 6 years ago

Pardon my noob question but how will this ECR evolve to an EIP and actually be coded. Will the result of this be a Solidity library? Does it need to be approved by someone, or once the dialog has settled, coding begins. Do you have a rough timeline?

Excellent work by the way. I am excited by where this ECR is going.

neuhaus commented 6 years ago

A provable identity provided by Ethereum would be very useful for projects such as commento, obliviating the need for central autorities for single signon! Consider me intrigued …

k06a commented 6 years ago

@frozeman what do you think about multi-identity? Looks like we implemented this idea in a common way recently: https://github.com/bitclave/Multiownable

gregorypro commented 6 years ago

While I applaud the effort, I have a few observations: 1) the Identity API proposed here, is very much Ethereum-tailored, hence my concern would be regarding the universally accepted Identity API. 2) How does this work relate to uPort and other Identity efforts? Are the companies like uPort, ShoCard, BlockAuth, ObjectTech, and so on, involved in the effort?

frozeman commented 6 years ago

@3esmit what you mean with "move keys to the other account"?

I would suggest not using the ACTOR key as MANAGEMENT key, to improve security, but when a key management contract is used, it could be one address, certainly.

I dont understand the bytes32, vs uint256, could you give an example? Whats the benefit, as i can also have a mapping with utint256 values.

Concerning the multiple approve functions: This is certainly an interesting idea, but i deemed it better to reduce the amount of functions and leave the logic to the approve function internally to figure out which operation to approve.

@k06a this looks interesting and if it solves the approve process in a clean and simple way, we could certainly use that. Though i didn't look at the details yet, could you write which functions need to be ported? And this could certainly another standard, where then #725 is build using these separate standards.

@gregorypro The smart contract is running on the Ethereum based blockchain, so it is in some way ethereum related. BUT i fully agree that it should be gerneric enough to work as reference all kind of off chain and other chain projects. Therefore i already added the signatureType to the claim structure to allow multiple type of keys. And we should certainly do the same for the keys itself. E.g. making keys a struct as follows:

struct Key {
    uint256 keyType;
    bytes32 key;
    uint256 type;
}

Concerning the other parties. I am in talks with uport and others, but ultimately this discussion is about a standard every of those projects can use, when they see there is a broad consensus. Systems which use commonly accepted standards win ultimately, so it will be up to them to contribute or come with a better standard.

3esmit commented 6 years ago

@frozeman I think I'm missing something here then, can you show me how I should fit the return of addClaim of #735 in approve defined by #725?

I see that 735 specifies:

function addClaim(uint256 _claimType, address issuer, uint256 signatureType, bytes _signature, bytes _data, string _uri) returns (bytes32 claimRequestId)

It returns (bytes32 claimRequestId)

while execute:

function execute(address _to, uint256 _value, bytes _data) returns (uint256 executionId)

it returns (uint256 executionId);

See, they are uncompatible. For me it's obvious how to approve execute using

function approve(uint256 _id, bool _approve) returns (bool success)

But how to do it fo the bytes32 claimRequestId?

Should I convert the bytes32 to uint256?

dzyk commented 6 years ago

ok. im want it))) its good idea. thx

frozeman commented 6 years ago

@3esmit thats a mistake on my side then. It should be uint256, as the event below is also uint256, i mixed the up between the claimID and the claimRequestId.

I corrected it.

monoman commented 6 years ago

Small typo. The description of addKey needs to be uptaded to reflect the greater number of key types now enumerated:

addKey Adds a _key to the identity. The _type specifies the type of key. Initially we propose two types:

addKey Adds a _key to the identity. The _type specifies the type of key. Initially we propose four types:

frozeman commented 6 years ago

IMPORTANT CHANGE

After speaking with @chriseth concerning multiple key types i made the following changes to the spec, to be discussed:

added getKey to return all the keys values (purpose, type, key) getKeyType -> getKeyPurpose and requires bytes32 as argument. getKeysByType -> getKeysByPurpose and returns array fo bytes32 addKey(bytes32 _key, uint256 _purpose, uint256 _type) etc. will receive a key in bytes32 format and also the type, while the old type is now purpose. Also getKeyPurpose will return an array, as there could be one key having multiple purposes.

I removed replaceKey for now, as its more of a convenient function and with these new 3 requires parameters, not short anymore.

There are also changed to #735 in the claim structure, in so far that signatureType is changed for scheme. A scheme number will determine how the claim can be validated, e.g. scheme number 25 could mean that data is a function call, and issuer a contract address. It can also have the meaning of a specific type of signature, or a specific way of how the actual claim data can be checked.

3esmit commented 6 years ago

Thanks, this changes makes lots of sense, especially for the new uses of off-chain technologies. Maybe I missed something but I would like to understand in the new design how we define a key that is another contract? I see that we can have a type specifically for handling this that would not use a signature, instead the address of msg.sender as valid signature, but then I think we should define this important case in the ERC, and probably should be the first type - and this case could be allowed to be used by externally owned accounts or require msg.sender codesize > 0?

mkovarsk commented 6 years ago

@frozeman @tjayrush I am the co-founder of The Humanized Internet and similarly we are looking at various scenarios working with the UN etc. Part of our team is someone who went through several of those scanarios and can speak to them. Scenarios include being A) captured and ability not to reveal real identity B) little to no smartphone penetration (slums and refugees) how do you capture an identity best ie fingerprint, facial recognition etc. C) Safety concerns with the UN is what data do we store and how do we make it bullet proof as history has shown us data revealed about people (whether color, religion) can and will be used in hardful ways. Food or thought and more than happy to discuss. Skype: mkovar01 or Cell (whatapp): 416-726-6746 . www.thehumanizedinternet.org

tjayrush commented 6 years ago

@mkovarsk I think this (protecting people from unscrupulous uses of this new "Identity-tech") is a super-important issue. I don't know how to help, but I support your efforts.

jamesray1 commented 6 years ago

They got their proof - without that they need to actually see the biometric data, nor the address, as they trust the issuer - and they can proceed in opening an account. —https://github.com/ethereum/EIPs/issues/725#issuecomment-333798370

This opens upp for the possiblitity of proving something about an account with a zkp without revelaing which identity owns that account.

As @oed said you could trustlessly use ZK-S(N/T)ARKs rather than trusting a claim issuer, which is important for Ethereum's decentralization ethos.

a.I would suggest a logic that permits explicit approval of claim by issuer - perhaps requiring an asynchronous process. @overdrev, all processes that operate via the internet (and by extension, most blockchains), need to be asynchronous.

Another thing we have though a lot about is the privacy of claims. Right now there is basically no privacy for claims that are on-chain, which is basically a no-go for a lot of our use cases.

@oed, with ZK-S(T/N)ARKs you can keep private ID on-chain while using it, but not disclosing it, e.g. one's physical address, biometrics, or things that can count toward KYC ID, e.g. debit cards, letters from institutions with your name and address, government issued ID cards, etc.

@frozeman, @Arachnid, @bneiluj I don't think it's necessary to limit keys to meaning a cryptographic key just because the word is in code. If this becomes a frequent concern then it could be changed to id_key, however I believe that the ERC has been updated to define what a key is, I don't think it's necessary to do this.

> @overdrev I’m not sure if a Claim permission process is needed, except when an entity wants to know the details of the claim, e.g. see the biometric data stored by the issuer. Then of course we need a async process. I have made some thoughts about that, but this is not really part of this ERC and can be a separate standard. I see it as follows: claim data (referenced by ‘bytes claim’) is double encrypted by the issuer and me, if somebody wants to see the actual data they request it at the issuers ‘bytes _location’ and request the issuers part to be decrypted, then they come to me and ask for decrypting the second part, which i can always decline.

b. Do we cover a single usage access method such that a third party can prove my info, but only once.

I think concerns like this are made redundant with ZK-S(T/N)ARKs. You don't need to disclose the ID to prove it, and why would you need to if the ZK-S(T/N)ARK proves the identity claim is correct? The proof only needs to be done once.

> Concerning privacy: as the claim data is only meta data, the only viable information one can retrieve from your identity is: which claim issuer has claims about me (e.g. the German state, or Vitalik claims something about me), and when that data has changed.

@frozeman, it's best to avoid leaking any data, including meta data. I think you really need to ensure privacy, in order to avoid manipulation of identity, e.g. with identity fraud. It's also important to have one identity per entity, in order to prevent Sybil attacks.

frozeman commented 6 years ago

As i see it this standard is good for your public profile, meaning the things you want people to be able to access and proof about yourself. Like we fill pages on linkedin with quite personal data, to display our experience.

If you want to have private claims, they can always be in a private (encrypted?) claim storage based on DIDs, which can actually be linked as one of your claims (So they perfectly fit in this standard as well)

So this allows for public and private data, and its up to you what you put where.

What this standard mainly solves is the root problem. Where do i go look for somebodies data. Or from where i can find all the other places (which could be access restricted).

Concerning the meta data, as i already wrote (i think), there will be claim issuer aggregator services poping up, which then obfuscate from which country you are etc, as they proof your residency, or biometric data, but do this globally for many people/entities.

Obviously the best way would be to have zksnark proofs linked in claims, which can be auto-verified without revealing anything.

The nice things is that this standard "container" allows for all of that together and it doesn't restrict what you put in the claims. Though we need to define claim schemes so the verification process is clear. See my additions in #735

tbocek commented 6 years ago

Here is the latest interface for this ERC

pragma solidity ^0.4.18;

contract ERC725 {

    uint256 constant MANAGEMENT_KEY = 1;
    uint256 constant ACTION_KEY = 2;
    uint256 constant CLAIM_SIGNER_KEY = 3;
    uint256 constant ENCRYPTION_KEY = 4;

    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, uint256 _purpose) public constant returns(uint256 purpose, uint256 keyType, bytes32 key);
    function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose);
    function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys);
    function addKey(bytes32 _key, uint256 _purpose, uint256 _type) 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);
}
frozeman commented 6 years ago

Thanks will add this to the ERC above

frozeman commented 6 years ago

i changed type to keyType. But i would actually like to rename that to type again, any ideas on that? Is there a case where it could conflict with soldities namespaces?

tbocek commented 6 years ago

@frozeman In ERC735 you have "scheme" for the type of the key:

scheme: The scheme with which this claim SHOULD be verified or how it should be processed. Its a uint256 for different schemes. E.g. could 3 mean contract verification, where the data will be call data, and the issuer a contract address to call (ToBeDefined). Those can also mean different key types e.g. 1 = ECDSA, 2 = RSA, etc. (ToBeDefined)
3esmit commented 6 years ago

@frozeman can you confirm keyType = 0 is used for contract calls as signature?

cbruguera commented 6 years ago

If now keys are able to be present with multiple purposes (inferring that from the fact that method getKeyPurpose returns an array), I would suggest renaming to plural getKeyPurposes.

JosefJ commented 6 years ago

@cbruguera on the same note, shouldn't the Key struct contain uint256[] purposes instead of just uint256?

Is there a case where new purposes can be added or selectively removed - isn't it simpler to remove the key completely and add it again just with new set of purposes? If it isn't justifiable, I would propose to change the uint256 purpose into uint256[] purposes in general across the contact.

EDIT1: Also, I don't think the same keys with different purposes should differ in type => function getKey(bytes32 _key, uint256 _purpose) could be changed just to function getKey(bytes32 _key)

Again, is there a case for multiple types on the same key?

EDIT2: @tbocek @frozeman I think the function removeKey() is missing from the interface. function removeKey(bytes32 _key, uint256 purpose) public returns (bool success); respectively: function removeKey(bytes32 _key) public returns (bool success);

JosefJ commented 6 years ago

I tried to naively implement the key and execution handling https://github.com/JosefJ/IdentityContract/blob/master/contracts/KeyHolder.sol The contract deviates from the so far stated description a bit as I applied some changes suggested in my last comment above.

frozeman commented 6 years ago

The idea was that you can get one key per purpose. Using getKey(key, purpose) you can know if the key exists. This key should then only return that one purpose. To get all the keys purposes there is the extra function getKeyPurposes. Otherwise one could say you dont need to getKeyPurposes function at all, if the getKey gives you all purposes directly.

My idea was that smart contracts can simply determine with one call, if key X of purpose Y exists. Which is needed when you verify a claim, and checking the key and its purpose on the claim issuer.

frozeman commented 6 years ago

Actually now reading @cbruguera comment again. The idea of getKeyPurpose() was to check if a key exists for a specific purpose.

We should think about a clean way to check that a key exists for a purpose, and having purposes returning an array, makes that tricky, as the smart contract would need to get the key and then check if the purpose is in the array, which is more costly. I would like to have one function to call for that.

frozeman commented 6 years ago

@JosefJ testing your code, i dont seem to be able to get the key added in the constructor: In remix: "call to KeyHolder.getKey errored: VM error: revert. revert The transaction has been reverted to the initial state. Note: The constructor should be payable if you send value. Debug the transaction to get more information. "