ERC 725 and ERC 735 的实现及关系
https://github.com/OriginProtocol/origin-playground
通过ERC 725 and ERC 735 的实现来说明它们到底是做什么的:
看了这个例子后才大概明白了claim,key,identity之间的关系,就比如有一个合约为Claim checkers,作为一个卖票网站,这个网站只允许那些有着身份并且身份满足某种claim的消费者来该网站购买claim发行商
比如说一个身份为customer的消费者想要到这个网站上买票,这个消费者的账户是一个identity contract,以合约地址来代表该身份,key为其的公钥,然后此时Claim checkers这个卖票网站(也是一个contract)要求只有满足claim issuer,即发行商origin发行的一种claim的身份才能到该网站上买票。就比如这种claim名为Has Github,只有添加了这种claim的身份才能在网站上买东西,由这里可以看出,每个身份可以有多个claim,但是每一类claim一个identity只有一个,一个claim可能被多个身份添加。
根据这个网站给的教程进行学习过程为:
You can try a deployed version of this app at http://erc725.originprotocol.com/#/
刚进入时网站有三类contract,即identity(顾客)、claim issuers(发行商)和claim checkers(卖票网站):
点击右上角,使用的是Rinkeby测试网络,有三个账户,这三个用户分别来部署上面的三类contract:
首先使用第一个账户来创建一个identity contract,名叫customer
点击,然后在下图中填写customer,然后deploy:
然后就使用第一个账户0x313AaDcA1750CaadC7BCB26FF08175c95DCf8E38部署好了一个名为customer的identity contract,合约地址为0xF39D9Ec84E2b5597c96E44c8745d756338929b4c:
并且自动生成该合约的公私钥,查看代码 bytes32 _key = keccak256(msg.sender);可知key为合约地址的hash值:
可以添加key,点击右上角的,然后add key即可:
public key :0xbDA42d0EEB4faf56978aD5682774F8c2D1922F4e
private key: 0xf7a144c52ab222d3edce7cc404153cb126f7b156e78f3eb39ad9f9da12c09300
key :0x3542b67ac6670b296e63a7afab979cf80663f47158863b55a24ef67844d6cc99
后面查看代码实现
从这里我们就可以解释EIP-725中的key定义:
key
: A public key owned by this identitypurpose
:uint256[]
Array of the key types, like 1 = MANAGEMENT, 2 = ACTION, 3 = CLAIM, 4 = ENCRYPTION,这个就是该key的作用,我们新增的这个key的作用是ENCRYPTION,加密数据keyType
: The type of key used, which would be auint256
for different key types. e.g. 1 = ECDSA, 2 = RSA, etc.使用的是椭圆曲线数字签名算法(ECDSA)key
:bytes32
The public key. // for non-hex and long keys, its the Keccak256 hash of the key
然后就添加成功了,这是identity
在claim issuer中已经有了一个发行商为origin,该合约地址为0x64453cE1CB78A5F3d918d0BA162FE084bD2b5405,使用账户0x82d26CA48cCB83978eF2825053800C686f1b62D9部署的:
这里可以看见有keys,claims,claims signer services;在这里可见claim type有8种,Full name、Has LinkedIn、Email、Has Facebook、Has Twitter、Has GitHub、Has Google和Has LinkedIn(查代码看差别)
这里可以用来解释EIP-735中的claim定义:
claim issuer
: is another smart contract or external account, which issues claims about this identity. The claim issuer can be an identity contract itself.claim发行商能发行了有关身份的claims,它可以是一个智能合约或一个外部账户,当然也可以是身份合约本身claim
: A claim is an information an issuer has about the identity holder.(claim就是发行商所有的关于身份holder的信息) This contains the following:topic
: Auint256
number which represents the topic of the claim. (e.g. 1 biometric(生物识别), 2 residence(住宅) (ToBeDefined: number schemes, sub topics based on number ranges??))scheme
: The scheme with which this claim SHOULD be verified or how it should be processed. Its auint256
for different schemes. E.g. could3
mean contract verification, where thedata
will be call data, and theissuer
a contract address to call (ToBeDefined). Those can also mean different key types e.g. 1 = ECDSA, 2 = RSA, etc. (ToBeDefined)issuer
: The issuers identity contract address, or the address used to sign the above signature. If an identity contract, it should hold the key with which the above message was signed, if the key is not present anymore, the claim SHOULD be treated as invalid. The issuer can also be a contract address itself, at which the claim can be verified using the calldata
.当发行商是一个合约的时候,claims的证明就可以使用call data来进行signature
: Signature which is the proof that the claim issuer issued a claim oftopic
for this identity(签名是发行商对这个身份address(identityHolder
)发行的有关topic的claim). it MUST be a signed message of the following structure:keccak256(address identityHolder_address, uint256 _ topic, bytes data)
// orkeccak256(abi.encode(identityHolder_address, topic, data))
?data
: The hash of the claim data, sitting in another location, a bit-mask, call data, or actual data based on the claim scheme.uri
: The location of the claim, this can be HTTP links, swarm hashes, IPFS hashes, and such.
claim type即相当于上面的topic信息,在这里我选择的是Has Github这个claim type的claims signer services,该services的用处是当identity add claim的时候如果选择的是这几个claim type的话,那么就通过该服务器进行签名和验证,生成signature
和hash
在这里举例说明:
回到identity中点击add claim:
然后选择get claim from issuer :
在这里选择的是Has GitHub:
然后得到结果,点击OK,只要issuer,target是一样的,选择GitHub得到的下面的claim内容是相同的,所以每一类claim一个identity只有一个:
然后就得到与EIP-735一致的claim信息:
signature : 0xa27f961dfb729eaaf921c96295daaf6f1c1af8d2c124daf4c896c8830588c2185ae1c21bf0221cd87821de10514d6672853414220e338680e84308905c9d86f61b
然后就可以看见customer中出现了claim:
下面要做的就是部署Claim Checkers合约(卖票网站),设置两种顾客限制,看是否实现了claim的限制:
使用另一个账户0x56BEaa3C8C87c04584b48C9aF037c01d2677B8B2来部署该合约:
1)这种情况就是只有顾客拥有被发行商origin发行的claim type为has github的claim才能访问该合约
2.这种情况就是只有顾客拥有被发行商origin发行的claim type为has Facebook的claim才能访问该合约
然后就如下图所示:
点击,选择身份customer,查看其claim是否满足:
然后得到结果为第一种情况测为有效,第二种情况测为无效,与预期一致:
接下来是代码分析:
erc725,声明key及函数:
pragma solidity ^0.4.; contract ERC725 { uint256 constant MANAGEMENT_KEY = ;
uint256 constant ACTION_KEY = ;
uint256 constant CLAIM_SIGNER_KEY = ;
uint256 constant ENCRYPTION_KEY = ; event KeyAdded(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType);
event KeyRemoved(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType);
event ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
event Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
event Approved(uint256 indexed executionId, bool approved); struct Key {
uint256 purpose; //e.g., MANAGEMENT_KEY = 1, ACTION_KEY = 2, etc.
uint256 keyType; // e.g. 1 = ECDSA, 2 = RSA, etc.
bytes32 key;
} function getKey(bytes32 _key) public constant returns(uint256 purpose, uint256 keyType, bytes32 key);
function getKeyPurpose(bytes32 _key) public constant returns(uint256 purpose);
function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys);
function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) 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);
}
contracts/KeyHolder.sol
pragma solidity ^0.4.; import './ERC725.sol'; contract KeyHolder is ERC725 {//identity中的customer生成是就是生成了这个合约 uint256 executionNonce; struct Execution {
address to;
uint256 value;
bytes data;
bool approved;
bool executed;
} mapping (bytes32 => Key) keys;
mapping (uint256 => bytes32[]) keysByPurpose;
mapping (uint256 => Execution) executions; event ExecutionFailed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data); function KeyHolder() public {//初始化生成一个management的key
bytes32 _key = keccak256(msg.sender);//得知key为合约地址的hash
keys[_key].key = _key;
keys[_key].purpose = ; //management
keys[_key].keyType = ; //ECDSA
keysByPurpose[].push(_key); //所以如果一种purpose的key有好几个,那么就顺序排在数组中
emit KeyAdded(_key, keys[_key].purpose, );
} function getKey(bytes32 _key)
public
view
returns(uint256 purpose, uint256 keyType, bytes32 key)
{
return (keys[_key].purpose, keys[_key].keyType, keys[_key].key);
} function getKeyPurpose(bytes32 _key)
public
view
returns(uint256 purpose)
{
return (keys[_key].purpose);
} function getKeysByPurpose(uint256 _purpose)
public
view
returns(bytes32[] _keys)
{
return keysByPurpose[_purpose];
} function addKey(bytes32 _key, uint256 _purpose, uint256 _type)
public
returns (bool success)
{
require(keys[_key].key != _key, "Key already exists"); // Key should not already exist
if (msg.sender != address(this)) {
require(keyHasPurpose(keccak256(msg.sender), ), "Sender does not have management key"); // Sender has MANAGEMENT_KEY
} keys[_key].key = _key;
keys[_key].purpose = _purpose;
keys[_key].keyType = _type; keysByPurpose[_purpose].push(_key); emit KeyAdded(_key, _purpose, _type); return true;
} function approve(uint256 _id, bool _approve)//执行操作的purpose的实现
public
returns (bool success)
{
require(keyHasPurpose(keccak256(msg.sender), ), "Sender does not have action key"); emit Approved(_id, _approve); if (_approve == true) {
executions[_id].approved = true;
success = executions[_id].to.call(executions[_id].data, );//运行data操作
if (success) {
executions[_id].executed = true;
emit Executed(
_id,
executions[_id].to,
executions[_id].value,
executions[_id].data
);
return;
} else {
emit ExecutionFailed(
_id,
executions[_id].to,
executions[_id].value,
executions[_id].data
);
return;
}
} else {
executions[_id].approved = false;
}
return true;
} function execute(address _to, uint256 _value, bytes _data)
public
returns (uint256 executionId)
{
require(!executions[executionNonce].executed, "Already executed");
executions[executionNonce].to = _to; //把执行操作的数据写入
executions[executionNonce].value = _value;
executions[executionNonce].data = _data; emit ExecutionRequested(executionNonce, _to, _value, _data); if (keyHasPurpose(keccak256(msg.sender),) || keyHasPurpose(keccak256(msg.sender),)) {//只有是management和action两种purpose才能批准该操作的执行
approve(executionNonce, true);
} executionNonce++;
return executionNonce-;
} function removeKey(bytes32 _key)
public
returns (bool success)
{
require(keys[_key].key == _key, "No such key");
emit KeyRemoved(keys[_key].key, keys[_key].purpose, keys[_key].keyType); /* uint index;
(index,) = keysByPurpose[keys[_key].purpose.indexOf(_key);
keysByPurpose[keys[_key].purpose.removeByIndex(index); */ delete keys[_key]; return true;
} function keyHasPurpose(bytes32 _key, uint256 _purpose)
public
view
returns(bool result)
{
bool isThere;
if (keys[_key].key == ) return false;
isThere = keys[_key].purpose <= _purpose;
return isThere;
} }
erc735,声明claim及函数:
pragma solidity ^0.4.; contract ERC735 { event ClaimRequested(uint256 indexed claimRequestId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri); event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes32 signature, bytes claim, string uri);
event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimRemoved(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimChanged(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri); struct Claim {//这就是一个claims的组成,即某identity使用了该claim,就将相关信息记录下来,this.address即该identity的合约地址
uint256 claimType;
uint256 scheme; //说明使用的是ECDSA等哪个签名算法
address issuer; // msg.sender
bytes signature; // this.address + claimType + data
bytes data;
string uri;
} function getClaim(bytes32 _claimId) public constant returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri);
function getClaimIdsByType(uint256 _claimType) public constant returns(bytes32[] claimIds);
function addClaim(uint256 _claimType, uint256 _scheme, address issuer, bytes _signature, bytes _data, string _uri) public returns (bytes32 claimRequestId);
function removeClaim(bytes32 _claimId) public returns (bool success);
}
pragma solidity ^0.4.; import './ERC735.sol';
import './KeyHolder.sol'; contract ClaimHolder is KeyHolder, ERC735 {//claimholder即发行商合约 mapping (bytes32 => Claim) claims;
mapping (uint256 => bytes32[]) claimsByType; function addClaim(//identity想要add,虽然是在identity页面上进行操作,但是其实是发行商在实现这个功能
uint256 _claimType,
uint256 _scheme, //ECDSA
address _issuer,
bytes _signature,
bytes _data,
string _uri
)
public
returns (bytes32 claimRequestId)
{
bytes32 claimId = keccak256(_issuer, _claimType); if (msg.sender != address(this)) {
require(keyHasPurpose(keccak256(msg.sender), ), "Sender does not have claim signer key"); //该合约地址address(this)就是发行商合约地址msg.sender
}//如果不是,那么可能是其他的合约,那么其要have claim signer key if (claims[claimId].issuer != _issuer) {
claimsByType[_claimType].push(claimId);
} claims[claimId].claimType = _claimType;
claims[claimId].scheme = _scheme;
claims[claimId].issuer = _issuer;
claims[claimId].signature = _signature;
claims[claimId].data = _data;
claims[claimId].uri = _uri; emit ClaimAdded(
claimId,
_claimType,
_scheme,
_issuer,
_signature,
_data,
_uri
); return claimId;
} function removeClaim(bytes32 _claimId) public returns (bool success) {
if (msg.sender != address(this)) {
require(keyHasPurpose(keccak256(msg.sender), ), "Sender does not have management key");
} /* uint index; */
/* (index, ) = claimsByType[claims[_claimId].claimType].indexOf(_claimId);
claimsByType[claims[_claimId].claimType].removeByIndex(index); */ emit ClaimRemoved(
_claimId,
claims[_claimId].claimType,
claims[_claimId].scheme,
claims[_claimId].issuer,
claims[_claimId].signature,
claims[_claimId].data,
claims[_claimId].uri
); delete claims[_claimId];
return true;
} function getClaim(bytes32 _claimId)
public
constant
returns(
uint256 claimType,
uint256 scheme,
address issuer,
bytes signature,
bytes data,
string uri
)
{
return (
claims[_claimId].claimType,
claims[_claimId].scheme,
claims[_claimId].issuer,
claims[_claimId].signature,
claims[_claimId].data,
claims[_claimId].uri
);
} function getClaimIdsByType(uint256 _claimType)
public
constant
returns(bytes32[] claimIds)
{
return claimsByType[_claimType];
} }
contracts/Identity.sol
这个是当我们部署identity的时候如果想要使用左下角的,则实现方式就是下面的代码,否则使用的还是claimholder:
点击后:
就是部署identity的同时加上claim
pragma solidity ^0.4.; import './ClaimHolder.sol'; /**
* NOTE: This contract exists as a convenience for deploying an identity with
* some 'pre-signed' claims. If you don't care about that, just use ClaimHolder
* instead.
*/ contract Identity is ClaimHolder { function Identity(
uint256[] _claimType,
uint256[] _scheme,
address[] _issuer,
bytes _signature,
bytes _data,
string _uri,
uint256[] _sigSizes,
uint256[] dataSizes,
uint256[] uriSizes
)
public
{
bytes32 claimId;
uint offset = ;
uint uoffset = ;
uint doffset = ; for (uint i = ; i < _claimType.length; i++) { claimId = keccak256(_issuer[i], _claimType[i]); claims[claimId] = Claim(
_claimType[i],
_scheme[i],
_issuer[i],
getBytes(_signature, offset, _sigSizes[i]),
getBytes(_data, doffset, dataSizes[i]),
getString(_uri, uoffset, uriSizes[i])
); offset += _sigSizes[i];
uoffset += uriSizes[i];
doffset += dataSizes[i]; emit ClaimAdded(
claimId,
claims[claimId].claimType,
claims[claimId].scheme,
claims[claimId].issuer,
claims[claimId].signature,
claims[claimId].data,
claims[claimId].uri
);
}
} function getBytes(bytes _str, uint256 _offset, uint256 _length) constant returns (bytes) {
bytes memory sig = new bytes(_length);
uint256 j = ;
for (uint256 k = _offset; k< _offset + _length; k++) {
sig[j] = _str[k];
j++;
}
return sig;
} function getString(string _str, uint256 _offset, uint256 _length) constant returns (string) {
bytes memory strBytes = bytes(_str);
bytes memory sig = new bytes(_length);
uint256 j = ;
for (uint256 k = _offset; k< _offset + _length; k++) {
sig[j] = strBytes[k];
j++;
}
return string(sig);
}
}
contracts/ClaimVerifier.sol
pragma solidity ^0.4.; import './ClaimHolder.sol'; contract ClaimVerifier {//Claim Checkers合约 event ClaimValid(ClaimHolder _identity, uint256 claimType);
event ClaimInvalid(ClaimHolder _identity, uint256 claimType); ClaimHolder public trustedClaimHolder; function ClaimVerifier(address _trustedClaimHolder) public {
trustedClaimHolder = ClaimHolder(_trustedClaimHolder);//即发行商的合约地址
} function checkClaim(ClaimHolder _identity, uint256 claimType)
public
returns (bool claimValid)
{
if (claimIsValid(_identity, claimType)) {
emit ClaimValid(_identity, claimType);
return true;
} else {
emit ClaimInvalid(_identity, claimType);
return false;
}
} function claimIsValid(ClaimHolder _identity, uint256 claimType)
public
constant
returns (bool claimValid)
{
uint256 foundClaimType;
uint256 scheme;
address issuer;
bytes memory sig;
bytes memory data; // Construct claimId (identifier发行商地址 + claim type)
bytes32 claimId = keccak256(trustedClaimHolder, claimType); // Fetch claim from user,
( foundClaimType, scheme, issuer, sig, data, ) = _identity.getClaim(claimId); bytes32 dataHash = keccak256(_identity, claimType, data);
bytes32 prefixedHash = keccak256("\x19Ethereum Signed Message:\n32", dataHash); // Recover address of data signer,sig是identity实现的签名
address recovered = getRecoveredAddress(sig, prefixedHash);//得到对数据进行签名的address,即identity // Take hash of recovered address
bytes32 hashedAddr = keccak256(recovered);//将其进行hash // Does the trusted identifier have they key which signed the user's claim?
return trustedClaimHolder.keyHasPurpose(hashedAddr, );
} function getRecoveredAddress(bytes sig, bytes32 dataHash)
public
view
returns (address addr)
{
bytes32 ra;
bytes32 sa;
uint8 va; // Check the signature length
if (sig.length != ) {
return ();
} // Divide the signature in r, s and v variables
assembly {
ra := mload(add(sig, ))
sa := mload(add(sig, ))
va := byte(, mload(add(sig, )))
} if (va < ) {
va += ;
} address recoveredAddress = ecrecover(dataHash, va, ra, sa); return (recoveredAddress);
} }
还有一个更复杂的例子,有空看看:https://github.com/mirceapasoi/erc725-735
ERC 725 and ERC 735 的实现及关系的更多相关文章
- ethereum/EIPs-725
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-725.md eip title author discussions-to status ...
- BOM/ROUTING/PO/WIP等模块常用查询
常用查询scripts /*bom*/ select p_item.segment1,c_item.segment1,bic.COMPONENT_QUANTITY,bic.COMPONENT_YIEL ...
- emacs安装及配置
目录 平台 安装 基本配置 配置文件结构 elpa仓库管理 主题配色 字体显示配置(解决中文卡顿) 插件配置 markdown 简介 markdown-mode markdown-toc org导出m ...
- 英特尔老款CPU支持虚拟化对照表(转)
说明:一般来说新款的挤牙膏公司出的CPU都基本支持虚拟化,但不包括Atom系列的,也就是小主机低功耗机器使用的CPU. Intel® Virtualization Technology List YE ...
- Altium Designer中Electrical Type的意思
:之前Altium Designer设计图时发现: 它的引脚上有两个三角 双击打开引脚,打开配置: 于是从网上查了一下:http://blog.csdn.net/jbb0523/article/det ...
- Block Chain Learning Notes
区块链是什么 区块链技术是由比特币创造的,本文也将从比特币开始进行引导,一步一步告诉大家什么是区块链.如果你想立马知道区块链是什么,也可以直接转到文章末尾的区块链定义. 区块链,可能是当下最有前景又充 ...
- ERC: Claim Holder #735 status:Discussion
EIP: Title: Claim Holder Author: Fabian Vogelsteller (@frozeman) Type: Standard Category: ERC Status ...
- [转帖](区块链补习班)ERC20很多人都听过,但ERC是什么你真的了解吗?
(区块链补习班)ERC20很多人都听过,但ERC是什么你真的了解吗? http://baijiahao.baidu.com/s?id=1600948969290990883&wfr=spide ...
- 暴力枚举 UVA 725 Division
题目传送门 /* 暴力:对于每一个数都判断,是否数字全都使用过一遍 */ #include <cstdio> #include <iostream> #include < ...
随机推荐
- 解决:oracle+myBatis ResultMap 类型为 map 时,表字段类型有 Long/Blob/Clob 时报错
前言:最近在做一个通用查询单表的组件,所以 sql 的写法就是 select *,然后 resultType="map" .如果数据库中的表里有字段类型为 Long 等类型时,my ...
- iframe 页面刷新
1.点击刷新 [1].html页面代码 <a href="javascript:;" title="刷新当前页" id="Refresh&quo ...
- js-ES6学习笔记-Class(3)
1.Class之间可以通过extends关键字实现继承. class ColorPoint extends Point { constructor(x, y, color) { super(x, y) ...
- Jquery 只保留数字和小数点(正则)
str.replace(/[^\d.]/g,"")
- EasyUI 通过 Combobox 实现 AutoComplete 效果
朋友在做一个web程序,用的EasyUI框架,让我帮忙实现一个自动提示功能.由于之前我也没用过EasyUI框架,就想到了jQueryUI有 AutoComplete 插件,就想直接拿过来用. 但当我将 ...
- 排错-windows平台下访问oracle em出现空白的解决方法
排错-windows平台下访问oracle em出现空白的解决方法 by:授客 QQ:1033553122 问题描述 IE浏览器本地访问oem,出现空白页面,就左上角有一行字符 http://loca ...
- [WPF 容易忽视的细节] —— Exception in WPF's Converter
前言: 在WPF中,Converter是我们经常要用到的一个工具,因为XAML上绑定的数据不一定是我们需要的数据. 问题: 在Converter中抛出一个异常导致程序崩溃,而且是在对未捕获异常进行集中 ...
- Ajax 小实例
1.urls.py url(r'^jiafa', views.jiafa), 2.views.py def jiafa(request): if request.method == "GET ...
- Python+Selenium笔记(十七):操作cookie
(一)方法 方法 简单说明 add_cookie(cookie_dict) 在当前会话中添加cookie信息 cookie_dict:字典,name和value是必须的 delete_all_cook ...
- sh: ./bin/my_print_defaults: /lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录 FATAL ERROR: Neither host 'kvm' nor 'localhost' could be looked up with ./bin/resolveip Please configure the 'hostname'
初始化数据库报错: sh: ./bin/my_print_defaults: /lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录FATAL ERROR ...