WeIdentity 源码分析 | 狗哥解码(一)

栏目: IT技术 · 发布时间: 4年前

内容简介:一方面,本系列会提供项目的源码解读 —— 这是其他源码分析也能做到的。更重要的是另一方面,也是本系列的创新点:本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

一方面,本系列会提供项目的源码解读 —— 这是其他源码分析也能做到的。更重要的是另一方面,也是本系列的创新点: 本系列将会示范阅读源码的方法论。

狗哥解码是本公众号新开设的系列,旨在分析区块链世界里的一些重要项目。

一方面,本系列会提供项目的源码解读 —— 这是其他源码分析也能做到的。更重要的是另一方面,也是本系列的创新点:

本系列将会示范阅读源码的方法论。

在一个理想的、存在真空中的球形鸡的世界里,所有代码都有完善的文档,清楚地阐述每一个接口的用途与参数。但是,真实的世界中,代码不断敏捷迭代,文档和代码进行着永恒地「龟兔赛跑」。同时,前人留下的 永远不会修复的 TODO、缺乏注释的变量与各种奇技淫巧 也阻碍我们去理解源码。

在这种情况下,我们实际面临的是这样一种情况 —— 要在没有说明书的情况下去了解一个「机器(程序)」的内部运转原理,在此基础上再对其进行魔改与增强。

1 了解 WeIdentity

在阅读与分析 WeIdentity 的源码之前,首先阅读 WeIdentity 的官方文档 —— 永远先看官方文档而不是二手资料是一个好的习惯:

https://weidentity.readthedocs.io/

WeIdentity 是一套分布式多中心的技术解决方案,可承载实体对象(人或者物)的现实身份与链上身份的可信映射、以及实现实体对象之间安全的访问授权与数据交换。WeIdentity由微众银行自主研发并完全开源,秉承公众联盟链整合资源、交换价值、服务公众的理念,致力于成为链接多个垂直行业领域的分布式商业基础设施,促进泛行业、跨机构、跨地域间的身份认证和数据合作。

通过阅读文档,我们对 WeIdentity 建立起一个最粗浅的模型:

+---------------+   +--------------------+   +-----+
| FISCO BCOS 链 |---|  WeId Solidity 合约 |---| SDK |
+---------------+   +--------------------+   +-----+

在这个基础上,我们可以进行下一步了——先把示例流程跑通。

2 跑通示例流程

这个过程写在这篇文章里:

https://mp.weixin.qq.com/s/BySSeIkMstBXLTeZ2krIHA

通过跑通示例流程,我们可以更细致地理解这个「机器」的工作原理。

同时,如果仅局限于「使用」,而不涉及「魔改」和「增强」的话,在跑通这个流程后,已经可以上手开发相关的应用了。在开发应用的过程中,我们便会对它产生更多的理解,而无需先研究透彻再上手实践。

3 合约分析

示例流程走完后,我们就可以开始着手分析它的合约了:

https://github.com/WeBankFinTech/weid-contract

这份合约涉及多个文件,因此我们需要梳理合约结构。值得欣慰的是 WeIdentity 的文档比较完善,通过文档我们即能了解到合约结构:

https://fintech.webank.com/developer/docs/weidentity/docs/weidentity-contract-design.html

WeIdentity 源码分析 | 狗哥解码(一)

现在,我们要开始选择分析的第一个部分。结合合约结构和示例流程,我们可以先看下 WeIDContract 合约 —— 因为对它的理解并不依赖于其它模块。

pragma solidity ^0.4.4;
/*
 *       Copyright© (2018) WeBank Co., Ltd.
 *
 *       This file is part of weidentity-contract.
 *
 *       weidentity-contract is free software: you can redistribute it and/or modify
 *       it under the terms of the GNU Lesser General Public License as published by
 *       the Free Software Foundation, either version 3 of the License, or
 *       (at your option) any later version.
 *
 *       weidentity-contract is distributed in the hope that it will be useful,
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *       GNU Lesser General Public License for more details.
 *
 *       You should have received a copy of the GNU Lesser General Public License
 *       along with weidentity-contract.  If not, see <https://www.gnu.org/licenses/>.
 */

contract WeIdContract {

    mapping(address => uint) changed;

    modifier onlyOwner(address identity, address actor) {
         require (actor == identity);
         _;
    }

    bytes32 constant private WEID_KEY_CREATED = "created";
    bytes32 constant private WEID_KEY_AUTHENTICATION = "/weId/auth";

    event WeIdAttributeChanged(
        address indexed identity,
        bytes32 key,
        bytes value,
        uint previousBlock,
        int updated
    );

    function getLatestRelatedBlock(
        address identity
    ) 
        public 
        constant 
        returns (uint) 
    {
        return changed[identity];
    }

    function createWeId(
        address identity,
        bytes auth,
        bytes created,
        int updated
    )
        public
        onlyOwner(identity, msg.sender)
    {
        WeIdAttributeChanged(identity, WEID_KEY_CREATED, created, changed[identity], updated);
        WeIdAttributeChanged(identity, WEID_KEY_AUTHENTICATION, auth, changed[identity], updated);
        changed[identity] = block.number;
    }

    function setAttribute(
        address identity, 
        bytes32 key, 
        bytes value, 
        int updated
    ) 
        public 
        onlyOwner(identity, msg.sender)
    {
    	WeIdAttributeChanged(identity, key, value, changed[identity], updated);
        changed[identity] = block.number;
    }
    
    function isIdentityExist(
        address identity
    ) 
        public 
        constant 
        returns (bool) 
    {
        if (0x0 != identity && 0 != changed[identity]) {
            return true;
    }
        return false;
    }
}

这个合约简明易懂,其中涉及**事件(Event)、修饰器(modifier)、映射结构(mapping)**等知识点。

可以通过这篇文章对这些知识点进行了解:

https://mp.weixin.qq.com/s/pNIQ1QCPHF7MQGTRUMNoEA

4 在 Remix 上调试

下面就让我们来愉快地 Play 一下这个合约。打开 Remix:

http://remix.ethereum.org/

1)创建文件夹与合约

WeIdentity 源码分析 | 狗哥解码(一)

2)将 WeIdContract.sol 的内容复制过去

3)注释掉 modifier 相关的字句以方便调试

4)加上两个函数以方便调试

function stringToBytes32(string memory source) returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }

        assembly {
            result := mload(add(source, 32))
        }
    }
    
    function bytes32ToString(bytes32 x) constant returns (string) {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;
        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }
        bytes memory bytesStringTrimmed = new bytes(charCount);
        for (j = 0; j < charCount; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        return string(bytesStringTrimmed);
    }

我们在写合约和写代码的时候经常会遇到类型转换的问题。我的方式是将代码片段(Snippet)存在 Dash 里,结合 Alfred 进行调用。

WeIdentity 源码分析 | 狗哥解码(一)

5)合约的部署与调用

在 Js VM 中对合约进行部署,然后我们就可以尝试着去调用它了。

WeIdentity 源码分析 | 狗哥解码(一)

以 createWeId 参数为例:

a)在 https://vanity-eth.tk/ 生成一个以太坊地址

b)剩下参数随便填写个合法的

WeIdentity 源码分析 | 狗哥解码(一)

调用成功了,在右下角的控制台里我们可以看到日志等信息:

WeIdentity 源码分析 | 狗哥解码(一)

我们把刚才的地址放到 isIdentityExist 函数里,返回 true,身份信息的确存储上了。

WeIdentity 源码分析 | 狗哥解码(一)

5 以合约为引子阅读 SDK 源码

然而,我们并不满足于光看合约,我们还想知道 WeIdentity-SDK 是如何对合约进行调用的。刚才我们的 authcreatedupdated 等参数是随便填的。通过文档我们可以知道 created 和 updated 是时间戳,那么auth 具体是什么呢?文档中似乎没写,所以我们要看代码。

于是我们找到 weid-build-tools 中的 command 示例,这是整个链条中的最后一环:

……
import com.webank.weid.service.impl.WeIdServiceImpl;
import com.webank.weid.util.FileUtils;

/**
 * @author tonychen 2019/4/11
 */
public class CreateWeId {

    /**
     * log4j.
     */
    private static final Logger logger = LoggerFactory.getLogger(CreateWeId.class);

    private static WeIdService weIdService = new WeIdServiceImpl();
……

可以看到创建了一个 WeIdService 对象,根据这个线索我们去找 WeIdentity 项目中的对应实现。找到 WeIdServiceImpl.java 这个文件,我们可以看到它是通过调用 weIdServiceEngine 来实现的。

……
private ResponseData<Boolean> processCreateWeId(String weId, String publicKey,
        String privateKey) {

        String address = WeIdUtils.convertWeIdToAddress(weId);
        try {
            return weIdServiceEngine.createWeId(address, publicKey, privateKey);
……

于是我们找到 WeIdServiceEngineV2.java 文件,我们终于找到了链条的最末端:

……
public ResponseData<Boolean> createWeId(String weAddress, String publicKey, String privateKey) {

        String auth = new StringBuffer()
            .append(publicKey)
            .append(WeIdConstant.SEPARATOR)
            .append(weAddress)
            .toString();
        String created = DateUtils.getNoMillisecondTimeStampString();
        TransactionReceipt receipt;
        WeIdContract weIdContract =
            reloadContract(fiscoConfig.getWeIdAddress(), privateKey, WeIdContract.class);
        try {
            receipt = weIdContract.createWeId(
                weAddress,
                DataToolUtils.stringToByteArray(auth),
                DataToolUtils.stringToByteArray(created),
                BigInteger.valueOf(DateUtils.getNoMillisecondTimeStamp())
            ).send();
……

Okay,到这里,这根「链条」的分析就结束了。通过这样的一根根链条的分析,我们最终便能把握整个机器的「原理」。

WeIdentity 源码分析 | 狗哥解码(一)

狗哥解码是本公众号新开设的系列,旨在分析区块链世界里的一些重要项目。

一方面,本系列会提供项目的源码解读 —— 这是其他源码分析也能做到的。更重要的是另一方面,也是本系列的创新点:

本系列将会示范阅读源码的方法论。

在一个理想的、存在真空中的球形鸡的世界里,所有代码都有完善的文档,清楚地阐述每一个接口的用途与参数。但是,真实的世界中,代码不断敏捷迭代,文档和代码进行着永恒地「龟兔赛跑」。同时,前人留下的 永远不会修复的 TODO、缺乏注释的变量与各种奇技淫巧 也阻碍我们去理解源码。

在这种情况下,我们实际面临的是这样一种情况 —— 要在没有说明书的情况下去了解一个「机器(程序)」的内部运转原理,在此基础上再对其进行魔改与增强。

1 了解 WeIdentity

在阅读与分析 WeIdentity 的源码之前,首先阅读 WeIdentity 的官方文档 —— 永远先看官方文档而不是二手资料是一个好的习惯:

https://weidentity.readthedocs.io/

WeIdentity 是一套分布式多中心的技术解决方案,可承载实体对象(人或者物)的现实身份与链上身份的可信映射、以及实现实体对象之间安全的访问授权与数据交换。WeIdentity由微众银行自主研发并完全开源,秉承公众联盟链整合资源、交换价值、服务公众的理念,致力于成为链接多个垂直行业领域的分布式商业基础设施,促进泛行业、跨机构、跨地域间的身份认证和数据合作。

通过阅读文档,我们对 WeIdentity 建立起一个最粗浅的模型:

+---------------+   +--------------------+   +-----+
| FISCO BCOS 链 |---|  WeId Solidity 合约 |---| SDK |
+---------------+   +--------------------+   +-----+

在这个基础上,我们可以进行下一步了——先把示例流程跑通。

2 跑通示例流程

这个过程写在这篇文章里:

https://mp.weixin.qq.com/s/BySSeIkMstBXLTeZ2krIHA

通过跑通示例流程,我们可以更细致地理解这个「机器」的工作原理。

同时,如果仅局限于「使用」,而不涉及「魔改」和「增强」的话,在跑通这个流程后,已经可以上手开发相关的应用了。在开发应用的过程中,我们便会对它产生更多的理解,而无需先研究透彻再上手实践。

3 合约分析

示例流程走完后,我们就可以开始着手分析它的合约了:

https://github.com/WeBankFinTech/weid-contract

这份合约涉及多个文件,因此我们需要梳理合约结构。值得欣慰的是 WeIdentity 的文档比较完善,通过文档我们即能了解到合约结构:

https://fintech.webank.com/developer/docs/weidentity/docs/weidentity-contract-design.html

WeIdentity 源码分析 | 狗哥解码(一)

现在,我们要开始选择分析的第一个部分。结合合约结构和示例流程,我们可以先看下 WeIDContract 合约 —— 因为对它的理解并不依赖于其它模块。

pragma solidity ^0.4.4;
/*
 *       Copyright© (2018) WeBank Co., Ltd.
 *
 *       This file is part of weidentity-contract.
 *
 *       weidentity-contract is free software: you can redistribute it and/or modify
 *       it under the terms of the GNU Lesser General Public License as published by
 *       the Free Software Foundation, either version 3 of the License, or
 *       (at your option) any later version.
 *
 *       weidentity-contract is distributed in the hope that it will be useful,
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *       GNU Lesser General Public License for more details.
 *
 *       You should have received a copy of the GNU Lesser General Public License
 *       along with weidentity-contract.  If not, see <https://www.gnu.org/licenses/>.
 */

contract WeIdContract {

    mapping(address => uint) changed;

    modifier onlyOwner(address identity, address actor) {
         require (actor == identity);
         _;
    }

    bytes32 constant private WEID_KEY_CREATED = "created";
    bytes32 constant private WEID_KEY_AUTHENTICATION = "/weId/auth";

    event WeIdAttributeChanged(
        address indexed identity,
        bytes32 key,
        bytes value,
        uint previousBlock,
        int updated
    );

    function getLatestRelatedBlock(
        address identity
    ) 
        public 
        constant 
        returns (uint) 
    {
        return changed[identity];
    }

    function createWeId(
        address identity,
        bytes auth,
        bytes created,
        int updated
    )
        public
        onlyOwner(identity, msg.sender)
    {
        WeIdAttributeChanged(identity, WEID_KEY_CREATED, created, changed[identity], updated);
        WeIdAttributeChanged(identity, WEID_KEY_AUTHENTICATION, auth, changed[identity], updated);
        changed[identity] = block.number;
    }

    function setAttribute(
        address identity, 
        bytes32 key, 
        bytes value, 
        int updated
    ) 
        public 
        onlyOwner(identity, msg.sender)
    {
        WeIdAttributeChanged(identity, key, value, changed[identity], updated);
        changed[identity] = block.number;
    }

    function isIdentityExist(
        address identity
    ) 
        public 
        constant 
        returns (bool) 
    {
        if (0x0 != identity && 0 != changed[identity]) {
            return true;
    }
        return false;
    }
}

这个合约简明易懂,其中涉及 事件(Event)、修饰器(modifier)、映射结构(mapping) 等知识点。

可以通过这篇文章对这些知识点进行了解:

https://mp.weixin.qq.com/s/pNIQ1QCPHF7MQGTRUMNoEA

4 在 Remix 上调试

下面就让我们来愉快地 Play 一下这个合约。打开 Remix:

http://remix.ethereum.org/

1)创建文件夹与合约

WeIdentity 源码分析 | 狗哥解码(一)

2)将 WeIdContract.sol 的内容复制过去

3)注释掉 modifier 相关的字句以方便调试

4)加上两个函数以方便调试

function stringToBytes32(string memory source) returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }

        assembly {
            result := mload(add(source, 32))
        }
    }

    function bytes32ToString(bytes32 x) constant returns (string) {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;
        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }
        bytes memory bytesStringTrimmed = new bytes(charCount);
        for (j = 0; j < charCount; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        return string(bytesStringTrimmed);
    }

我们在写合约和写代码的时候经常会遇到类型转换的问题。我的方式是将代码片段(Snippet)存在 Dash 里,结合 Alfred 进行调用。

WeIdentity 源码分析 | 狗哥解码(一)

5)合约的部署与调用

在 Js VM 中对合约进行部署,然后我们就可以尝试着去调用它了。

WeIdentity 源码分析 | 狗哥解码(一)

以 createWeId 参数为例:

a)在 https://vanity-eth.tk/ 生成一个以太坊地址

b)剩下参数随便填写个合法的

WeIdentity 源码分析 | 狗哥解码(一)

调用成功了,在右下角的控制台里我们可以看到日志等信息:

WeIdentity 源码分析 | 狗哥解码(一)

我们把刚才的地址放到 isIdentityExist 函数里,返回 true,身份信息的确存储上了。

WeIdentity 源码分析 | 狗哥解码(一)

5 以合约为引子阅读 SDK 源码

然而,我们并不满足于光看合约,我们还想知道 WeIdentity-SDK 是如何对合约进行调用的。刚才我们的 authcreatedupdated 等参数是随便填的。通过文档我们可以知道 created 和 updated 是时间戳,那么auth 具体是什么呢?文档中似乎没写,所以我们要看代码。

于是我们找到 weid-build-tools 中的 command 示例,这是整个链条中的最后一环:

……
import com.webank.weid.service.impl.WeIdServiceImpl;
import com.webank.weid.util.FileUtils;

/**
 * @author tonychen 2019/4/11
 */
public class CreateWeId {

    /**
     * log4j.
     */
    private static final Logger logger = LoggerFactory.getLogger(CreateWeId.class);

    private static WeIdService weIdService = new WeIdServiceImpl();
……

可以看到创建了一个 WeIdService 对象,根据这个线索我们去找 WeIdentity 项目中的对应实现。找到 WeIdServiceImpl.java 这个文件,我们可以看到它是通过调用 weIdServiceEngine 来实现的。

……
private ResponseData<Boolean> processCreateWeId(String weId, String publicKey,
        String privateKey) {

        String address = WeIdUtils.convertWeIdToAddress(weId);
        try {
            return weIdServiceEngine.createWeId(address, publicKey, privateKey);
……

于是我们找到 WeIdServiceEngineV2.java 文件,我们终于找到了链条的最末端:

……
public ResponseData<Boolean> createWeId(String weAddress, String publicKey, String privateKey) {

        String auth = new StringBuffer()
            .append(publicKey)
            .append(WeIdConstant.SEPARATOR)
            .append(weAddress)
            .toString();
        String created = DateUtils.getNoMillisecondTimeStampString();
        TransactionReceipt receipt;
        WeIdContract weIdContract =
            reloadContract(fiscoConfig.getWeIdAddress(), privateKey, WeIdContract.class);
        try {
            receipt = weIdContract.createWeId(
                weAddress,
                DataToolUtils.stringToByteArray(auth),
                DataToolUtils.stringToByteArray(created),
                BigInteger.valueOf(DateUtils.getNoMillisecondTimeStamp())
            ).send();
……

Okay,到这里,这根「链条」的分析就结束了。通过这样的一根根链条的分析,我们最终便能把握整个机器的「原理」。

WeIdentity 源码分析 | 狗哥解码(一)

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 22分钟前
  • 阅读 ( 18 )
  • 学分 ( 0 )
  • 分类:FISCO BCOS

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Haskell

Haskell

Simon Thompson / Addison-Wesley / 1999-3-16 / GBP 40.99

The second edition of Haskell: The Craft of Functional Programming is essential reading for beginners to functional programming and newcomers to the Haskell programming language. The emphasis is on th......一起来看看 《Haskell》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具