fabirc链码开发及在开发模式下的调试

一 前言

Fabric Chaincode是智能合约在Fabric上的实现方式,是与Fabric区块链交互的唯一渠道,也是生成Transaction的唯一来源。
开发语言:go、java
本文中选用go
Fabric节点运行模式有两种:

  • 一般模式
    Chaincode运行在docker容器里,这种方式开发调试过程非常繁杂:部署—调试—修改—创建docker镜像—部署—···
  • 开发模式
    相对容易:部署—调试—修改—部署—···

本文中采用开发模式调试链码。

二 开发环境

需要fabric-samples源码
下载好后进入到chaincode目录,新建一个mychaincode文件夹,接下来,我们将会在这个目录中编写我们的ChainCode。
还有一个chaincode-docker-devmode目录。在这个目录中,我们可以借助构建自带区块链样例网络时已经预先生成好的order和channel来启动“开发者模式”。这样,用户可以立即编译chaincode并调用函数。
接下来,我们将会在这个目录中调试我们编写的ChainCode。包括编译,安装,实例化等等。

三 编写Chaincode

在mychaincode目录下新建一个go文件,编写相应内容,给出一个建立链路合约(仿照官方转账示例)的示例代码如下:

package main

import (
    "fmt"
    "encoding/json"
    "github.com/hyperledger/fabric/core/chaincode/shim"
    pb "github.com/hyperledger/fabric/protos/peer"
    "time"
)

// 合约状态
const (
    BillInfo_State_Publish = "Publish"
)


// 账单数据结构
type Bill struct {
    BillId string `json:"bill_id"`
    PurchaseUserCode string `json:"purchase_user_code"`
    BidingStartTime string `json:"biding_start_time"`
    BidingEndTime string `json:"biding_end_time"`
    ContractStartTime string `json:"contract_start_time"`
    ContractEndTime string `json:"contract_end_time"`
    LinkStart string `json:"link_start"`
    LinkEnd string `json:"link_end"`
    State string `json:"state"`
}

//链码返回结构
type chaincodeRet struct {
    Code int // 0 success otherwise 1
    Des  string //description
}

// 根据返回码和描述返回序列号后的字节数组
func getRetByte(code int,des string) []byte {
    var r chaincodeRet
    r.Code = code
    r.Des = des

    b,err := json.Marshal(r)

    if err!=nil {
        fmt.Println("marshal Ret failed")
        return nil
    }
    return b
}

// 根据返回码和描述返回序列号后的字符串
func getRetString(code int,des string) string {
    var r chaincodeRet
    r.Code = code
    r.Des = des

    b,err := json.Marshal(r)

    if err!=nil {
        fmt.Println("marshal Ret failed")
        return ""
    }
    return string(b[:])
}

//初始化默认
func (a *Bill) Init(stub shim.ChaincodeStubInterface) pb.Response {
    return shim.Success(nil)
}

//链码Invoke接口
func (a *Bill) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    function,args := stub.GetFunctionAndParameters()
    //invoke
    if function == "issue" {
        //发布合约(招标)
        return a.issue(stub, args)
    }

    if function == "query" {
        // the old "Query" is now implemtned in invoke
        return a.query(stub, args)
    }


    return shim.Error("ChainnovaChaincode Unkown method!")
}

//保存合约
func (a *Bill) putBill(stub shim.ChaincodeStubInterface, bill Bill) ([]byte, bool) {

    byte,err := json.Marshal(bill)
    if err!=nil {
        return nil, false
    }

    err = stub.PutState(bill.BillId, byte)
    if err!=nil {
        return nil, false
    }
    return byte, true
}

//根据合约号取出合约
func (a *Bill) getBill(stub shim.ChaincodeStubInterface, bill_No string) (Bill, bool) {

    var bill Bill
    key := bill_No
    b,err := stub.GetState(key)
    if b!=nil {
        return bill, false
    }

    err = json.Unmarshal(b,&bill)
    if err!=nil {
        return bill, false
    }
    return bill, true
}

//发布合约
func (a *Bill) issue(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    if len(args)!=9 {
        return shim.Error("ChainnovaChaincode Invoke issue args != 9")
    }

    bill := Bill{BillId:args[0],PurchaseUserCode:args[1],BidingStartTime:args[2],BidingEndTime:args[3],ContractStartTime:args[4],ContractEndTime:args[5],LinkStart:args[6],LinkEnd:args[7],State:args[8]}

    //根据合约ID查找合约ID是否已存在
    _, existbl :=a.getBill(stub,bill.BillId)
    if existbl {
        return shim.Error("ChainnovaChaincode Invoke issue failed : the billNo has exist")
    }
    if bill.BillId == "" {
        bill.BillId = fmt.Sprintf("%d",time.Now().UnixNano())
    }

    //更改合约信息和状态并保存合约:合约状态设为新发布
    bill.State = BillInfo_State_Publish

    //保存票据
    _, bl := a.putBill(stub, bill)
    if !bl {
        return shim.Error("ChainnovaChaincode Invoke issue putdata failed!")
    }

    return shim.Success(nil)
}

//查询合约
func (a *Bill) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    if len(args) != 1 {
        return shim.Error("Incorrect number of arguments. Expecting 1")
    }

    billAsBytes, _ := stub.GetState(args[0])
    return shim.Success(billAsBytes)
}


func main() {
    if err := shim.Start(new(Bill)); err != nil {
        fmt.Printf("Error starting Bill chaincode: %s", err)
    }
}

四 调试Chaincode

调试ChainCode主要分为三个步骤:

  • 启动Fabric网络
  • 编译安装chainCode
  • 调用ChainCode

这三个步骤,需要进入chaincode-docker-devmode目录下面打开三个独立的终端分别完成。

第一个终端
docker-compose -f docker-compose-simple.yaml up

上述指令启动了一个带有SingleSampleMSPSoloorderer profile的网络,并将peer和orderer节点在“开发者模式”下启动。它还启动了两个额外的容器:一个包含chaincode运行环境;另一个是CLI命令行,可与chaincode进行交互。创建并加入channel(管道)的指令内嵌于CLI容器中。

第二个终端

进入容器:

docker exec -it chaincode bash

在容器中ls可以看到mychaincode文件夹,cd进入后可以看到我们编写的源码。
编译chaincode:

go build

如果没有报错,则可以看到多了一个可执行的chaincode文件。
(注:编译这一步也可以在容器外做,即链码编写完之后直接在目录中执行go build)
运行chaincode:

CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./mychaincode

Chaincode被peer节点启动,chaincode日志表明peer节点成功注册。

第三个终端

下面我们将进入CLI容器进行chaincode在链上的安装和初始化:

docker exec -it cli bash
peer chaincode install -p chaincodedev/chaincode/mychaincode -n mycc -v 0
peer chaincode instantiate -n mycc -v0 -c'{"Args":[]}' -C myc

这里写图片描述
因为链码功能为新建合约,所以初始化的时候参数为空。
在CLI内部会为mychaincode创建SignedChaincodeDeploymentSpec,并将其发送到本地peer节点。这些节点会调用LSCC上的Install方法。上述的-p选项指明chaincode的路径,其必须在用户的GOPATH目录下(比如$GOPATH/src/sacc)。
现在我们执行一次invoke调用,新建一条从北京到纽约的链路:

peer chaincode invoke -n mycc -v 0 -c '{"Args":["issue","lc1522512000","CMCC","1525104000","1525881600","1527782400","1559318400","beijing","new york","new"]}' -C myc

最后查询该链路,我们会看到链路的内容。

peer chaincode invoke -n mycc -v 0 -c '{"Args":["query","lc1522512000"]}' -C myc
退出

Terminal2和Terminal3用exit命令退出docker。

Terminal 1直接ctrl+c退出,然后再执行

docker-compose -f docker-compose-simple.yaml down

一定要确保容器清理干净。

调试过程

当测试时出错时,如果每次都关闭网络环境退出容器后重新修改代码并编译,过于麻烦。一个简单做法是第一个终端不关,在第二个终端中退出容器,然后进入mychaincode文件夹修改链码并编译后重新进入容器,甚至可以在容器内安装vim并修改编译链码,完成之后在第三个终端中继续调用即可。

参考:

区块链和HyperLedger系列(IBM微讲堂)——区块链第四讲
官方文档
《深度探索区块链——hyperledger技术与应用》

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Hyperledger Fabric中编写和部署Hello World链码的步骤如下: 1. 安装Hyperledger Fabric环境和配置网络 - 参考官方文档:https://hyperledger-fabric.readthedocs.io/en/latest/install.html 2. 编写链码 - 创建一个新目录,例如`hello-world-chaincode` - 在该目录下创建一个名为`hello-world.go`的文件,内容如下: ```go package main import ( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" ) type HelloWorldChaincode struct {} func (cc *HelloWorldChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response { return shim.Success(nil) } func (cc *HelloWorldChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response { fmt.Println("Hello, World!") return shim.Success(nil) } func main() { if err := shim.Start(new(HelloWorldChaincode)); err != nil { fmt.Printf("Error starting chaincode: %s", err) } } ``` 3. 打包链码 - 在`hello-world-chaincode`目录下执行以下命令: ```sh go mod init hello-world-chaincode go mod vendor go build ``` - 生成的二进制文件为`hello-world-chaincode` 4. 部署链码 - 使用Fabric CLI工具`peer`来部署链码 - 在命令行中执行以下命令: ```sh peer chaincode install -n hello-world -v 1.0 -p ./hello-world-chaincode peer chaincode instantiate -n hello-world -v 1.0 -C mychannel -c '{"Args":[]}' ``` - 第一条命令将链码安装到对等节点,第二条命令在通道`mychannel`上实例化链码 5. 调用链码 - 在命令行中执行以下命令: ```sh peer chaincode invoke -n hello-world -C mychannel -c '{"Args":[]}' ``` - 此命令将触发链码的`Invoke`函数,输出`Hello, World!` 以上就是在Hyperledger Fabric中编写和部署Hello World链码的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值