在Fabric ChainCode中导入第三方包(以状态机为例)
在企业级应用开发中,经常会涉及到流程和状态,而有限状态机(FSM)则是对应的一种简单实现,如果复杂化,就上升到Workflow和BPM了。我们在Fabric ChainCode的开发过程中,也很可能涉及到状态机,这里我们就举一个例子,用FSM实现一个二级审批的状态转移。
我们有一个表单,员工填写表单是可以保存为Draft状态,提交后变成Submitted状态,然后在一级审批的时候,可以Approve或者Reject,同意了改为L1Approved,进入下一级审批,拒绝了那么就以Reject状态打回给起草人,二级审批人员也是有Approve和Reject两个操作,同意了状态就改为Complete,拒绝了就改为Reject。这是一个很常见的审批例子。
我们使用Go来开发ChainCode,那么可以采用https://github.com/looplab/fsm 这个FSM库。这个库也是Fabric官方采用的状态机库。下面是我的操作过程:
1.新建ChainCode项目并引入fsm库
我们新建一个项目fsmtest,并在其中建立住ChainCode文件:main.go,然后新建vendor文件夹,将https://github.com/looplab/fsm从GitHub clone下来,并放在vendor/github.com/looplab/fsm文件夹中,最终项目个文件结构如下:
2.定义FSM初始化函数
接下来打开main.go文件,除了编写ChainCode所必须使用的函数外,最主要的就是编写定义状态机转移的初始化函数了,我们根据前面流程图中的流程状态定义,我们可以写出如下的FSM初始化函数:
func InitFSM(initStatus string) *fsm.FSM{
f := fsm.NewFSM(
initStatus,
fsm.Events{
{Name: "Submit", Src: []string{"Draft"}, Dst: "Submited"},
{Name: "Approve", Src: []string{"Submited"}, Dst: "L1Approved"},
{Name: "Reject", Src: []string{"Submited"}, Dst: "Reject"},
{Name: "Approve", Src: []string{"L1Approved"}, Dst: "Complete"},
{Name: "Reject", Src: []string{"L1Approved"}, Dst: "Reject"},
},
fsm.Callbacks{},
)
return f;
}
3.在ChainCode中调用FSM Event
接下来我们在ChainCode重定义了4个函数,
- Draft
- Submit
- Approve
- Reject
于是我们可以在Invoke函数中定义4中情况:
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
fmt.Println("invoke is running " + function)
if function == "Draft" { //自定义函数名称
return t.Draft(stub, args) //定义调用的函数
} else if function == "Submit" {
return FsmEvent(stub,args,"Submit")
} else if function == "Approve" {
return FsmEvent(stub,args,"Approve")
} else if function == "Reject" {
return FsmEvent(stub,args,"Reject")
}
return shim.Error("Received unknown function invocation")
}
其中Draft函数就是把表单状态初始化为Draft并保存到数据库,并不涉及状态的修改:
func (t *SimpleChaincode) Draft(stub shim.ChaincodeStubInterface, args []string) pb.Response{
formNumber:=args[]
status:="Draft"
stub.PutState(formNumber,[]byte(status))//初始化Draft状态的表单保存到StateDB
return shim.Success([]byte(status))
}
而其他操作都涉及状态的修改,由于我们引入了状态机,所以我们只需要初始化状态机,并发送对应的Event即可,而最新的状态是由状态机根据我们的定义而获得的。所以我们虽然有3个操作,去只需要一个函数就能完成,并没有冗余的if else判断,这就是状态机的优势!
func FsmEvent(stub shim.ChaincodeStubInterface, args []string,event string) pb.Response{
formNumber:=args[]
bstatus,err:=stub.GetState(formNumber)//从StateDB中读取对应表单的状态
if err!=nil{
return shim.Error("Query form status fail, form number:"+formNumber)
}
status:=string(bstatus)
fmt.Println("Form["+formNumber+"] status:"+status)
f:=InitFSM(status)//初始化状态机,并设置当前状态为表单的状态
err=f.Event(event)//触发状态机的事件
if err!=nil{
return shim.Error("Current status is "+status+" does not support envent:"+event)
}
status=f.Current()
fmt.Println("New status:"+status)
stub.PutState(formNumber,[]byte(status))//更新表单的状态
return shim.Success([]byte(status));//返回新状态
}
4.部署并测试ChainCode
现在状态写完了,我们需要进行测试,我们可以git push到GitHub,然后到Ubuntu中git clone下来,也可以通过rz命令,把Windows中开发好的ChainCode上传到Ubuntu中,不管什么方法,最终我们整个ChainCode项目放在了~/go/src/github.com/hyperledger/fabric/examples/chaincode/go/fsmtest这个文件夹下。
然后使用e2e_cli下面的network_setup.sh up命令启动整个Fabric网络。启动Fabric网络后,我们需要进入CLI进行部署和合适fsmtest:
docker exec -it cli bash
然后安装并初始化我们的ChainCode:
peer chaincode install -n fsmtest -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/fsmtest
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peer chaincode instantiate -o orderer.example.com: --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -v 1.0 -c '{"Args":[]}'
现在安装完毕后,我们可以起草一个报销单EXP1:
peer chaincode invoke -o orderer.example.com: --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -c '{"Args":["Draft","EXP1"]}'
我们可以看到系统返回的结果:
现在状态是Draft,然后我们试一试提交报销单EXP1:
peer chaincode invoke -o orderer.example.com: --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -c '{"Args":["Submit","EXP1"]}'
我们看到状态已经改为Submitted了。接下来我们进一步一级审批通过,二级审批通过,都是执行相同的命令:
peer chaincode invoke -o orderer.example.com: --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -c '{"Args":["Approve","EXP1"]}'
这个时候,状态已经是Complete了,如果我们再次调用Approve函数会怎么样?因为我们在状态机中并没有定义这么一个流转事件,所以肯定是报错,无法正常执行的:
大家如果也在做这个实验,也可以去测试Reject函数,会得到想要的结果的。
5.总结
总的来说,在Fabric的ChainCode开发中,引入第三方的库可以方便我们编写更强大的链上代码。而这个FSM虽然简单,但是也可以很好的将状态流转的逻辑进行集中,避免了在状态流转时编写大量的Ugly的代码,让我们在每个函数中更专注于业务逻辑,而不是麻烦的状态转移。最后直接粘贴出我的完整ChainCode 源码,方便大家直接使用。
在Fabric ChainCode中导入第三方包(以状态机为例)的更多相关文章
- PyCharm中导入第三方包
在Windows中的PyCharm中导入模块的方法 1.在file-->setting-->project interpreter中 2,点击右上角加号,搜索要添加的第三方库的名称,得到后 ...
- 向Pycharm中导入第三方包 && 更改Pycharm上镜像源
一.Pycharm本身导包 下载成功会这个样子(如下图) 但是有时因为包的版本太高,代码运行出错,此时需要选中右下角的Specify version,然后选择想要的版本即可 如果还出错,那就在命令行下 ...
- Android Studio系列教程五--Gradle命令详解与导入第三方包
Android Studio系列教程五--Gradle命令详解与导入第三方包 2015 年 01 月 05 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://s ...
- Android Studio中导入第三方库
之前开发Android都是使用的eclipse,近期因为和外国朋友Timothy一起开发一款应用,他是从WP平台刚切换使用Android的,使用的开发环境时Android Studio,为了便于项目的 ...
- json库的编译方法和vs2010中导入第三方库的方法
json库的编译方法和vs2010中导入第三方库的方法 一.去相应官网下载json.cpp文件 Jsoncpp下载:https://sourceforge.net/projects/jsoncpp/ ...
- Thinkphp5.1 导入第三方包的问题
一般刚接触tp5.1的,会很不适应,虽然版本号只是比5.0多了0.1,但是差别挺大,废弃了不少方法,官方的教程又很简单,很多东西没说全,在此鄙视一下框架作者,最起码体谅一下小白嘛,搞了好多天才把5.1 ...
- 详细地jsoncpp编译方法 和 vs2010中导入第三方库的方法
详细地jsoncpp编译方法 和 vs2010中导入第三方库的方法 一 编译链接 1 在相应官网下载jsoncpp 2 解压得到jsoncpp-src-0.5.0文件 3 打开jsoncpp-src- ...
- 在 Ionic2 TypeScript 项目中导入第三方 JS 库
原文发表于我的技术博客 本文分享了在Ionic2 TypeScript 项目中导入第三方 JS 库的方法,供参考. 原文发表于我的技术博客 1. Typings 的方式 因在 TypeScript 中 ...
- 在java工程中导入jar包的注意事项
在java工程中导入jar包后一定要bulid path,不然jar包不可以用.而在java web工程中导入jar包后可以不builld path,但最好builld path.
随机推荐
- Java提高班(二)深入理解线程池ThreadPool
本文你将获得以下信息: 线程池源码解读 线程池执行流程分析 带返回值的线程池实现 延迟线程池实现 为了方便读者理解,本文会由浅入深,先从线程池的使用开始再延伸到源码解读和源码分析等高级内容,读者可根据 ...
- vim编辑器详解(week1_day3)--技术流ken
vi编辑器 作用:编辑文本文件中的内容的工具 命令历史 末行模式中,以:和/开头的命令都有历史纪录,可以首先键入:或/然后按上下箭头来选择某个历史命令. 启动vim 在命令行窗口中输入以下命令即可 v ...
- vb.net連接SQL数据库
'導入命名空間Imports System.Data.SqlClient '定義變量 Dim Sql As String 'SQL字串 Dim Sqlado As SqlConnection '连接数 ...
- C++系列总结——mutable关键字
介绍 mutable的中文意思是易变的,是C++的一个关键字.它的作用就是允许修改被const修饰的对象的成员变量. 常用场景 什么情况下我们会使用到mutable? 一般我们会用const修饰get ...
- MySql 双主多从配置指导
MySql 双主多从配置指导 一.背景 互联网项目为了数据的可靠性和架构的可拓展性经常会用到双主多从的数据库,来实现数据的备份.负载均衡和突发状况时数据库切换. 二.思路 配置两台数据库A.B互为主从 ...
- 如何搭建一个VUE项目
搭建环境 搭建node环境 下载 1.进入node.js官方网站下载页,点击下图中框出位置,进行下载即可,当前版本为8.9.4,下载网址为:https://nodejs.org/zh-cn/downl ...
- BZOJ3122: [Sdoi2013]随机数生成器(BSGS)
题意 题目链接 Sol 这题也比较休闲. 直接把\(X_{i+1} = (aX_i + b) \pmod P\)展开,推到最后会得到这么个玩意儿 \[ a^{i-1} (x_1 + \frac{b}{ ...
- 2019-01-28 [日常]Beyond的歌里最多是"唏嘘"吗? - Python分词+词频
看了一个Beyond的纪录片, 提到这个. 觉得心有不甘, 于是搜集了24首歌词, 用Python做了简单分词和词频统计. 源码(包括歌词)在: program-in-chinese/study 统计 ...
- 一种提升连接Dynamics 365性能的方法
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复256或者20170512可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- 2019年1月份A项目面试纪要
2019年1月份A项目面试纪要 本周二(1月22号),笔者接到了A项目的电话面试.这个面试来自A项目的客户,客户的后勤模块的几个顾问组成阵容强大的面试官团队.参加这个面试,让笔者感触良多,自己虽然在S ...