最近开始研究区块链,对这个新兴的技术有了基本概念上的了解,所以打算基于一个开源项目做做实验。如果是做数字货币,那么比特币的源代码是最好的了,不过这算是区块链1.0吧,已经有很多改进的竞争币和山寨币出来了,所以打算对区块链2.0,也就是智能合约入手。

智能合约比较成功的就是以太坊了。以太坊主要是公有链,其实对企业应用来说并不是特别合适,而且本身并没有权限控制功能,面向企业的,主要还是超级账本HyperLedger的Fabric和刚刚开源出来的R3的Corda。关于这些项目的应用场景和区别,我觉得这篇文章写的比较好:http://geek.csdn.net/news/detail/134967

经过比较,觉得Fabric目前比较合适,所以就以这个项目为基础,学习智能合约。

一、环境准备

1.1 安装VirtualBox并在其中安装好Ubuntu

这一步其实没啥好说的,下载好最新版的VirtualBox,下载Ubuntu Server,我用的是16.10 X64。在安装完Ubuntu后,需要保证apt source是国内的,不然如果是国外的话会很慢很慢的。具体做法是

sudo vi /etc/apt/sources.list

打开这个apt源列表,如果其中看到是http://us.xxxxxx之类的,那么就是外国的,如果看到是http://cn.xxxxx之类的,那么就不用换的。我的是美国的源,所以需要做一下批量的替换。在命令模式下,输入:

:%s/us./cn./g

就可以把所有的us.改为cn.了。然后输入:wq即可保存退出。

sudo apt-get update

更新一下源。

然后安装ssh,这样接下来就可以用putty或者SecureCRT之类的客户端远程连接Ubuntu了。

sudo apt-get install ssh

1.2 安装Docker

安装Docker也会遇到外国网络慢的问题,幸好国内有很好的镜像,推荐DaoClound,安装Docker的命令是:

curl -sSL https://get.daocloud.io/docker | sh
安装完成后,运行以下脚本将当前用户添加到Docker的组中
sudo usermod -aG docker studyzy
重新登录当前用户,接下来修改 Docker 服务配置(/etc/default/docker 文件)。
sudo vi /etc/default/docker
添加以下内容:
DOCKER_OPTS="$DOCKER_OPTS -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --api-cors-header='*'"
接下来就需要设置国内的Docker镜像地址,需要注册一个账号,然后在加速器页面提供了设置Docker镜像的脚本,加速器页面是:
https://www.daocloud.io/mirror 我提供的脚本是:
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://d4cc5789.m.daocloud.io
运行完脚本后,重启Docker服务
sudo service docker restart

1.3 安装docker-compose

Docker-compose是支持通过模板脚本批量创建Docker容器的一个组件。在安装Docker-Compose之前,需要安装Python-pip,运行脚本:

sudo apt-get install python-pip

安装完成后,接下来从DaoClound安装Docker-compose,运行脚本:

curl -L https://get.daocloud.io/docker/compose/releases/download/1.10.1/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
sudo mv ~/docker-compose /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

二、Fabric部署

2.1 下载Fabric镜像

Fabric的Docker镜像是在https://hub.docker.com/r/hyperledger/ 我们要做实验主要用到peer,baseimage,membersrvc,先现在Peer和membersrvc,这两个镜像提供了latest版本,所以直接pull下来即可。

docker pull hyperledger/fabric-peer
docker pull hyperledger/fabric-membersrvc

但是baseimage是没有latest版本,所以我们可以下载一个新一点的版本,然后rename成latest。

docker pull hyperledger/fabric-baseimage:x86_64-0.3.
docker tag hyperledger/fabric-baseimage:x86_64-0.3. hyperledger/fabric-baseimage:latest

现在我们运行docker images命令,可以看到我们准备好的镜像:

REPOSITORY                      TAG                 IMAGE ID            CREATED             SIZE
hyperledger/fabric-baseimage    latest              f4751a503f02        7 days ago          1.27 GB

hyperledger/fabric-baseimage    x86_64-0.3.0        f4751a503f02        7 days ago          1.27 GB

hyperledger/fabric-membersrvc   latest              b3654d32e4f9        3 months ago        1.42 GB

hyperledger/fabric-peer         latest              21cb00fb27f4        3 months ago        1.42 GB

2.2 使用Git下载Docker-compose模板

如果没有安装Git,那么需要先安装Git,安装Git很简单:

sudo apt-get install git

感谢yeasy提供的很好的HyperLedger的模板,我们先克隆到本地:

git clone https://github.com/yeasy/docker-compose-files

2.3 以PBFT模式启动Fabric

先进入Git下载下来的Docker-compose目录:

cd docker-compose-files/hyperledger/0.6/pbft/

这里提供了多种模式的启动方案,一种是启动4个节点的Peer,没有权限认证:4-peers.yml 另一种是在4节点Peer的基础上,再加上MembershipService节点,也就是需要权限认证的:4-peers-with-membersrvc.yml 另外还有再进一步,提供了web的Explorer的:4-peers-with-membersrvc-explorer.yml

这里我们就简单点,直接忽略掉MembershipService和Explorer,只启用4个节点的PBFT:

docker-compose -f -peers.yml up

系统会打印出启动的日志:

Creating network "pbft_default" with the default driver
Creating pbft_vp0_1
Creating pbft_vp3_1
Creating pbft_vp2_1
Creating pbft_vp1_1
……

至此,我们的环境搭建完毕,接下来我们就可以在上面跑链上代码了。

三、测试Fabric

3.1 在CLI中测试Example02

我们前面创建了4个容器,开启另外一个命令行窗口,输入docker ps命令,可以看到当前容器的状态:
CONTAINER ID        IMAGE                            COMMAND             CREATED             STATUS              PORTS                                   NAMES

2131cede4ade        hyperledger/fabric-peer:latest   "peer node start"   3 minutes ago       Up 3 minutes        7050-7059/tcp                           pbft_vp1_1

5acea88f21bc        hyperledger/fabric-peer:latest   "peer node start"   3 minutes ago       Up 3 minutes        7050-7059/tcp                           pbft_vp2_1

546b103d904d        hyperledger/fabric-peer:latest   "peer node start"   3 minutes ago       Up 3 minutes        7050-7059/tcp                           pbft_vp3_1

327ab874b2e3        hyperledger/fabric-peer:latest   "peer node start"   3 minutes ago       Up 3 minutes        0.0.0.0:7050->7050/tcp, 7051-7059/tcp   pbft_vp0_1

这里我们可以看到,最后一个容器pbft_vp0_1其启用了端口映射的,容器上面的7050端口会映射到Ubuntu的7050端口上。我们要执行命令行代码,需要先连接到这个容器内部:

docker exec -it pbft_vp0_1 bash

进入容器后,命令行会变为:root@vp0:/opt/gopath/src/github.com/hyperledger/fabric#

这里的容器已经帮我们把测试代码都放在了容器里面,所以我们不需要再下载测试代码。

3.1.1部署Go语言的ChainCode并初始化

下面我们部署Example02到Fabric上:

peer chaincode deploy -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -c '{"Function":"init", "Args": ["a","100", "b", "200"]}'

这个示例是初始化两个账户a和b,a有余额100元,b有余额200元,这是运行结果:

root@vp0:/opt/gopath/src/github.com/hyperledger/fabric# peer chaincode deploy -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -c '{"Function":"init", "Args": ["a","100", "b", "200"]}'
::12.187 [chaincodeCmd] chaincodeDeploy -> INFO Deploy result: type:GOLANG chaincodeID:<path:"github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02" name:"ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539" > ctorMsg:<args:"init" args:"a" args:"" args:"b" args:"" >
Deploy chaincode: ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539
::12.188 [main] main -> INFO Exiting.....

这里我们可以看到已经部署成功,并返回了ChainCode的ID:ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539

3.1.2查询ChainCode

下面我们把这个ID放入一个变量中:

CC_ID="ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539"

下面我们来查询一下a账户的余额:

peer chaincode query -n ${CC_ID} -c '{"Function": "query", "Args": ["a"]}'

这是运行结果:

root@vp0:/opt/gopath/src/github.com/hyperledger/fabric# peer chaincode query -n ${CC_ID} -c '{"Function": "query", "Args": ["a"]}'
::17.780 [chaincodeCmd] chaincodeInvokeOrQuery -> INFO Successfully queried transaction: chaincodeSpec:<type:GOLANG chaincodeID:<name:"ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539" > ctorMsg:<args:"query" args:"a" > >
Query Result:
::17.781 [main] main -> INFO Exiting.....

可以看到查询结果是100元。

注意:这里如果遇到了抛出异常:

LedgerError - ResourceNotFound: ledger: resource not found

那么就得看log,到底是什么地方错了,我们可以切换回docker-compose的那个窗口,那个窗口会打印错误日志,或者我们再打开一个窗口,运行命令:

docker logs -f pbft_vp0_1

查看peer日志,找到原因。我之前一直遇到这个异常,后来发现是baseimage没有latest版的造成的,所以2.1步骤不能出错。

3.1.3调用ChainCode

接下来,我们让a给b转账10元,运行命令:

peer chaincode invoke -n ${CC_ID} -c '{"Function": "invoke", "Args": ["a", "b", "10"]}'

这是调用后的结果:

root@vp0:/opt/gopath/src/github.com/hyperledger/fabric# peer chaincode invoke -n ${CC_ID} -c '{"Function": "invoke", "Args": ["a", "b", "10"]}'
::19.903 [chaincodeCmd] chaincodeInvokeOrQuery -> INFO Successfully invoked transaction: chaincodeSpec:<type:GOLANG chaincodeID:<name:"ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539" > ctorMsg:<args:"invoke" args:"a" args:"b" args:"" > > (94c9cbd9-ea04-436f-9cf8-3436303554d2)
::19.904 [main] main -> INFO Exiting.....

3.1.4检查调用ChainCode后的结果

现在已经转账完毕,我们再来查询一下a账户的余额:

peer chaincode query -n ${CC_ID} -c '{"Function": "query", "Args": ["a"]}'

查询结果:

root@vp0:/opt/gopath/src/github.com/hyperledger/fabric# peer chaincode query -n ${CC_ID} -c '{"Function": "query", "Args": ["a"]}'
::33.937 [chaincodeCmd] chaincodeInvokeOrQuery -> INFO Successfully queried transaction: chaincodeSpec:<type:GOLANG chaincodeID:<name:"ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539" > ctorMsg:<args:"query" args:"a" > >
Query Result:
::33.937 [main] main -> INFO Exiting.....

可以看到,a账户变成90元了。

3.2 在REST API中测试Example02

前面我们已经说到,容器的7050端口会映射成Ubuntu的7050端口,我们在Ubuntu下,运行ifconfig,可以看到Ubuntu的IP,然后我们回到Windows,就可以通过REST的Client来测试,这里我喜欢用Chrome的插件DHC,很好用,强烈推荐!不过要翻墙才能装。

这里我Ubuntu的IP是192.168.100.129,下面就用DHC进行REST API的Example02部署。

3.2.1通过REST API部署GO语言的ChainCode

POST 192.168.100.129:7050/chaincode

Body是:

{
"jsonrpc": "2.0",
"method": "deploy",
"params": {
"type": 1,
"chaincodeID":{
"path":"github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
},
"ctorMsg": {
"function":"init",
"args":["a", "1000", "b", "2000"]
}
},
"id": 1
}

这里为了区别,我们把a账户初始化1000元,b账户初始化2000元。返回的结果是:

{
"jsonrpc": "2.0",
"result":{
"status": "OK",
"message": "04233c6dd8364b9f0749882eb6d1b50992b942aa0a664182946f411ab46802a88574932ccd75f8c75e780036e363d52dd56ccadc2bfde95709fc39148d76f050"
},
"id": 1
}

这里04233c6dd8364b9f0749882eb6d1b50992b942aa0a664182946f411ab46802a88574932ccd75f8c75e780036e363d52dd56ccadc2bfde95709fc39148d76f050就是部署后的ChainCodeID。

3.2.2通过REST API查询ChainCode

POST 192.168.100.129:7050/chaincode

Body内容是:

{
"jsonrpc": "2.0",
"method": "query",
"params": {
"type": 1,
"chaincodeID":{
"name":"04233c6dd8364b9f0749882eb6d1b50992b942aa0a664182946f411ab46802a88574932ccd75f8c75e780036e363d52dd56ccadc2bfde95709fc39148d76f050"
},
"ctorMsg": {
"function":"query",
"args":["a"]
}
},
"id": 2
}

系统返回的结果是:

{
"jsonrpc": "2.0",
"result":{
"status": "OK",
"message": "1000"
},
"id": 2
}

一切正常,返回a账户的1000元。

3.2.3通过REST API调用ChainCode

我们试着从a向b转账100元:

POST 192.168.100.129:7050/chaincode

Body内容是:

{
"jsonrpc": "2.0",
"method": "invoke",
"params": {
"type": 1,
"chaincodeID":{
"name":"04233c6dd8364b9f0749882eb6d1b50992b942aa0a664182946f411ab46802a88574932ccd75f8c75e780036e363d52dd56ccadc2bfde95709fc39148d76f050"
},
"ctorMsg": {
"function":"invoke",
"args":["a", "b", "100"]
}
},
"id": 3
}

返回的结果:

{
"jsonrpc": "2.0",
"result":{
"status": "OK",
"message": "2ac78b5f-6d35-400d-b7c4-75ef81e14d3e"
},
"id": 3
}

3.2.4通过REST API检查调用ChainCode后的结果

这里我们来查询一下b账户。

POST 192.168.100.129:7050/chaincode

Body内容改为:

{
"jsonrpc": "2.0",
"method": "query",
"params": {
"type": 1,
"chaincodeID":{
"name":"04233c6dd8364b9f0749882eb6d1b50992b942aa0a664182946f411ab46802a88574932ccd75f8c75e780036e363d52dd56ccadc2bfde95709fc39148d76f050"
},
"ctorMsg": {
"function":"query",
"args":["b"]
}
},
"id": 4
}

返回结果:

{
"jsonrpc": "2.0",
"result":{
"status": "OK",
"message": "2100"
},
"id": 4
}

一切正常,b账户果然真假了100元。

关于更多的REST API,我们可以参考这里:https://github.com/hyperledger-archives/fabric/blob/master/docs/API/CoreAPI.md#rest-api

3.3测试Java版Chain Code

Fabric除了支持本身的Go语言的ChainCode,也可以支持其他语言,比如最常用的Java语言。Fabric的源代码中也提供了Java示例,这里我们就用SimpleSample这个示例:

https://github.com/hyperledger/fabric/tree/master/examples/chaincode/java/SimpleSample

3.3.1在CLI中部署该Java代码的ChainCode到Fabric

命令是:

peer chaincode deploy -l java -p /opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample -c '{"Function":"init", "Args": ["a","100", "b", "200"]}'

运行结果为:

root@vp0:/opt/gopath/src/github.com/hyperledger/fabric# peer chaincode deploy -l java -p /opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample -c '{"Function":"init", "Args": ["a","100", "b", "200"]}'
09:20:16.857 [chaincodeCmd] chaincodeDeploy -> INFO 001 Deploy result: type:JAVA chaincodeID:<path:"/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample" name:"0f5b1d65041bc6d500bd0f1cab50eb6154c291ef0f4596d64b6797e8ef8f7c34a179b5a2cea82253ff3d74e768512fe0481503eadcf13d18f9761bbb8133efd0" > ctorMsg:<args:"init" args:"a" args:"100" args:"b" args:"200" >
Deploy chaincode: 0f5b1d65041bc6d500bd0f1cab50eb6154c291ef0f4596d64b6797e8ef8f7c34a179b5a2cea82253ff3d74e768512fe0481503eadcf13d18f9761bbb8133efd0
09:20:16.857 [main] main -> INFO 002 Exiting.....

接下来的各种查询,调用都是差不多的,我就不再累述了。

3.3.2通过REST API部署Java ChainCode到Fabric

POST 192.168.100.129:7050/chaincode

Body为

{
"jsonrpc": "2.0",
"method": "deploy",
"params": {
"type": 4,
"chaincodeID":{
"path":"/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample"
},
"ctorMsg": {
"function":"init",
"args":["a", "1000", "b", "2000"]
}
},
"id": 1
}

系统返回的结果为:

{
"jsonrpc": "2.0",
"result":{
"status": "OK",
"message": "27cb2925013a5e8f27b41be748e6767c3fbc7bfdfe2453c2640f9069e75c4db38735fa3b6b8cac78e212a1c97193f3bfb2f9b810ce0a11f437a96b330d508fbd"
},
"id": 1
}

这里需要注意的是type:4,不再是1。1是Go语言的,而Java语言是4.接下来的操作也是类似的了,我就不累述了。

总的来说,Fabric基于Docker容器技术,部署的ChainCode在运行时会基于baseimage重新创建Docker容器,运行的链上代码越多,容器就会越多。运行docker ps会看到很多容器被创建。docker images也可以看到多了很多镜像。需要注意清理。

在Ubuntu中部署并测试HyperLedger Fabric 0.6的更多相关文章

  1. 在Ubuntu中部署并测试Fabric 1.0 Beta

    [更新:1.0Beta已经是过去式了,现在出了1.0.0的正式版,请大家参照 http://www.cnblogs.com/studyzy/p/7437157.html  安装Fabric 1.0.0 ...

  2. ubuntu中编译安装gcc 9.2.0

    一切都和其他源码安装软件是一样的: 一.下载源代码: http://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.xz 二.解压文件 tar xvf gcc- ...

  3. Ubuntu中部署Django项目的配置与链接MySQL

    Django的简介 MVT模式的介绍创建项目的虚拟环境 本次使用的是pip安装 一.更新 sudo apt update 二.安装pip sudo apt install python3-pip 三. ...

  4. Centos 7 中 部署 asp.net core 3.0 + nginx + mongodb 的一些新手简单入门,非docker

    目录 零.准备工作 一.部署Mongodb 1.安装Mongodb 2.创建mongodb的数据目录 3.设置目录权限 4.设置mongodb启动 5.修改mongodb的配置文件 6.启动Mongo ...

  5. rails创建项目,部署,测试流程(rails5.0+ruby2.3.1)

    rails new test_app --skip-test-unit 不生成默认的test,稍后用rspeccd test_app 修改Gemfile(大部分为自动生成) source 'https ...

  6. 记一次Docker中部署Asp.Net Core 3.0的踩坑过程

    最近公司打算重构目前直销报单系统到微信小程序中,目前的系统只能在PC上面使用,这两年也搞过App端,但是由于人员流动和公司架构调整最后都不了了之,只留下一堆写了一半的接口.以前的接口依然是使用Asp. ...

  7. Hyperledger Fabric Chaincode for Operators——实操智能合约

    什么是Chaincode(智能合约)? chaincode是一个程序,它是使用Go语言编写的,最终在Java等其他编程语言中实现了指定的接口.chaincode运行在一个被背书peer进程独立出来的安 ...

  8. Hyperledger Fabric 1.0 从零开始(一)

    在HyperLedger/Fabric发布0.6的时候,公司就已经安排了一个团队研究这一块,后来也请IBM的专家组过来培训了一批人,不幸的是,这批人后来全走了,然后1.0就发布了.自从2017年7月H ...

  9. 不使用pvc的方式在K8S中部署apisix-gateway

    不使用pvc的方式在K8S中部署apisix-gateway 简介 我的apisix使用etcd作为数据存储服务器,官方的使用pvc方式或者docker-compose的方式,对于新手不太友好,本篇是 ...

随机推荐

  1. 排错-Loadrunner录制打不开浏览器解决方法

    排错-Loadrunner录制打不开浏览器解决方法 by:授客 QQ:1033553122 问题描述: 采用自带的web测试站点http://127.0.0.1:1080/WebTours/,进行录制 ...

  2. eclipse安装其他颜色主题包

    eclipse安装其他颜色主题包: 用Help-Install new software安装的时候,work with的URL是 http://eclipse-color-theme.github.c ...

  3. Vue项目用于Ios和Android端开发

    起因 前公司商城App项目使用的是H5开发,有微信公众号.Ios和Android三个版本,H5版本是自己写的一套框架,已经用了有些年头了,承载不下不断涌现出的新需求.而Ios和Android端通过we ...

  4. c++函数集锦

    1.标准C++库字符串类std::string的用法 begin       得到指向字符串开头的Iterator end       得到指向字符串结尾的Iterator rbegin        ...

  5. 结对编程--四则运算(Java)萧英杰 夏浚杰

    结对编程--四则运算(Java)萧英杰 夏浚杰 Github项目地址 功能要求 题目:实现一个自动生成小学四则运算题目的命令行程序 使用 -n 参数控制生成题目的个数(实现) 使用 -r 参数控制题目 ...

  6. backup是个相对论

    工作互备,是很多团队领导者都关注的事情.显然,当一项任务由两个(甚至两个以上的人)来完成,当任务交付使用后出现问题时,不会因为其中某一个成员的缺席而导致问题一时处理不了.如果某个任务只是由一个人来担当 ...

  7. SQL Server Replication的分发服务器的快照文件夹位置查找

    SQL Server分发服务器配置中,需要配置快照文件夹(Snapshot Folder),用于存储发布的数据和架构文件的工作目录,那么如何查找当前SQL Server数据库服务器的分发服务器的快照文 ...

  8. SqlServer执行Insert命令同时判断目标表中是否存在目标数据

    针对于已查询出数据结果, 且在程序中执行Sql命令, 而非数据库中的存储过程 INSERT INTO TableName (Column1, Column2, Column3, Column4, Co ...

  9. 记一次 MySQL semaphore crash 的分析(爱可生)

    文章来源:爱可生云数据库作者:洪斌 DBA应该对InnoDB: Semaphore wait has lasted > 600 seconds. We intentionally crash t ...

  10. [Hive_8] Hive 设计优化

    0. 说明 在 Hive 中,数据库是一个文件夹,表也是文件夹 partition,是一个字段,是文件 前提:在 Hive 进行 where 子句查询的时候,会将条件语句和全表进行比对,搜索出所需的数 ...