在Fabric ChainCode中導入第三方包(以狀態機爲例)

在企業級應用開發中,常常會涉及到流程和狀態,而有限狀態機(FSM)則是對應的一種簡單實現,若是複雜化,就上升到Workflow和BPM了。咱們在Fabric ChainCode的開發過程當中,也極可能涉及到狀態機,這裏咱們就舉一個例子,用FSM實現一個二級審批的狀態轉移。git

咱們有一個表單,員工填寫表單是能夠保存爲Draft狀態,提交後變成Submitted狀態,而後在一級審批的時候,能夠Approve或者Reject,贊成了改成L1Approved,進入下一級審批,拒絕了那麼就以Reject狀態打回給起草人,二級審批人員也是有Approve和Reject兩個操做,贊成了狀態就改成Complete,拒絕了就改成Reject。這是一個很常見的審批例子。github

image

咱們使用Go來開發ChainCode,那麼能夠採用https://github.com/looplab/fsm 這個FSM庫。這個庫也是Fabric官方採用的狀態機庫。下面是個人操做過程:docker

1.新建ChainCode項目並引入fsm庫

咱們新建一個項目fsmtest,並在其中創建住ChainCode文件:main.go,而後新建vendor文件夾,將https://github.com/looplab/fsm從GitHub clone下來,並放在vendor/github.com/looplab/fsm文件夾中,最終項目個文件結構以下:數據庫

image

2.定義FSM初始化函數

接下來打開main.go文件,除了編寫ChainCode所必須使用的函數外,最主要的就是編寫定義狀態機轉移的初始化函數了,咱們根據前面流程圖中的流程狀態定義,咱們能夠寫出以下的FSM初始化函數:bash

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[0]
   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[0]
   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:oop

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:7050 --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -v 1.0 -c '{"Args":[]}'

如今安裝完畢後,咱們能夠起草一個報銷單EXP1:測試

peer chaincode invoke -o orderer.example.com:7050  --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -c '{"Args":["Draft","EXP1"]}'

咱們能夠看到系統返回的結果:

image

如今狀態是Draft,而後咱們試一試提交報銷單EXP1:

peer chaincode invoke -o orderer.example.com:7050  --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -c '{"Args":["Submit","EXP1"]}'

image

咱們看到狀態已經改成Submitted了。接下來咱們進一步一級審批經過,二級審批經過,都是執行相同的命令:

peer chaincode invoke -o orderer.example.com:7050  --tls true --cafile $ORDERER_CA -C mychannel -n fsmtest -c '{"Args":["Approve","EXP1"]}'

image

這個時候,狀態已是Complete了,若是咱們再次調用Approve函數會怎麼樣?由於咱們在狀態機中並無定義這麼一個流轉事件,因此確定是報錯,沒法正常執行的:

image

你們若是也在作這個實驗,也能夠去測試Reject函數,會獲得想要的結果的。

5.總結

總的來講,在Fabric的ChainCode開發中,引入第三方的庫能夠方便咱們編寫更強大的鏈上代碼。而這個FSM雖然簡單,可是也能夠很好的將狀態流轉的邏輯進行集中,避免了在狀態流轉時編寫大量的Ugly的代碼,讓咱們在每一個函數中更專一於業務邏輯,而不是麻煩的狀態轉移。最後直接粘貼出個人完整ChainCode 源碼,方便你們直接使用。

相關文章
相關標籤/搜索