下载与构建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))
}
核心步骤就是三步:
- 初始化SDK对象。
- 创建需要的channelContext或者clientContext
- 调用相关接口。
简单说几个文件:
- 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方法