Fabric Go SDK 开发

下载与构建Fabric Go SDK

首先,自从官方获取fabric-sdk-go的源码,有两种方式:

  • Go get
    go get -u github.com/hyperledger/fabric-sdk-go
  • Git clone
    git clone https://github.com/hyperledger/fabric-sdk-go.git

需要注意的是,如果使用git,最好保持go的安装路径。因为如果想进行频繁修改或者debug,直接使用GOPATH的目录结构会很有用,安装也比较方便。

然后,直接根据需求进行编译即可。如果只是想安装依赖,并在sample引用。则可以到项目下执行安装命令。

# In the Fabric SDK Go directory
cd $GOPATH/src/github.com/hyperledger/fabric-sdk-go/

# Optional - Automatically install Go tools used by test suite
make depend-install
可能会遇到的问题
  • Coverage 安装失败
  • 该组件主要用来检测单元测试覆盖率报告的。
    可能在使用go get的时候会报错:
go get github.com/axw/gocov/...
go get github.com/AlekSi/gocov-xml

错误:

go get -v github.com/axw/gocov/...
Fetching https://golang.org/x/tools/cover?go-get=1
https fetch failed: Get https://golang.org/x/tools/cover?go-get=1: dial tcp 216.
239.37.1:443: connectex: A connection attempt failed because the connected party
did not properly respond after a period of time, or established connection fail
ed because connected host has failed to respond.
package golang.org/x/tools/cover: unrecognized import path "golang.org/x/tools/c
over" (https fetch: Get https://golang.org/x/tools/cover?go-get=1: dial tcp 216.
239.37.1:443: connectex: A connection attempt failed because the connected party
did not properly respond after a period of time, or established connection fail
ed because connected host has failed to respond.)

解决方案:
在GOPATH的目录下,按照目录结构创建文件夹,然后手动git克隆项目,直接在执行go get或者直接安装即可。

mkdir -p $GOPATH/src/golang.org/x
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/net.git
git clone https://github.com/golang/tools.git
  • sync.Map找不到
    如果在build的时候发现如下错误:
# github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/greylist
../../go/src/github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/greylist/greylist.go:26: undefined: sync.Map
# github.com/hyperledger/fabric-sdk-go/pkg/util/concurrent/lazycache
../../go/src/github.com/hyperledger/fabric-sdk-go/pkg/util/concurrent/lazycache/lazycache.go:46: undefined: sync.Map

发现这个包时Go1.9的feature。所以必须升级。我的版本是1.8.1。
然后删除了/usr/local/go的路径,重新解压新的go放进去。然后出现错误:

bash: /usr/local/go/bin/go: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

发现下错了版本,换成64位的就OK了。

sample

如果安装成功,就会发现在GOPATH目录的bin中有很多依赖的组件已经安装成功了。
Fabric Go SDK开发主要有两个注意点:Fabric网络配置和代码编写。

首先,修改配置文件。这里我们直接参考在官方代码中的end2end项目。
配置文件位于$GOPATH/src/github.com/hyperledger/fabric-sdk-go/test/fixtures/config中,我们选用config_test.yaml作为参考。
具体的源码直接大家在项目里找到就行,这里简单说下几个注意的:

  • 目前go-sdk的doc非常少,也没有API开发手册之类的。test里的example大多都是直接用配置文件进行init的。但是源码中和一些单元测试中有直接通过opt参数进行配置的。大家自己选择。
  • 路径中我用了$HOME,一直说找不到user,debug了一下发现creds的map是空的。换成直接的目录就好了。所以,特别注意配置文件中证书或者cred的路径。
  • 出现错误:failed to create channel context: failed to get client context to create channel client: user not found。翻了下源码和其他配置文件,发现在配置文件中的organisations下需要配置User的列表。
organizations:
  org1:
    mspid: org1.org
    cryptoPath:  "peer\\msp"
    peers:
      - peer0
    users:
      Admin:
        cert:
          pem: |
            -----BEGIN CERTIFICATE----
            -----END CERTIFICATE-----

同时在翻看源码的关于初始化过程中,发现了一个关于证书命名的细节。在pkg/msp/certfileuserstore.go中,调用load方法,然后其中调用storeKeyFromUserIdentifier方法。其获取的cert文件名(默认)格式是:key.ID + "@" + key.MSPID + "-cert.pem"

  • 配置了channel之后,有一个子配置项orderers被注释掉了。如果不把orderer配置上,其默认会去channel中取orderer的地址。如果你的网络访问Orderer不是orderer的内部地址(在fabric网路中使用的),比如说nginx的映射地址。那么就会访问不到。所以这个地方最好把orderer配置上,然后在后面orderers的配置项中详细配置指定的orderer。

然后,简单的代码例子:

package main

import (
    "encoding/binary"
    "fmt"
    "time"

    "github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
    "github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
    "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/core"
    "github.com/hyperledger/fabric-sdk-go/pkg/core/config"
    "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
)

const (
    channelID      = "default"
    orgName        = "bcs06071508"
    orgAdmin       = "Admin"
    ordererOrgName = "bcs06071508"
    ccID           = "obcs-example02"
)

// ExampleCC query and transaction arguments
var queryArgs = [][]byte{[]byte("query"), []byte("b")}
var txArgs = [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")}

func setupAndRun(configOpt core.ConfigProvider, sdkOpts ...fabsdk.Option) {
    //Init the sdk config
    sdk, err := fabsdk.New(configOpt, sdkOpts...)
    if err != nil {
        fmt.Println("Failed to create new SDK: %s", err)
    }
    defer sdk.Close()
    // ************ setup complete ************** //

    //prepare channel client context using client context
    clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser("Admin"), fabsdk.WithOrg(orgName))

    // Channel client is used to query and execute transactions (Org1 is default org)
    client, err := channel.New(clientChannelContext)

    if err != nil {
        fmt.Println("Failed to create new channel client: %s", err)
    }

    value := queryCC(client)
    fmt.Println("value is d%", int64(binary.BigEndian.Uint64(value)))

    eventID := "test([a-zA-Z]+)"

    // Register chaincode event (pass in channel which receives event details when the event is complete)
    reg, notifier, err := client.RegisterChaincodeEvent(ccID, eventID)
    if err != nil {
        fmt.Println("Failed to register cc event: %s", err)
    }
    defer client.UnregisterChaincodeEvent(reg)

    // Move funds
    executeCC(client)

    select {
    case ccEvent := <-notifier:
        fmt.Println("Received CC event: %#v\n", ccEvent)
    case <-time.After(time.Second * 20):
        fmt.Println("Did NOT receive CC event for eventId(%s)\n", eventID)
    }
}

func executeCC(client *channel.Client) {
    _, err := client.Execute(channel.Request{ChaincodeID: ccID, Fcn: "invoke", Args: txArgs},
        channel.WithRetry(retry.DefaultChannelOpts))
    if err != nil {
        fmt.Println("Failed to move funds: %s", err)
    }
}

func queryCC(client *channel.Client) []byte {
    response, err := client.Query(channel.Request{ChaincodeID: ccID, Fcn: "invoke", Args: queryArgs},
        channel.WithRetry(retry.DefaultChannelOpts))
    if err != nil {
        fmt.Println("Failed to query funds: %s", err)
    }
    fmt.Println(response)

    return response.Payload
}

func main() {
    configPath := "./config.yaml"
    //End to End testing
    setupAndRun(config.FromFile(configPath))
}

核心步骤就是三步:

  1. 初始化SDK对象。
  2. 创建需要的channelContext或者clientContext
  3. 调用相关接口。

简单说几个文件:

  • pkg/fab/channel/transactor.go: 从channel中获取orderer信息,给orderer发送相关tx。
  • pkg/client/channel/chclient.go:创建channel的client,执行query或者invoke(execute)
  • pkg/fab/endpointconfig.go: 根据配置文件,初始化channel,peer和orderer。
  • pkg/client/channel/invoke/txnhandler.go: endorseHandler,创建和发送Tx。
  • pkg/fab/txn/proposal.go: proposal相关操作,创爱,签名,发送。
  • pkg/fab/peer/peer.go:peer的endpoint相关属性和方法实现
  • pkg/fab/orderer/orderer.go: orderer的相关相关属性和方法实现
  • pkg/fab/peer/peerendorser.go: endorse相关属性和方法实现
  • pkg/fab/txn/txn.go: 交易相关操作(orderer),调用grpc方法

参考链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容