ERC-1484: 数字身份聚合器
Authors | Anurag Angara <anurag.angara@gmail.com>, Andy Chorlian <andychorlian@gmail.com>, Shane Hampton <shanehampton1@gmail.com>, Noah Zinsmeister <noahwz@gmail.com> |
---|---|
Created | 2018-10-12 |
Discussion Link | https://github.com/ethereum/EIPs/issues/1495 |
Requires | EIP-191 |
Table of Contents
简单总结
一个聚合数字身份信息的协议,该协议与现有、拟议和假设的未来数字身份标准具有广泛的互操作性。
摘要
此 EIP 提出了以太坊区块链上的身份管理和聚合框架。它允许实体通过一个单一的 Identity Registry
智能合约来声明一个 Identity
,以各种有意义的方式将其与以太坊地址相关联,并使用它与智能合约进行交互。这实现了任意复杂的身份相关功能。值得注意的是,(在其他功能中)ERC-1484 Identities
:是自主的,可以原生支持 ERC-725 和 ERC-1056 身份,是 DID 兼容的,并且可以完全由 meta-transactions 提供支持。
动机
以太坊社区提出的新兴身份标准和相关框架(包括 ERC/EIP 725,735,780,1056 等)以各种方式定义和工具化数字身份。随着现有方法的成熟,新的标准出现,以及孤立的、非标准的身份方法的发展,在身份上进行协调将对区块链用户和开发人员造成越来越大的负担,并涉及不必要的工作重复。
链上身份解决方案的激增可以追溯到这样一个事实,即每个解决方案都编纂了一个身份概念,并将其与以太坊的特定方面联系起来(声明协议、每个身份的智能合约、签名验证方案等)。该提案避免了这种方法,而是引入了一个介于以太坊网络和各个身份应用程序之间的协议层。这通过允许任何身份驱动的应用程序利用一个非武断的身份管理协议来解决身份管理和互操作性挑战。
定义
-
Identity Registry
: 一个单一的智能合约,它是所有Identities
的中心。Registry
的主要责任是定义和执行Identities
的全局命名空间的规则,这些Identities
由以太坊识别号 (EIN) 单独命名。 -
Identity
:一个数据结构,包含与身份相关的所有核心信息,即:Recovery Address
、Associated Addresses
集合、Providers
集合和Resolvers
集合。Identities
由 EIN 命名(从 1 开始递增的uint
标识符),这些标识符是唯一的,但其他方面没有信息。每个Identity
都是一个 Solidity 结构:
struct Identity {
address recoveryAddress;
AddressSet.Set associatedAddresses;
AddressSet.Set providers;
AddressSet.Set resolvers;
}
-
Associated Address
:公开与Identity
关联的以太坊地址。为了使地址成为Associated Address
,Identity
必须从候选地址和一个现有的Associated Address
进行交易或生成适当签名的消息,表明关联的意图。通过交易/生成表示取消关联意图的签名,可以从Identity
中删除Associated Address
。在任何给定时间,一个给定的地址只能是一个Identity
的Associated Address
。 -
Provider
:被授权代表已授权其这样做的Identities
行事的以太坊地址(通常但不是定义上的智能合约)。这包括但不限于管理Identity
的Associated Address
、Provider
和Resolver
集合。Providers
的存在是为了通过更容易管理Identities
来促进用户采用。 -
Resolver
:包含与Identities
相关的任意信息的智能合约。解析器可以实现一个身份标准,例如 ERC-725,或者可以包含一个利用或声明关于Identities
的识别信息的智能合约。这些可以是简单的证明结构,也可以是更复杂的金融 dApp、社交媒体 dApp 等。添加到Identity
的每个Resolver
都会使Identity
更加有用。 -
Recovery Address
: 一个以太坊地址(帐户或智能合约),可用于恢复丢失的Identities
,如恢复部分所述。 -
Destruction
:如果无法恢复对Identity
的控制,Destruction
是一种永久禁用Identity
的应急措施。它删除所有Associated Addresses
、Providers
和可选的Resolvers
,同时保留Identity
。Identity
存在的证据仍然存在,而对Identity
的控制权被取消。
规范
本提案中的数字身份可以被视为一个综合账户,包含比任何单个身份应用程序更多的关于身份的信息。这个综合身份可以解析为无限数量的子身份,称为 Resolvers
。这允许一个原子实体 Identity
,可以解析为抽象数据结构 Resolvers
。 Resolvers
通过它们的任何 Associated Addresses
或它们的 EIN
来识别 Identities
。
该协议围绕声明 Identity
和管理 Associated Addresses
、Providers
和 Resolvers
展开。 Identities
可以将大部分或全部责任委托给一个或多个 Providers
,或者直接从 Associated Address
执行。 Associated Addresses
/Providers
可以不加区别地添加和删除 Resolvers
和 Providers
。只有获得适当的许可,才能添加或删除 Associated Addresses
。
Identity Registry
Identity Registry
包含创建新 Identities
的功能,以及用于现有 Identities
来管理其 Associated Addresses
、Providers
和 Resolvers
的功能。重要的是要注意,这个注册表从根本上需要为构建 Identity
的各个方面进行交易。然而,认识到 dApp 和身份应用程序的可访问性的重要性,我们授权 Providers
代表用户构建 Identities
,而无需用户支付 gas 费用。这种模式的一个例子,通常被称为 meta transactions,可以在参考实现中看到。
由于多个地址可以与给定的身份相关联(尽管反之则不然),因此 Identities
由 EIN
命名。这个 uint
标识符可以用 QR 格式编码,或者在 Provider
或 Resolver
级别的注册表中映射到更友好的格式,例如 string
。
地址管理
地址管理功能包括以无信任方式将多个用户拥有的 Associated Addresses
连接到 Identity
。它不给任何特定的 Associated Address
特殊状态,而是将这个(可选的)规范留给构建在协议之上的身份应用程序 - 例如,ERC-725 标准中命名的 management
、action
、claim
和 encryption
密钥,或 ERC-1056 中命名的 identifiers
和 delegates
。这允许用户从多个钱包访问公共身份数据,同时仍然:
- 保留与其身份之外的合约交互的能力
- 利用在用户身份的应用层建立的特定于地址的权限。
地址管理功能中的无信任性是通过一个强大的权限方案实现的。要将 Associated Address
添加到 Identity
,需要来自 1) 注册表中已有的地址和 2) 要声明的地址的交易发送者的隐式许可或签名的显式许可。重要的是,交易不需要来自任何特定地址,只要建立了许可,这不仅允许用户而且允许第三方(公司、政府等)承担管理身份的开销。为了防止受损的 Associated Address
单方面删除其他 Associated Addresses
,只有通过交易或生成来自它的签名才能删除 Associated Address
。
ERC-1484 中所需的所有签名都是按照 ERC-191 v0 规范设计的。为了避免重放攻击,所有签名必须包括当前 block.timestamp
的滚动滞后窗口中的时间戳。有关更多信息,请参阅参考实现中的最佳实践文档。
Provider 管理
虽然该协议允许用户直接调用身份管理功能,但它也旨在通过允许 Providers
(通常是智能合约)代表用户执行身份管理功能来使其更健壮和面向未来。由 Identity
设置的 Provider
可以通过在函数调用中传递用户的 EIN
来执行地址管理和解析器管理功能。
Resolver 管理
Resolver
是任何编码解析为 Identity
的信息的智能合约。我们对可以在解析器中编码的特定信息以及此信息启用的功能保持不可知。 Resolvers
的存在主要是使这个 ERC 成为一个身份协议而不是一个身份应用程序。 Resolvers
将智能合约中的抽象数据解析为原子实体 Identity
。
恢复
如果用户失去了对 Associated Address
的控制,Recovery Address
提供了一个回退机制。在 Identity
创建时,Recovery Address
由创建者作为参数传递。在三种情况下会触发恢复功能:
1. 更改恢复地址:如果恢复密钥丢失,Associated Address
/Provider
可以triggerRecoveryAddressChange/triggerRecoveryAddressChangeFor。为了防止已经控制了 Associated Address
或 Provider
并将其 Recovery Address
更改为他们控制下的地址的人的恶意行为,此操作触发一个 14 天的挑战期,在此期间旧的 Recovery Address
可以通过触发恢复来拒绝更改。如果 Recovery Address
在 14 天内没有拒绝更改,则 Recovery Address
将被更改。
2. 恢复:当用户意识到属于用户的 Associated Address
或 Recovery Address
丢失或被盗时,会发生恢复。在这种情况下,Recovery Address
必须调用 triggerRecovery。这会从相应的 Identity
中删除所有 Associated Addresses
和 Providers
,并将其替换为函数调用中传递的地址。 Identity
和关联的 Resolvers
保持完整性。用户现在负责将适当的未受损地址添加回他们的 Identity
。
重要的是,Recovery Address
可以是用户控制的钱包或另一个地址,例如多重签名钱包或智能合约。这允许任意复杂的恢复逻辑!这包括恢复完全符合 DID 等标准的潜力。
3. 销毁
恢复方案为 Recovery Address
提供了相当大的权力;因此,当 Recovery Address
受到威胁时,Destruction
是一种应对恶意控制 Identity
的终极选择。如果恶意行为者危及用户的 Recovery Address
并触发恢复,则在 Recovery
过程中删除的任何地址都可以在 14 天内调用 triggerDestruction 以永久禁用 Identity
。然后,用户将需要创建一个新的 Identity
,并且有责任为在 Resolver
或 Provider
层中构建的任何身份应用程序参与恢复方案。
替代恢复考量
在设计上述恢复过程时,我们考虑了许多可能的替代方案。我们最终选择了最不带偏见、模块化且与 Associated Address
、Provider
和 Resolver
组件背后的理念相一致的方案。尽管如此,我们认为突出我们考虑过的其他一些恢复选项很重要,以便提供关于我们如何最终确定我们所做的理由。
高级别考虑
从根本上讲,恢复方案需要能够抵御受损地址控制用户 Identity
的情况。次要考虑因素是防止受损地址由于链下效用而恶意破坏用户的身份,这不是最佳情况,但严格来说比他们获得控制权更好。
替代方案 1:核选项
这种方法允许任何 Associated Address
在另一个 Associated Address
受到威胁时销毁 Identity
。虽然这可能看起来很严重,但我们强烈考虑了它,因为这个 ERC 是一个身份协议,而不是一个身份应用程序。这意味着虽然用户受损的 Identity
被销毁,但他们仍然可以追索到他们在 Resolver
和/或 Provider
级别的每个实际身份中可用的任何恢复机制。我们最终驳回了这种方法,原因有二:
- 在用户只有一个
Associated Address
的情况下,它不够健壮 - 由于其不宽容的性质,它会增加对身份应用程序的恢复请求的频率。
替代方案 2:通过 Providers 单方面删除地址
这将允许 Associated Addresses
/Providers
删除 Associated Addresses
,而无需来自所述地址的签名。此实现将允许 Providers
包含任意复杂的方案来删除恶意地址 - 例如,多重签名要求、集中的链下验证、用户控制的主地址、推迟到管辖合同等等。为了防止受损的 Associated Address
仅设置恶意 Provider
以删除未受损地址,它需要一个等待期,从设置 Provider
到他们能够删除 Associated Address
之间。我们驳回了这种方法,因为我们认为它给 Providers
带来了太高的负担。如果 Provider
向用户提供了一系列复杂的功能,但在部署后发现 Provider
的恢复逻辑中存在威胁,则需要重建 Provider
特定的基础设施。我们还考虑包含一个标志,允许用户决定 Provider
是否可以单方面删除 Associated Addresses
。最终,我们得出结论,仅允许通过 Recovery Address
删除 Associated Addresses
能够实现同样复杂的恢复逻辑,同时将功能与 Providers
分离,从而减少用户放弃对可能存在缺陷的实现的控制权的空间。
理由
我们发现在协议层,身份不应依赖于特定的声明或证明结构,而应成为一个值得信赖的框架的一部分,可以在该框架上构建任意复杂的声明和证明结构。
现有身份解决方案的主要批评是它们过于严格。我们的目标是限制要求,保持身份模块化和面向未来,并对特定身份组件可能具有的任何功能保持不带偏见的态度。此提案允许用户选择使用一个强大的 Identity
而不仅仅是一个地址在区块链上进行交互。
实现
ERC-1484 的参考实现可以在 NoahZinsmeister/ERC-1484 中找到。
identityExists
返回一个 bool
,指示是否存在由传递的 EIN
命名的 Identity
。
function identityExists(uint ein) public view returns (bool);
hasIdentity
返回一个 bool
,指示传递的 _address
是否与 Identity
关联。
function hasIdentity(address _address) public view returns (bool);
getEIN
返回与传递的 _address
关联的 EIN
。如果该地址未与 EIN
关联,则抛出。
function getEIN(address _address) public view returns (uint ein);
isAssociatedAddressFor
返回一个 bool
,指示传递的 _address
是否与传递的 EIN
关联。
function isAssociatedAddressFor(uint ein, address _address) public view returns (bool);
isProviderFor
返回一个 bool
,指示传递的 provider
是否已由传递的 EIN
设置。
function isProviderFor(uint ein, address provider) public view returns (bool);
isResolverFor
返回一个 bool
,指示传递的 resolver
是否已由传递的 EIN
设置。
function isResolverFor(uint ein, address resolver) public view returns (bool);
getIdentity
返回传递的 EIN
的 recoveryAddress
、associatedAddresses
、providers
和 resolvers
。
function getIdentity(uint ein) public view
returns (
address recoveryAddress,
address[] memory associatedAddresses, address[] memory providers, address[] memory resolvers
);
createIdentity
创建一个 Identity
,将 msg.sender
设置为唯一的 Associated Address
。返回新 Identity
的 EIN
。
function createIdentity(address recoveryAddress, address[] memory providers, address[] memory resolvers)
public returns (uint ein);
触发事件:IdentityCreated
createIdentityDelegated
执行与 createIdentity
相同的逻辑,但可以由任何地址调用。此函数需要来自 associatedAddress
的签名以确保他们的同意。
function createIdentityDelegated(
address recoveryAddress, address associatedAddress, address[] memory providers, address[] memory resolvers,
uint8 v, bytes32 r, bytes32 s, uint timestamp
)
public returns (uint ein);
触发事件:IdentityCreated
addAssociatedAddress
将 addressToAdd
添加到 approvingAddress
的 EIN
。 msg.sender
必须是 approvingAddress
或 addressToAdd
,并且签名必须来自另一个。
function addAssociatedAddress(
address approvingAddress, address addressToAdd, uint8 v, bytes32 r, bytes32 s, uint timestamp
)
public
addAssociatedAddressDelegated
将 addressToAdd
添加到 approvingAddress
的 EIN
。需要来自 approvingAddress
和 addressToAdd
的签名。
function addAssociatedAddressDelegated(
address approvingAddress, address addressToAdd,
uint8[2] memory v, bytes32[2] memory r, bytes32[2] memory s, uint[2] memory timestamp
)
public
removeAssociatedAddress
从其 EIN
中删除 msg.sender
作为 Associated Address
。
function removeAssociatedAddress() public;
removeAssociatedAddressDelegated
从其关联的 EIN
中删除 addressToRemove
。需要来自 addressToRemove
的签名。
function removeAssociatedAddressDelegated(address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint timestamp)
public;
addProviders
将 Providers
数组添加到 msg.sender
的 Identity
。
function addProviders(address[] memory providers) public;
触发事件:ProviderAdded
addProvidersFor
执行与 addProviders
相同的逻辑,但必须由 Provider
调用。
function addProvidersFor(uint ein, address[] memory providers) public;
触发事件:ProviderAdded
removeProviders
从 msg.sender
的 Identity
中删除 Providers
数组。
function removeProviders(address[] memory providers) public;
触发事件:ProviderRemoved
removeProvidersFor
执行与 removeProviders
相同的逻辑,但由 Provider
调用。
function removeProvidersFor(uint ein, address[] memory providers) public;
触发事件:ProviderRemoved
addResolvers
将 Resolvers
数组添加到 msg.sender
的 EIN
。
function addResolvers(address[] memory resolvers) public;
触发事件:ResolverAdded
addResolversFor
执行与 addResolvers
相同的逻辑,但必须由 Provider
调用。
function addResolversFor(uint ein, address[] memory resolvers) public;
触发事件:ResolverAdded
removeResolvers
从 msg.sender
的 EIN
中删除 Resolvers
数组。
function removeResolvers(address[] memory resolvers) public;
触发事件:ResolverRemoved
removeResolversFor
执行与 removeResolvers
相同的逻辑,但必须由 Provider
调用。
function removeResolversFor(uint ein, address[] memory resolvers) public;
触发事件:ResolverRemoved
triggerRecoveryAddressChange
启动 msg.sender
的 EIN
的当前 recoveryAddress
的更改。
function triggerRecoveryAddressChange(address newRecoveryAddress) public;
触发事件:RecoveryAddressChangeTriggered
triggerRecoveryAddressChangeFor
启动给定 EIN
的当前 recoveryAddress
的更改。
function triggerRecoveryAddressChangeFor(uint ein, address newRecoveryAddress) public;
触发事件:RecoveryAddressChangeTriggered
triggerRecovery
从当前的 recoveryAddress
或旧的 recoveryAddress
(如果在过去 2 周内更改)触发 EIN
恢复。
function triggerRecovery(uint ein, address newAssociatedAddress, uint8 v, bytes32 r, bytes32 s, uint timestamp) public;
触发事件:RecoveryTriggered
triggerDestruction
触发 EIN
的销毁。这使得 Identity
永久不可用。
function triggerDestruction(uint ein, address[] memory firstChunk, address[] memory lastChunk, bool clearResolvers)
public;
触发事件:IdentityDestroyed
事件
IdentityCreated
必须在创建 Identity
时触发。
event IdentityCreated(
address indexed initiator, uint indexed ein,
address recoveryAddress, address associatedAddress, address[] providers, address[] resolvers, bool delegated
);
AssociatedAddressAdded
必须在将地址添加到 Identity
时触发。
event AssociatedAddressAdded(
address indexed initiator, uint indexed ein, address approvingAddress, address addedAddress, bool delegated
);
AssociatedAddressRemoved
必须在从 Identity
中删除地址时触发。
event AssociatedAddressRemoved(address indexed initiator, uint indexed ein, address removedAddress, bool delegated);
ProviderAdded
必须在将 provider 添加到 Identity
时触发。
event ProviderAdded(address indexed initiator, uint indexed ein, address provider, bool delegated);
ProviderRemoved
必须在删除 provider 时触发。
event ProviderRemoved(address indexed initiator, uint indexed ein, address provider, bool delegated);
ResolverAdded
必须在添加解析器时触发。
event ResolverAdded(address indexed initiator, uint indexed ein, address resolvers, bool delegated);
ResolverRemoved
必须在删除解析器时触发。
event ResolverRemoved(address indexed initiator, uint indexed ein, address resolvers, bool delegated);
RecoveryAddressChangeTriggered
必须在触发恢复地址更改时触发。
event RecoveryAddressChangeTriggered(
address indexed initiator, uint indexed ein,
address oldRecoveryAddress, address newRecoveryAddress, bool delegated
);
RecoveryTriggered
必须在触发恢复时触发。
event RecoveryTriggered(
address indexed initiator, uint indexed ein, address[] oldAssociatedAddresses, address newAssociatedAddress
);
IdentityDestroyed
必须在销毁 Identity
时触发。
event IdentityDestroyed(address indexed initiator, uint indexed ein, address recoveryAddress, bool resolversReset);
Solidity 接口
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);
}
向后兼容性
根据此标准建立的 Identities
由现有的以太坊地址组成;因此,没有向后兼容性问题。希望成为 Identities
的 Resolvers
的已部署的、不可升级的智能合约将需要编写将地址解析为 EIN
命名的 Identities
的包装合约。
附加参考
版权
在 CC0 下放弃版权和相关权利。
Citation
Please cite this document as:
Anurag Angara <anurag.angara@gmail.com>, Andy Chorlian <andychorlian@gmail.com>, Shane Hampton <shanehampton1@gmail.com>, Noah Zinsmeister <noahwz@gmail.com>, "ERC-1484: 数字身份聚合器 [DRAFT]," Ethereum Improvement Proposals, no. 1484, October 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1484.