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 < ...
随机推荐
- 34.Linux-printk分析、使用__FILE__, __FUNCTION__, __LINE__ 调试
本节学习目的 1)分析printk()函数 2)使用printk()调试驱动 1.在驱动调试中,使用printk(),是最简单,最方便的办法 当uboot的命令行里的“console=tty1”时,表 ...
- Java基础——Servlet(四)
最近一直在学习Servlet,真的有烦躁,一下子要创建好几个文件,服务端.客户端.html页面....学习进度蛮慢的,很容易失掉信心.当学习到cookie时,发现有好多实现是在我们日常生活中可以会遇得 ...
- mybatis将传入0识别成空字符串
mybatis将传入的Integer类型的0被识别成空字符串,网上的解决办法: <if test="status != null and status != '' or status ...
- RxJava1升级到RxJava2的注意事项
1.package更改 rx1包名由原来的rx.xxx更改为io.reactivex.xxx,并且在同一个module之下,rx1和rx2是不兼容的. 2.背压支持 RxJava在1.0只有一个个观察 ...
- codevs1735 方程的解数(meet in the middle)
题意 题目链接 Sol 把前一半放在左边,后一半放在右边 meet in the middle一波 统计答案的时候开始想的是hash,然而MLE了两个点 实际上只要排序之后双指针扫一遍就行了 #inc ...
- OSGI企业应用开发(十一)Bundle资源获取途径
使用OSGI模块化标准构建Java EE项目,其中比较繁琐的一个方面就是Bundle资源的获取,因为很多开源框架官方都没有发布Bundle版本的Jar文件,这也是使用OSGI开发企业应用首先要解决的问 ...
- RaPC(rasterized polygon clipper): A discrete grid-based polygon clipping algorithm
RaPC(rasterized polygon clipper)-A discrete grid-based polygon clipping algorithm This algorithm is ...
- WOSA/XFS PTR Form解析库—xfsptrdata.h
#ifndef _XFSPTRDATA_H_#define _XFSPTRDATA_H_ #include <XFSPTR.H>#include <Windows.h>#inc ...
- Sql 中存储过程详细案例
转自:http://www.cnblogs.com/yank/p/4235609.html 概念 存储过程(Stored Procedure):已预编译为一个可执行过程的一个或多个SQL语句. 创建存 ...
- Mysql使用优化之处(转)
1 开启事务之前需要rollback 连接句柄.(清理垃圾)2 mysql_ping 失败,程序需要处理重连逻辑:3 mysql_query()执行的SQL语句是一个以‘/0’结尾的字符串,而mysq ...