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

ERC-1484: Digital Identity Aggregator #1495

Closed
NoahZinsmeister opened this issue Oct 15, 2018 · 45 comments
Closed

ERC-1484: Digital Identity Aggregator #1495

NoahZinsmeister opened this issue Oct 15, 2018 · 45 comments
Labels

Comments

@NoahZinsmeister
Copy link
Contributor

NoahZinsmeister commented Oct 15, 2018

Official ERC-1484 Discussion Forum

EIP 1484
Title Digital Identity Aggregator
Author Anurag Angara [email protected]
Andy Chorlian [email protected]
Shane Hampton [email protected]
Noah Zinsmeister [email protected]
Discussions-To #1495
Status Draft
Type Standards Track
Category ERC
Created 2018-10-12
Requires 191

Simple Summary

A protocol for aggregating digital identity information that's broadly interoperable with existing, proposed, and hypothetical future digital identity standards.

Abstract

This EIP proposes an identity management and aggregation framework on the Ethereum blockchain. It allows entities to claim an Identity via a singular Identity Registry smart contract, associate it with Ethereum addresses in a variety of meaningful ways, and use it to interact with smart contracts. This enables arbitrarily complex identity-related functionality. Notably (among other features) ERC-1484 Identities: are self-sovereign, can natively support ERC-725 and ERC-1056 identities, are DID compliant, and can be fully powered by meta-transactions.

Motivation

Emerging identity standards and related frameworks proposed by the Ethereum community (including ERCs/EIPs 725, 735, 780, 1056, etc.) define and instrumentalize digital identity in a variety of ways. As existing approaches mature, new standards emerge, and isolated, non-standard approaches to identity develop, coordinating on identity will become increasingly burdensome for blockchain users and developers, and involve the unnecessary duplication of work.

The proliferation of on-chain identity solutions can be traced back to the fact that each codifies a notion of identity and links it to specific aspects of Ethereum (claims protocols, per-identity smart contracts, signature verification schemes, etc.). This proposal eschews that approach, instead introducing a protocol layer in between the Ethereum network and individual identity applications. This solves identity management and interoperability challenges by enabling any identity-driven application to leverage an un-opinionated identity management protocol.

Definitions

  • Identity Registry: A single smart contract which is the hub for all Identities. The primary responsibility of the Registry is to define and enforce the rules of a global namespace for Identities, which are individually denominated by Ethereum Identification Numbers (EINs).

  • Identity: A data structure containing all the core information relevant to an identity, namely: a Recovery Address, an Associated Addresses set, a Providers set, and a Resolvers set. Identities are denominated by EINs (incrementing uint identifiers starting at 1), which are unique but otherwise uninformative. Each Identity is a Solidity struct:

struct Identity {
    address recoveryAddress;
    AddressSet.Set associatedAddresses;
    AddressSet.Set providers;
    AddressSet.Set resolvers;
}
  • Associated Address: An Ethereum address publicly associated with an Identity. In order for an address to become an Associated Address, an Identity must either transact from or produce an appropriately signed message from the candidate address and an existing Associated Address, indicating intent to associate. An Associated Address can be removed from an Identity by transacting/producing a signature indicating intent to disassociate. A given address may only be an Associated Address for one Identity at any given time.

  • Provider: An Ethereum address (typically but not by definition a smart contract) authorized to act on behalf of Identities who have authorized them to do so. This includes but is not limited to managing the Associated Address, Provider, and Resolver sets for an Identity. Providers exist to facilitate user adoption by making it easier to manage Identities.

  • Resolver: A smart contract containing arbitrary information pertaining to Identities. A resolver may implement an identity standard, such as ERC-725, or may consist of a smart contract leveraging or declaring identifying information about Identities. These could be simple attestation structures or more sophisticated financial dApps, social media dApps, etc. Each Resolver added to an Identity makes the Identity more informative.

  • Recovery Address: An Ethereum address (either an account or smart contract) that can be used to recover lost Identities as outlined in the Recovery section.

  • Destruction: In the event of irrecoverable loss of control of an Identity, Destruction is a contingency measure to permanently disable the Identity. It removes all Associated Addresses, Providers, and optionally Resolvers while preserving the Identity. Evidence of the existence of the Identity persists, while control over the Identity is nullified.

Specification

A digital identity in this proposal can be viewed as an omnibus account, containing more information about an identity than any individual identity application could. This omnibus identity is resolvable to an unlimited number of sub-identities called Resolvers. This allows an atomic entity, the Identity, to be resolvable to abstract data structures, the Resolvers. Resolvers recognize Identities by any of their Associated Addresses, or by their EIN.

The protocol revolves around claiming an Identity and managing Associated Addresses, Providers and Resolvers. Identities can delegate much or all of this responsibility to one or more Providers, or perform it directly from an Associated Address. Associated Addresses/Providers may add and remove Resolvers and Providers indiscriminately. Associated Addresses may only be added or removed with the appropriate permission.

Identity Registry

The Identity Registry contains functionality to create new Identities and for existing Identities to manage their Associated Addresses, Providers, and Resolvers. It is important to note that this registry fundamentally requires transactions for every aspect of building out an Identity. However, recognizing the importance of accessibility to dApps and identity applications, we empower Providers to build out Identities on the behalf of users, without requiring users to pay gas costs. An example of this pattern, often referred to as a meta transactions, can be seen in the reference implementation.

Due to the fact that multiple addresses can be associated with a given identity (though not the reverse), Identities are denominated by EIN. This uint identifier can be encoded in QR format or mapped to more user-friendly formats, such as a string, in registries at the Provider or Resolver level.

Address Management

The address management function consists of trustlessly connecting multiple user-owned Associated Addresses to an Identity. It does not give special status to any particular Associated Address, rather leaving this (optional) specification to identity applications built on top of the protocol - for instance, management, action, claim and encryption keys denominated in the ERC-725 standard, or identifiers and delegates as denominated in ERC-1056. This allows a user to access common identity data from multiple wallets while still:

  • retaining the ability to interact with contracts outside of their identity
  • taking advantage of address-specific permissions established at the application layer of a user's identity.

Trustlessness in the address management function is achieved through a robust permissioning scheme. To add an Associated Address to an Identity, implicit permission from a transaction sender or explicit permission from a signature is required from 1) an address already within the registry and 2) an address to be claimed. Importantly, the transaction need not come from any particular address, as long as permission is established, which allows not only users but third parties (companies, governments, etc.) to bear the overhead of managing identities. To prevent a compromised Associated Address from unilaterally removing other Associated Addresses, it's only possible to remove an Associated Address by transacting or producing a signature from it.

All signatures required in ERC-1484 are designed per the ERC-191 v0 specification. To avoid replay attacks, all signatures must include a timestamp within a rolling lagged window of the current block.timestamp. For more information, see this best practices document in the reference implementation.

Provider Management

While the protocol allows users to directly call identity management functions, it also aims to be more robust and future-proof by allowing Providers, typically smart contracts, to perform identity management functions on a user's behalf. A Provider set by an Identity can perform address management and resolver management functions by passing a user's EIN in function calls.

Resolver Management

A Resolver is any smart contract that encodes information which resolves to an Identity. We remain agnostic about the specific information that can be encoded in a resolver and the functionality that this enables. The existence of Resolvers is primarily what makes this ERC an identity protocol rather than an identity application. Resolvers resolve abstract data in smart contracts to an atomic entity, the Identity.

Recovery

If users lose control over an Associated Address, the Recovery Address provides a fallback mechanism. Upon Identity creation, a Recovery Address is passed as a parameter by the creator. Recovery functionality is triggered in three scenarios:

1. Changing Recovery Address: If a recovery key is lost, an Associated Address/Provider can triggerRecoveryAddressChange/triggerRecoveryAddressChangeFor. To prevent malicious behavior from someone who has gained control of an Associated Address or Provider and is changing the Recovery Address to one under their control, this action triggers a 14 day challenge period during which the old Recovery Address may reject the change by triggering recovery. If the Recovery Address does not reject the change within 14 days, the Recovery Address is changed.

2. Recovery: Recovery occurs when a user recognizes that an Associated Address or the Recovery Address belonging to the user is lost or stolen. In this instance the Recovery Address must call triggerRecovery. This removes all Associated Addresses and Providers from the corresponding Identity and replaces them with an address passed in the function call. The Identity and associated Resolvers maintain integrity. The user is now responsible for adding the appropriate un-compromised addresses back to their Identity.

Importantly, the Recovery Address can be a user-controlled wallet or another address, such as a multisig wallet or smart contract. This allows for arbitrarily sophisticated recovery logic! This includes the potential for recovery to be fully compliant with standards such as DID.

3. Destruction
The Recovery scheme offers considerable power to a Recovery Address; accordingly, Destruction is a nuclear option to combat malicious control over an Identity when a Recovery Address is compromised. If a malicious actor compromises a user's Recovery Address and triggers recovery, any address removed in the Recovery process can call triggerDestruction within 14 days to permanently disable the Identity. The user would then need to create a new Identity, and would be responsible for engaging in recovery schemes for any identity applications built in the Resolver or Provider layers.

Alternative Recovery Considerations

We considered many possible alternatives when devising the Recovery process outlined above. We ultimately selected the scheme that was most un-opinionated, modular, and consistent with the philosophy behind the Associated Address, Provider, and Resolver components. Still, we feel that it is important to highlight some of the other recovery options we considered, to provide a rationale as to how we settled on what we did.

High Level Concerns
Fundamentally, a Recovery scheme needs to be resilient to a compromised address taking control of a user's Identity. A secondary concern is preventing a compromised address from maliciously destroying a user's identity due to off-chain utility, which is not an optimal scenario, but is strictly better than if they've gained control.

Alternative 1: Nuclear Option
This approach would allow any Associated Address to destroy an Identity whenever another Associated Address is compromised. While this may seem severe, we strongly considered it because this ERC is an identity protocol, not an identity application. This means that though a user's compromised Identity is destroyed, they should still have recourse to whatever restoration mechanisms are available in each of their actual identities at the Resolver and/or Provider level. We ultimately dismissed this approach for two main reasons:

  • It is not robust in cases where a user has only one Associated Address
  • It would increase the frequency of recovery requests to identity applications due to its unforgiving nature.

Alternative 2: Unilateral Address Removal via Providers
This would allow Associated Addresses/Providers to remove Associated Addresses without a signature from said address. This implementation would allow Providers to include arbitrarily sophisticated schemes for removing a rogue address - for instance, multi-sig requirements, centralized off-chain verification, user controlled master addresses, deferral to a jurisdictional contract, and more. To prevent a compromised Associated Address from simply setting a malicious Provider to remove un-compromised addresses, it would have required a waiting period between when a Provider is set and when they would be able to remove an Associated Address. We dismissed this approach because we felt it placed too high of a burden on Providers. If a Provider offered a sophisticated range of functionality to a user, but post-deployment a threat was found in the Recovery logic of the provider, Provider-specific infrastructure would need to be rebuilt. We also considered including a flag that would allow a user to decide whether or not a Provider may remove Associated Addresses unilaterally. Ultimately, we concluded that only allowing removal of Associated Addresses via the Recovery Address enables equally sophisticated recovery logic while separating the functionality from Providers, leaving less room for users to relinquish control to potentially flawed implementations.

Rationale

We find that at a protocol layer, identities should not rely on specific claim or attestation structures, but should instead be a part of a trustless framework upon which arbitrarily sophisticated claim and attestation structures may be built.

The main criticism of existing identity solutions is that they're overly restrictive. We aim to limit requirements, keep identities modular and future-proof, and remain un-opinionated regarding any functionality a particular identity component may have. This proposal gives users the option to interact on the blockchain using an robust Identity rather than just an address.

Implementation

The reference implementation for ERC-1484 may be found in NoahZinsmeister/ERC-1484.

identityExists

Returns a bool indicating whether or not an Identity denominated by the passed EIN exists.

function identityExists(uint ein) public view returns (bool);

hasIdentity

Returns a bool indicating whether or not the passed _address is associated with an Identity.

function hasIdentity(address _address) public view returns (bool);

getEIN

Returns the EIN associated with the passed _address. Throws if the address is not associated with an EIN.

function getEIN(address _address) public view returns (uint ein);

isAssociatedAddressFor

Returns a bool indicating whether or not the passed _address is associated with the passed EIN.

function isAssociatedAddressFor(uint ein, address _address) public view returns (bool);

isProviderFor

Returns a bool indicating whether or not the passed provider has been set by the passed EIN.

function isProviderFor(uint ein, address provider) public view returns (bool);

isResolverFor

Returns a bool indicating whether or not the passed resolver has been set by the passed EIN.

function isResolverFor(uint ein, address resolver) public view returns (bool);

getIdentity

Returns the recoveryAddress, associatedAddresses, providers and resolvers of the passed EIN.

function getIdentity(uint ein) public view
    returns (
        address recoveryAddress,
        address[] memory associatedAddresses, address[] memory providers, address[] memory resolvers
    );

createIdentity

Creates an Identity, setting the msg.sender as the sole Associated Address. Returns the EIN of the new Identity.

function createIdentity(address recoveryAddress, address[] memory providers, address[] memory resolvers)
    public returns (uint ein);

Triggers event: IdentityCreated

createIdentityDelegated

Preforms the same logic as createIdentity, but can be called by any address. This function requires a signature from the associatedAddress to ensure their consent.

function createIdentityDelegated(
    address recoveryAddress, address associatedAddress, address[] memory providers, address[] memory resolvers,
    uint8 v, bytes32 r, bytes32 s, uint timestamp
)
    public returns (uint ein);

Triggers event: IdentityCreated

addAssociatedAddress

Adds the addressToAdd to the EIN of the approvingAddress. The msg.sender must be either of the approvingAddress or the addressToAdd, and the signature must be from the other one.

function addAssociatedAddress(
    address approvingAddress, address addressToAdd, uint8 v, bytes32 r, bytes32 s, uint timestamp
)
    public

Triggers event: AssociatedAddressAdded

addAssociatedAddressDelegated

Adds the addressToAdd to the EIN of the approvingAddress. Requires signatures from both the approvingAddress and the addressToAdd.

function addAssociatedAddressDelegated(
    address approvingAddress, address addressToAdd,
    uint8[2] memory v, bytes32[2] memory r, bytes32[2] memory s, uint[2] memory timestamp
)
    public

Triggers event: AssociatedAddressAdded

removeAssociatedAddress

Removes the msg.sender as an Associated Address from its EIN.

function removeAssociatedAddress() public;

Triggers event: AssociatedAddressRemoved

removeAssociatedAddressDelegated

Removes the addressToRemove from its associated EIN. Requires a signature from the addressToRemove.

function removeAssociatedAddressDelegated(address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint timestamp)
    public;

Triggers event: AssociatedAddressRemoved

addProviders

Adds an array of Providers to the Identity of the msg.sender.

function addProviders(address[] memory providers) public;

Triggers event: ProviderAdded

addProvidersFor

Preforms the same logic as addProviders, but must be called by a Provider.

function addProvidersFor(uint ein, address[] memory providers) public;

Triggers event: ProviderAdded

removeProviders

Removes an array of Providers from the Identity of the msg.sender.

function removeProviders(address[] memory providers) public;

Triggers event: ProviderRemoved

removeProvidersFor

Preforms the same logic as removeProviders, but is called by a Provider.

function removeProvidersFor(uint ein, address[] memory providers) public;

Triggers event: ProviderRemoved

addResolvers

Adds an array of Resolvers to the EIN of the msg.sender.

function addResolvers(address[] memory resolvers) public;

Triggers event: ResolverAdded

addResolversFor

Preforms the same logic as addResolvers, but must be called by a Provider.

function addResolversFor(uint ein, address[] memory resolvers) public;

Triggers event: ResolverAdded

removeResolvers

Removes an array of Resolvers from the EIN of the msg.sender.

function removeResolvers(address[] memory resolvers) public;

Triggers event: ResolverRemoved

removeResolversFor

Preforms the same logic as removeResolvers, but must be called by a Provider.

function removeResolversFor(uint ein, address[] memory resolvers) public;

Triggers event: ResolverRemoved

triggerRecoveryAddressChange

Initiates a change in the current recoveryAddress for the EIN of the msg.sender.

function triggerRecoveryAddressChange(address newRecoveryAddress) public;

Triggers event: RecoveryAddressChangeTriggered

triggerRecoveryAddressChangeFor

Initiates a change in the current recoveryAddress for a given EIN.

function triggerRecoveryAddressChangeFor(uint ein, address newRecoveryAddress) public;

Triggers event: RecoveryAddressChangeTriggered

triggerRecovery

Triggers EIN recovery from the current recoveryAddress, or the old recoveryAddress if changed within the last 2 weeks.

function triggerRecovery(uint ein, address newAssociatedAddress, uint8 v, bytes32 r, bytes32 s, uint timestamp) public;

Triggers event: RecoveryTriggered

triggerDestruction

Triggers destruction of an EIN. This renders the Identity permanently unusable.

function triggerDestruction(uint ein, address[] memory firstChunk, address[] memory lastChunk, bool clearResolvers)
  public;

Triggers event: IdentityDestroyed

Events

IdentityCreated

MUST be triggered when an Identity is created.

event IdentityCreated(
    address indexed initiator, uint indexed ein,
    address recoveryAddress, address associatedAddress, address[] providers, address[] resolvers, bool delegated
);

AssociatedAddressAdded

MUST be triggered when an address is added to an Identity.

event AssociatedAddressAdded(
    address indexed initiator, uint indexed ein, address approvingAddress, address addedAddress, bool delegated
);

AssociatedAddressRemoved

MUST be triggered when an address is removed from an Identity.

event AssociatedAddressRemoved(address indexed initiator, uint indexed ein, address removedAddress, bool delegated);

ProviderAdded

MUST be triggered when a provider is added to an Identity.

event ProviderAdded(address indexed initiator, uint indexed ein, address provider, bool delegated);

ProviderRemoved

MUST be triggered when a provider is removed.

event ProviderRemoved(address indexed initiator, uint indexed ein, address provider, bool delegated);

ResolverAdded

MUST be triggered when a resolver is added.

event ResolverAdded(address indexed initiator, uint indexed ein, address resolvers, bool delegated);

ResolverRemoved

MUST be triggered when a resolver is removed.

event ResolverRemoved(address indexed initiator, uint indexed ein, address resolvers, bool delegated);

RecoveryAddressChangeTriggered

MUST be triggered when a recovery address change is triggered.

event RecoveryAddressChangeTriggered(
    address indexed initiator, uint indexed ein,
    address oldRecoveryAddress, address newRecoveryAddress, bool delegated
);

RecoveryTriggered

MUST be triggered when recovery is triggered.

event RecoveryTriggered(
    address indexed initiator, uint indexed ein, address[] oldAssociatedAddresses, address newAssociatedAddress
);

IdentityDestroyed

MUST be triggered when an Identity is destroyed.

event IdentityDestroyed(address indexed initiator, uint indexed ein, address recoveryAddress, bool resolversReset);

Solidity Interface

interface IdentityRegistryInterface {
    function isSigned(address _address, bytes32 messageHash, uint8 v, bytes32 r, bytes32 s)
        external pure returns (bool);

    // Identity View Functions /////////////////////////////////////////////////////////////////////////////////////////
    function identityExists(uint ein) external view returns (bool);
    function hasIdentity(address _address) external view returns (bool);
    function getEIN(address _address) external view returns (uint ein);
    function isAssociatedAddressFor(uint ein, address _address) external view returns (bool);
    function isProviderFor(uint ein, address provider) external view returns (bool);
    function isResolverFor(uint ein, address resolver) external view returns (bool);
    function getIdentity(uint ein) external view returns (
        address recoveryAddress,
        address[] memory associatedAddresses, address[] memory providers, address[] memory resolvers
    );

    // Identity Management Functions ///////////////////////////////////////////////////////////////////////////////////
    function createIdentity(address recoveryAddress, address[] calldata providers, address[] calldata resolvers)
        external returns (uint ein);
    function createIdentityDelegated(
        address recoveryAddress, address associatedAddress, address[] calldata providers, address[] calldata resolvers,
        uint8 v, bytes32 r, bytes32 s, uint timestamp
    ) external returns (uint ein);
    function addAssociatedAddress(
        address approvingAddress, address addressToAdd, uint8 v, bytes32 r, bytes32 s, uint timestamp
    ) external;
    function addAssociatedAddressDelegated(
        address approvingAddress, address addressToAdd,
        uint8[2] calldata v, bytes32[2] calldata r, bytes32[2] calldata s, uint[2] calldata timestamp
    ) external;
    function removeAssociatedAddress() external;
    function removeAssociatedAddressDelegated(address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint timestamp)
        external;
    function addProviders(address[] calldata providers) external;
    function addProvidersFor(uint ein, address[] calldata providers) external;
    function removeProviders(address[] calldata providers) external;
    function removeProvidersFor(uint ein, address[] calldata providers) external;
    function addResolvers(address[] calldata resolvers) external;
    function addResolversFor(uint ein, address[] calldata resolvers) external;
    function removeResolvers(address[] calldata resolvers) external;
    function removeResolversFor(uint ein, address[] calldata resolvers) external;

    // Recovery Management Functions ///////////////////////////////////////////////////////////////////////////////////
    function triggerRecoveryAddressChange(address newRecoveryAddress) external;
    function triggerRecoveryAddressChangeFor(uint ein, address newRecoveryAddress) external;
    function triggerRecovery(uint ein, address newAssociatedAddress, uint8 v, bytes32 r, bytes32 s, uint timestamp)
        external;
    function triggerDestruction(
        uint ein, address[] calldata firstChunk, address[] calldata lastChunk, bool resetResolvers
    ) external;

    // Events //////////////////////////////////////////////////////////////////////////////////////////////////////////
    event IdentityCreated(
        address indexed initiator, uint indexed ein,
        address recoveryAddress, address associatedAddress, address[] providers, address[] resolvers, bool delegated
    );
    event AssociatedAddressAdded(
        address indexed initiator, uint indexed ein, address approvingAddress, address addedAddress
    );
    event AssociatedAddressRemoved(address indexed initiator, uint indexed ein, address removedAddress);
    event ProviderAdded(address indexed initiator, uint indexed ein, address provider, bool delegated);
    event ProviderRemoved(address indexed initiator, uint indexed ein, address provider, bool delegated);
    event ResolverAdded(address indexed initiator, uint indexed ein, address resolvers);
    event ResolverRemoved(address indexed initiator, uint indexed ein, address resolvers);
    event RecoveryAddressChangeTriggered(
        address indexed initiator, uint indexed ein, address oldRecoveryAddress, address newRecoveryAddress
    );
    event RecoveryTriggered(
        address indexed initiator, uint indexed ein, address[] oldAssociatedAddresses, address newAssociatedAddress
    );
    event IdentityDestroyed(address indexed initiator, uint indexed ein, address recoveryAddress, bool resolversReset);
}

Backwards Compatibility

Identities established under this standard consist of existing Ethereum addresses; accordingly, there are no backwards compatibility issues. Deployed, non-upgradeable smart contracts that wish to become Resolvers for Identities will need to write wrapper contracts that resolve addresses to EIN-denominated Identities.

Additional References

Copyright

Copyright and related rights waived via CC0.

@tyleryasaka
Copy link

First of all, I appreciate the spirit of this as I also have written about the shortcomings of existing identity standards.

However, I will make a contention: Ethereum already has identity built in. It's called an address, and it's already fully supported by the entire ecosystem. I just published a medium post explaining this argument.

So we don't need to standardize identity. That said, there may be specific problems related to identity that can benefit from standards. For example, I think that universal login is a problem that could benefit from a standard. (See the blog post above or see my repo exploring a standard around this.) Claims are also a related problem, for which we already have ERC780. Some have criticized 780 for not being quite flexible enough, so I am also exploring a more flexible claims standard that is fully backwards compatible with 780 (again, see my repo if you're interested in this).

@AnuragHydro
Copy link

@tyleryasaka thanks for the response - reading through your articles, it's great to see a shared mindset of future-proofing the concept of identity management while remaining un-opinionated about its particular execution.

I do think that the challenge around relying on a single user-controlled address is that an address is really optimized for one function, which is governance - signing messages for state changes to the ethereum blockchain. It's not natively optimized to resolve an Identity as an entity to multiple data structures in a meaningful way.

What we're aiming to do here is to allow user-controlled "first-class citizenship" in application of the essentials you outline in your second article while allowing identity applications flexibility for customized solutions to the challenges mentioned in your first article: namely, implementation-specific coupled or decoupled address management functions, claims approaches, or even broader identification schemes such as reputation-driven ones.

The real purpose of a standardized scheme for Identity interoperability is to bridge the gap between what recognizing an entity by an Identity provides versus what recognizing an entity by a given address provides in terms of usability, multiple address linkage, and recovery.

@alexandermuehle
Copy link

Why did you introduce EINs instead of using DIDs?

@morelazers
Copy link

@alexandermuehle I believe that current DID proposals treat a single Ethereum address as a single identity, which is what this proposal is attempting to avoid.

@alexandermuehle
Copy link

@morelazers I'm not quite sure I get your point, while there should be only one authoritative DID Document per DID, that document can include various public keys of different types and purposes, including Ethereum addresses or what do you mean by treating an Ethereum address as a single identity?

@AndyHydro
Copy link

@alexandermuehle we are currently working on the DID specification for this ERC and have an initial draft that we plan to publish soon. This implementation still allows for DIDs to exist. We believe that using a persistent, non-address identifier (EIN) as the core of an ERC-1484 DID document makes more sense than an address. The DID is generated through the recovery address and would look something like did:erc1484:2F2B37C890824242Cb9B0FE5614fA2221B79901E#225 where the address is the checksummed recovery address and the 225 is the users EIN.

@cbruguera
Copy link

@AndyHydro I do see EINs as a better representation for IDs than addresses, and I can see one or more DID methods could be implemented on top of this protocol. However, I don't quite understand why is the recovery address a part of the DID. Since recovery addresses can be rotated for a single identity, that would defeat the persistence feature of DIDs... Wouldn't it make more sense to make it dependable only on the EIN? e.g. did:erc1484:225 (or a fixed-size hex representation of the EIN)?

@AnuragHydro
Copy link

AnuragHydro commented Oct 24, 2018

@cbruguera that's a good point - since the EIN is globally unique, I think a hex-encoded EIN is the most straightforward and persistent DID representation. I've updated the DID method to reflect this. Let us know if you've got any further thoughts on this!

@cbruguera
Copy link

cbruguera commented Oct 25, 2018

@AnuragHydro Another arising thought is that an EIN is "globally" unique in the universe pertaining to the EIN registry contract. This assumes there is only one global ERC1484 implementation up and running on the network, which might be ideal but apparently not enforceable. I can't come up now with a good reason, but what if there are multiple contract instances (or versions), each one with its own EIN counter?... Would it then make sense to have the contract address (or some other distinctive feature) take part in corresponding DID generation?

Examples:

a. A keccak hash of: the contract address concatenated with (a fixed hex representation of the) EIN
b. A composite DID in the form: did:erc1484:<contract_address>:<_EIN>

(At first, it looks like option b would be more practical, since it provides useful info that would be obfuscated in the first option)

...Any thoughts on this?

@AnuragHydro
Copy link

@cbruguera That's a reasonable concern (although it would certainly be ideal to standardize usage of a single deployment of the IdentityRegistry). I also don't see a reason to hash the contract address, but I do like the idea of using did:erc1484:<network>:<contract_address>:<_EIN> (option b). I've updated the DID Method again to reflect this convention 👍

@AnuragHydro
Copy link

For those following the reference implementation, it has now been updated to include best practices for resolvers recognizing EINs, example resolvers under the ERC 725 and 1056 standards, and an example meta-transactions provider

@xrn
Copy link

xrn commented Oct 28, 2018

@AnuragHydro I have the question maybe I am wrong but we will see... A universal login idea has one very powerful feature this means user funds are stored on smart contract balance and then owners (multiple - checked via signatures) can sign requests to spent this money (eth, tokens). The idea with relayers is pretty good too - even if do not have any funds on wallet we still can make some transactions.

You know this topic probably best then I but this is very useful (from other hands very trackable). A user can connect many devices to 1 contract and do not have to remember about private key backups etc. A user has access to all my funds assigned to given wallet from any place.

To summarize - what do you think to add one more array of actors like "treasury" to existing "providers" and "resolvers" which have access to spent money from special contracts deployed per user?

@NoahZinsmeister
Copy link
Contributor Author

NoahZinsmeister commented Oct 28, 2018

We'd like to request some specific feedback on the signature scheme in 1484, specifically the form of requested signatures and the steps taken to prevent replay attacks. This best-practices document is suggested reading.

Format

Right now, signatures are formatted per ERC191 v0. This means that addresses are required to sign the hash of <standard 191 data, an authorization string, authorization-specific variables> to perform various identity-related actions (see the identity creation code for an example). While there are many reasons we prefer this v0 scheme, some community members prefer v1, which is fleshed out in ERC #721. Other alternatives are also possible.

Preventing Replay Attacks

To prevent replay attacks, the implementation requires that all signatures include a timestamp which is enforced to be within a day of the current block timestamp. This ensures that there is a one day timeout for (properly) signed messages to retain their ability to authorize identity-related actions. For more context, see the Signature Timeout section of this comment.

Are there any thoughts or opinions on either of these issues?

@NoahZinsmeister
Copy link
Contributor Author

NoahZinsmeister commented Oct 28, 2018

@xrn thanks for the suggestion! We discussed this issue, and ultimately decided to remain agnostic about these specific types of issues at the Registry level, for the following reasons.

One specific use of the Associated Addresses data structure might be to treat each as a joint owner of a common repository of ETH/tokens/etc. This way, a Resolver could incorporate the ability for any Associated Address to transact value on behalf of its associated Identity.

We also fully expect more sophisticated delegation/treasury patterns to spring up on a per-Provider level. For example, a Provider that we have been working on includes the ability for 1484 Identities to deposit tokens to their identities, and offers functions to Resolvers that allow them to manipulate these token balances (with the proper authorization).

Since these types of patterns and more may or may not require Resolvers/Providers to store additional per-Identity information, we view the current structure as appropriately balancing a) storing enough useful and generic information centrally in the Registry while b) enabling arbitrarily sophisticated use cases in the future.

Does that address your concerns?

@cbruguera
Copy link

I agree that the identity protocol should remain agnostic with regard to token/ETH dynamics.

The aforementioned functionality can be achieved through new token contract protocols, or via custom Resolver/Provider implementations as pointed out by @NoahZinsmeister. Moreover, my personal take on the matter is that the capability of shared holding of ETH/tokens in this way is not as valuable as being able to associate "claims" to multiple addresses. i.e. if you control multiple addresses, you already control the ETH/tokens held among them, it's a trivial matter to transfer money among addreses, but signed claims about a subject cannot (or shouldn't) be "transferred" in such a way.

@NoahZinsmeister
Copy link
Contributor Author

NoahZinsmeister commented Nov 2, 2018

In addition to requesting feedback on signatures, we'd also like to solicit opinions on the value of the constants in ERC-1484.

Max Associated Addresses

uint public maxAssociatedAddresses = 50;

This constant controls the maximum number of associatedAddresses an Identity can have at any given time. The reason this limit must exist is because when calling triggerRecovery, the current members of the associatedAddress set are operated on to facilitate the triggerPoisonPill process.

  • This is because to call triggerPoisonPill, a user needs to know the ordered list of associatedAddresses at the time of last recovery. Logging the addresses straightforwardly addresses this problem. Some gas could be saved by omitting this logging, at the expense of usability, since the associatedAddress array would then have to be reconstructed from prior event logs.

In order to ensure that triggerRecovery never fails, we need to ensure that the number of associatedAddresses is never so high that these operations hit the block gas limit. Based on preliminary calculations, the increased cost per address is ~36k gas, with a fixed cost of ~175k gas. This means that triggering recovery from an Identity with 50 addresses would cost ~2 million gas, which seems reasonably within expected future gas block limits.

We would love for someone to externally validate these calculations.

Signature Timeout

uint public signatureTimeout = 1 days;

This constant controls the amount of time that a timestamped signature remains valid for. This must be low enough that the probability of replay attacks within the window is acceptably low, and high enough to ensure that the number of blocks for which a signature is valid is high enough such that transactions can be broadcast and confirmed reliably.

We would love feedback on this, specifically around potential replay attacks within the window for each instance in which ERC-1484 requires a signature.

Recovery Timeout

uint public recoveryTimeout = 2 weeks;

This constant controls the amount of time which must elapse between:

  • Changes in recoveryAddress. This timeout is necessary to give the old recoveryAddress enough time to be able to dispute malicious changes in recoveryAddress by calling triggerRecovery.
  • Triggered Recoveries. This timeout is necessary so that if a malicious address ends up calling triggerRecovery, any of the removed associatedAddresses have enough time to be able to irrevocably dispute this recovery by calling triggerPoisonPill.

We would love feedback on these two scenarios.

@cbruguera
Copy link

@AnuragHydro I have a few questions with regard to current work on ERC1484:

  • In the reference implementation, what's the purpose of storing recovery address change structs in RecoveryAddressChangeLogs (why not rely on emitting events as a method for logging)?
  • Are existing instances of ERC1056 or ERC725 able to be integrated as Resolvers for ERC1484 identities, or do these specific identity solutions have to be re-implemented with specific logic that allows them to function as ERC1484 resolvers?
  • Is there a work being done for implementing a DID resolver for ERC1848?

@NoahZinsmeister I pretty much agree on the constants proposal. Yet I think in order to be as least restrictive as possible, we should maximize the maxAssociatedAddresses to a point closer to hitting the maximum block gas limit for the involved methods. For example, current maximum block gas limit is about 8 million, if my googling is correct, so there's still significant room for a higher number of addresses.

With regard to recovery timeout, I'm thinking 1 week could be enough, but there's no concrete reasoning behind that number, more than just an intuitive guess. Looking forward to know other opinions on the matter.

@AnuragHydro
Copy link

@cbruguera

In the reference implementation, what's the purpose of storing recovery address change structs in RecoveryAddressChangeLogs (why not rely on emitting events as a method for logging)?

The recoveryAddressChangeLog is to enable recourse if a compromised Associated Address attempts to change the Recovery Address. This enables the check within the recovery function to ensure that Recovery can be called within recoveryTimeout.

Are existing instances of ERC1056 or ERC725 able to be integrated as Resolvers for ERC1484 identities, or do these specific identity solutions have to be re-implemented with specific logic that allows them to function as ERC1484 resolvers?

@AndyHydro - can you elaborate in more detail regarding methods in the 725 and 1056 examples to port existing instances of each respective resolver?

Is there a work being done for implementing a DID resolver for ERC1848?

At the moment, we only have the DID method - we can look into creating a DID resolver to fetch data from the DID method - do you have any specific considerations on this front?

@cbruguera
Copy link

@AnuragHydro With regard to DID methods, I think it'd be more precise to ask what is the role of a DID method for this proposal... Since this is sort of an abstraction layer that can encompass existing Ethereum-based identity schemes such as ERC1056 and ERC725 (as far as I understand), how does ERC1484 integrate or substitute (DID-wise) these other platforms?

There are DID methods for ERC725 and ERC1056. Does enveloping these identities through an ERC1484 EIN make these other DID methods unnecessary? Or is there any reason for these multiple DID methods to coexist or hold any relationship among them?

@AnuragHydro
Copy link

@cbruguera I don't think that any particular DID method replaces the need for another - they are just used to locate their corresponding on-chain identifiers and construct any associated identity information accordingly. It's true that an EIN is informative about a 725 or 1056 identity; for instance, using did:erc1484:<contract-address>:<ein>, you can get the EIN's corresponding 725 or 1056 public profiles from the respective registry and construct the DID that way, but I think it's better for each identity application to maintain an authoritative DID method independent of ERC 1484 for two main reasons:

  1. Static DID Method: If the 1484 DID method were to assume the role of a universal Ethereum DID, it would need to be dynamic, with a mechanism to update nonstandard information to the DID doc with each aggregated identity application. Since these docs are constructed off-chain, the DID method would need to include centralized update procedures for each new aggregated identity application to remain authoritative.

  2. Independent Identity Application Services: certain DIDs leverage service endpoints with off-chain customized functionality that may not directly tie to ERC 1484 functionality. Forcing these service endpoints into an ERC 1484 DID method may prove restrictive.

Instead, I'd posit that the 1484 DID method is useful for making non-Ethereum-based DIDs resolvable to the Ethereum blockchain. For instance, an identity application built on non-ethereum structure may have a service endpoint accepting a signature from an Associated Address in order to authoritatively add the corresponding EIN into its own DID doc. This would allow an off-chain digital identity structure to remain resolvable to an EIN insofar as the service endpoint and off-chain infrastructure is trusted.

@cbruguera
Copy link

@AnuragHydro It makes total sense not to try turning the ERC1484 DID method into an "all-encompassing" method, for the reasons provided. However, I'm having a bit of trouble understanding your last example. Can you expand it further?... In this scenario is the identity owner the "controller" of both the ERC1484 EIN and the off-chain service? Does the off-chain service have a DID that is resolvable to an EIN of its own? Or is this about delegating "keys" among different DID methods? What does "adding the corresponding EIN into its own DID doc" mean exactly?

@cbruguera
Copy link

On a different topic, I've been thinking of the potential limitations of having the EIN be a simple and (predictably) incremental counter. Although at this time no specific privacy-threat scenario comes to mind, certainly in the current proposal an EIN by itself reveals a minimum degree of information (i.e. chronological order). I was wondering if it makes sense to "randomize" the generation of EINs, so that there's no explicit order relationship among them. For example: the counter can still be used as a "nonce", and EINs could be generated as a 32-byte hash of that nonce concatenated with some other info (such as the caller's address, etc.)

That would ensure uniqueness of identifiers, plus a bit more of privacy (I guess anyone can still resolve the EINs and obtain public info such as date of creation, yet the identifiers per se reveal nothing). I'd just like to know what is the overall view on this matter, and if it's considered actually relevant or not.

@AnuragHydro
Copy link

@cbruguera

In this scenario is the identity owner the "controller" of both the ERC1484 EIN and the off-chain service?

Yes - the off-chain service would use an Associated Address signature to prove this.

Does the off-chain service have a DID that is resolvable to an EIN of its own? Or is this about delegating "keys" among different DID methods? What does "adding the corresponding EIN into its own DID doc" mean exactly?

Off-chain service can be as opinionated or unopinionated as it chooses. An example would be building an identity profile on hyperledger indy; with an Associated Address signature, all of the functionality for the identity would maintain integrity on "non-ethereum" service while remaining resolvable to an Ethereum EIN, proving a persistent user (insofar as the off-chain infrastructure is trusted). I hope that makes more sense!

On a different topic, I've been thinking of the potential limitations of having the EIN be a simple and (predictably) incremental counter. Although at this time no specific privacy-threat scenario comes to mind, certainly in the current proposal an EIN by itself reveals a minimum degree of information (i.e. chronological order). I was wondering if it makes sense to "randomize" the generation of EINs, so that there's no explicit order relationship among them. For example: the counter can still be used as a "nonce", and EINs could be generated as a 32-byte hash of that nonce concatenated with some other info (such as the caller's address, etc.) That would ensure uniqueness of identifiers, plus a bit more of privacy (I guess anyone can still resolve the EINs and obtain public info such as date of creation, yet the identifiers per se reveal nothing). I'd just like to know what is the overall view on this matter, and if it's considered actually relevant or not.

Personally, I do not see this as a big concern; since EINs are generated on-chain anyways, the order in which they were generated is already public regardless of whether uints vs random identifiers are used. Not sure if anyone else feels strongly on this.

@cbruguera
Copy link

Can I suggest considering a little less "allegorical" name for the "poison pill" functionality? Perhaps something more technically accurate such as disable, destroy, etc.?

@zhous
Copy link
Contributor

zhous commented Nov 27, 2018

This is great. And alos it's what I have being trid for months. Now I need some time to read the proposal, my English is slow. Anyway, good job!

@AnuragHydro
Copy link

@cbruguera sure thing! changed to "destroy"

@cbruguera
Copy link

Basic question here: So far it seems that associated addresses for ERC1484 identities are "flat", meaning there's no hierarchy or custom permissioning set on these addresses. How would a hierarchical (or any other sort of complex permissioning) scheme be implemented over ERC1484 identities? How would this scheme compare to that of ERC725 for example? or How would you describe the possible integration between ERC1484 and ERC725 (or similar) to achieve this level of "complex identities" (e.g. Identity structure for corporate entities, etc)...?

@AnuragHydro
Copy link

AnuragHydro commented Dec 18, 2018

@cbruguera
Good question! This is a good example of the two ERCs working well in conjunction with one another. You are correct in that ERC 1484 simply links addresses for a given EIN and does not, by itself, define the nature of the relationship between those addresses.

The ERC 734 address-management structure defines the nature of the relationship between keys, which may be useful in some instances while not useful in others. If a user (or a dApp for its users) wants to add informativeness about the nature of the relationship between keys, it would simply set the desired key-management structure as a resolver for the EIN. In this case, I see that an ERC 734 resolver contract defines the key-management structure for a user of my dApp, so I drive logic in my dApp accordingly. Someone else with a different dApp prefers a different key management structure for their users, so they have the users set a different resolver. The second dApp drives their logic based on information from a different resolver for the same user which defines key management differently. Here, we'd have two identity applications aggregated for the same end-user, driving their interactions with different platforms. A third dApp could piggyback on one of the existing key-management structures or define its own, per its custom needs.

Ultimately, the objective is always to apply informativeness at the resolver level.

@cbruguera
Copy link

@AnuragHydro thanks for your reply. I guess my question is more about control than "informativeness". More than depicting the relationships between different addresses, I'm trying to figure out how to implement a hierarchy (or other more complex settings) for EIN/DID permissioning. So, I'm guessing part of the permissioning structure for an identity would be dApp-specific and therefore be represented through the corresponding resolvers as you stated, yet actual control over the identity would be implemented at the Provider layer instead?... On the other hand, all associated addresses at the ERC1484 level seem to have full control, meaning we can regard them all as "management keys" (using the ERC725 terminology), is that correct? I'm still trying to wrap my head around how would a corporate identity look like on ERC1484 (e.g. a single identity comprising of different departments, each one probably with its own EIN, yet some of them authorized or not to perform certain operations, same as keys within each department, etc..)

On another topic, I can't see any info on address-management on the link you provided for ERC734, so I'm not sure what is it exactly. Is it a way to reference both ERC725 and ERC735?

@cbruguera
Copy link

I'm still trying to wrap my head around the concept of a "Resolver" in the context of ERC1484... It was my understanding that these were smart contract addresses that were able to aggregate arbitrary data (or functionality) associated to an EIN, however, in the ERC1484 proposal and reference implementation resolvers is just an array of addresses. There's no mnemonic indexation or discoverability mechanism tied to these resolvers, so how can relying parties know which of these addresses contain any valuable information about the identity? Wouldn't it make more sense to store resolvers in a mapping structure?

Perhaps there's something I'm missing about the resolver idea, but looking at the ERC725 example, it's just a contract that retrieves an EIN from the ERC1484 instance and associates it internally, yet there seems to be no point in having the ERC725 instance address listed as a "resolver" on the ERC1484 registry side... Can you provide a little more insight into what is the role of resolvers on this protocol?

@ghost
Copy link

ghost commented Jan 2, 2019

Hey @cbruguera, great questions. Some comments:

...all associated addresses at the ERC1484 level seem to have full control, meaning we can regard them all as "management keys" (using the ERC725 terminology), is that correct?

That is correct! This is the most neutral position we were able to come up with. From the point of view of 1484, all associated addresses are equal.

...yet actual control over the identity would be implemented at the Provider layer instead?

One way to implement further permissioning on top of the associated addresses would certainly be at the provider layer, as you suggested. This could mean that a provider maintains specific permission structures/delegated call mechanics/etc. You could for example write a provider s.t. any resolver wishing to work in your ecosystem only accepts delegated calls or looks up address permissioning from your provider.

...part of the permissioning structure for an identity would be dApp-specific and therefore be represented through the corresponding resolvers...

Another way to implement permissioning would be at the Resolver level. This could mean that instead of relying on associated addresses, a resolver (or provider) could instead enforce that an EIN has added another resolver which contains ERC-725 data, and use that resolver's data to permission addresses.

There's no mnemonic indexation or discoverability mechanism tied to these resolvers, so how can relying parties know which of these addresses contain any valuable information about the identity?

That is the case, which is both good and bad. It's good in the sense that resolvers are just arbitrary addresses, it's bad in that like you said, there's no other metadata attached to resolvers, so they have to be interpreted on a case-by-case basis.

...there seems to be no point in having the ERC725 instance address listed as a "resolver" on the ERC1484 registry side...

This is definitely mostly the case, and gets to the core issue. The resolvers array is really only a) to help front-end identity aggregators know which identity applications are currently active for a given EIN, and b) to add an element of user-controlled permissioning to their identity. Resolvers are really only useful in so far as there is custom logic on either a smart contract or front-end that utilizes them in specific ways. Users gain some level of control over this process by being able to add/remove resolvers at will.

It's hard for me to comment more specifically without knowing a bit about the potential use cases you're looking to implement, but hopefully this helps, let me know if there's anything else you'd like to discuss!

@cbruguera
Copy link

@NoahHydro Thanks for your reply. I still think there's a functional gap in the idea of resolvers. The way you describe it (and as it's presently implemented) it looks like the assumption is being made that applications will know the resolver addresses beforehand from which they might be able to obtain the corresponding EIN of an identity and handle it internally as desired. The use case I have in mind, though, is quite the opposite: where a dApp knows an entity's EIN and then wishes to obtain associated data (e.g. ERC735 claims). Moreover, the "relying party" in this case could be another smart contract, which would have even more limitations than a user facing application. Yet in the current setup there's no way to traverse that relationship parting from an EIN since there's no meta-data on added resolvers.

If EINs are supposed to work as sort of universal identifiers for Ethereum (being ERC1484 a protocol intended to stand on top of other on-chain identity solutions), it's my view that it'll be quite common (and a desirable feature in fact) that an EIN is the only attribute to be shared and stored among different apps and contracts, from which other relevant attributes should be discoverable. Otherwise, I don't think it's working much as an actual identity aggregator.

Still, there's a huge chance I'm misunderstanding the actual purpose of resolvers in this scheme.

@ghost
Copy link

ghost commented Jan 3, 2019

@cbruguera just sent you a message in the ERC 1484 telegram! If we work something out there or in DMs that's of general relevance, I'll post a full recap here and in telegram.

@NoahZinsmeister
Copy link
Contributor Author

FYI for all:

The latest ERC-1484 implementation received perfect marks on 3 community audits.

Barring the uncovering of any significant security or design flaws, we plan to mark the current deployments as canonical.

Requesting final comments on the implementation :)

P.S. My personal opinion is that the ETH 1.x and the ETH 2.0 Casper/EWASM releases will prompt a broad rethinking of optimal smart contract structure. Specifically, current conversations and proposals around state rent/event logging/monolithic contracts have implications for smart contract developers, and for this ERC. However, I still believe that the current implementation is sound, and support deploying it in its current form, primarily because there's still a fair amount of uncertainty in the Ethereum development roadmap, and because of the relative simplicity/flexibility of ERC-1484. This being an open discussion forum, however, I'd also be happy to hear additional comments/differing opinions!

@sadfool1
Copy link

sadfool1 commented Feb 8, 2019

Awesome!

@MicahZoltu
Copy link
Contributor

MicahZoltu commented Mar 31, 2019

This EIP feels like an app/contract proposal, not a standard. A lot of the stuff in this could be extracted out into separate independent EIPs, and a number of items in here don't really benefit significantly from being standardized. For example, the recovery stuff both doesn't need to be an EIP (it doesn't benefit from standardization) and it could be extracted out into a separate EIP wholly unrelated to this EIP.

Remember, when someone is authoring a contract, they can implement multiple interfaces. You don't have to create a single monolithic interface that solves all of the problems at the same time. Also keep in mind that you can author a contract that has public methods that are not part of an interface. 😄

Note: The above commentary doesn't mean I don't think the stuff discussed here are good ideas, just that I don't think everything discussed here is a good idea to standardize and that I don't think everything here is a good idea to bundle together in a single standard. In fact, I have a smart wallet I used that I wrote a couple years ago that has a very similar recovery mechanism (and I do have thoughts on how you can improve yours if you are interested)!

@NoahZinsmeister
Copy link
Contributor Author

NoahZinsmeister commented Apr 8, 2019

Thanks for taking a look @MicahZoltu ! We certainly concede that there's a lot going on in this EIP, and since we made many aspects as modular as possible, some could almost certainly be extracted into their own EIPs.

Your point about interfaces is well taken. Especially in light of upcoming ETH 1.x/2 changes, it might have made more sense to, for example, a) allow multiple implementations of the interface, and b) set it up so that per-entity data is stored in separate contracts (that maybe all reference or are referenced by some stripped-down registry).

My personal opinion is that as it stands, this EIP has some aspects that are perhaps less than ideal, but still solves non-trivial problems in the ETH identity space (especially when compared to alternatives), and can be built on today in a very extensible way (including adding additional methods that are not part of the interface! See Snowflake.).

Looking forward to ETH 2.0 land, my hopes are that it will still continue to exist, but perhaps transform into (or at least inform) an even more robust/extensible/un-opinionated identity framework.

@hexoul
Copy link

hexoul commented Apr 25, 2019

After I read your comments @NoahZinsmeister , I have a question. Don't you have a plan to develop this EIP any more before ETH 2.0?

I guess, if paygas will be applied to ETH 2.0, existing architectures which includes delegate concept should be replaced. Was it one of your concern in this step?

@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