说明:链码开发语言是golang,源码分析是基于v1.4.3版本
Hyperledger Fabric有一个很好的特性,就是有很多特性是可插拔的,可以很方便的进行自定义扩展,以契合具体的业务场景。今天分享如何使用系统链码插件。
Golang在1.8版本时已经支持了Plugin特性,但是只支持Linux和macOS。
Plugin的优势在于轻量以及弱耦合:
1、轻量:运行空间就是主进程,无需额外支持通信相关功能;
2、弱耦合:接口定义完毕之后,插件可独立开发,独立升级,对主进程无影响;
Plugin的底层实现也极简单,核心只有一个结构体和一个接口:
type Plugin struct {
pluginpath string
err string // set if plugin failed to load
loaded chan struct{} // closed when loaded
syms map[string]interface{}
}
func Open(path string) (*Plugin, error) {}
func (p *Plugin) Lookup(symName string) (Symbol, error) {}
type Symbol interface{}
使用Open函数来加载一个插件,通过Lookup找到插件中对应的变量或者函数,以Symbol返回,再通过Go的类型断言取到原本的变量或者函数即可进行访问。
插件的代码必须在一个main包中。
peer 启动后读取core.yaml文件中chaincode.systemPlugins配置项,获取系统链码插件定义,可定义多个系统链码插件,相关结构如下:
type PluginConfig struct {
Enabled bool `mapstructure:"enabled" yaml:"enabled"`
Name string `mapstructure:"name" yaml:"name"`
Path string `mapstructure:"path" yaml:"path"`
InvokableExternal bool `mapstructure:"invokableExternal" yaml:"invokableExternal"`
InvokableCC2CC bool `mapstructure:"invokableCC2CC" yaml:"invokableCC2CC"`
}
其中:
a)、Enabled表示是否启用该系统链码插件
b)、Name表现系统链码名
c)、Path表示链码链码插件在磁盘上的绝对路径
d)、InvokableExternal和InvokableCC2CC分别表示该系统链码是否允许外部直接调用、是否允许通过链码调用
加载每一个系统链码插件(plugin.Open),查找名称为New,类型为func() shim.Chaincode的函数并调用,以创建链码对象,该对象应实现接口github.com/hyperledger/fabric/core/chaincode/shim.Chaincode。
相关函数:
函数: func CreatePluginSysCCs(p *Provider) []SelfDescribingSysCC {}
位置: core/scc/register_pluginsenabled.go:13
函数: func loadSysCCs(p *Provider) []*SystemChaincode {}
位置: core/scc/loadsysccs.go:37
函数: func loadPlugin(path string) *shim.Chaincode {}
位置: core/scc/loadsysccs.go:65
系统链码白名单校验函数:func isWhitelisted(syscc SelfDescribingSysCC) bool {
位置: core/scc/sysccapi.go:206
使用系统链码插件,需要配置系统链码白名单,在core.yaml中chaincode.system中配置。
系统链码插件的开发,可以fabric的源码中的样例:$GOPATH/src/github.com/hyperledger/fabric/examples/plugins/scc/plugin.go
简单说一下,满足以下约束:
1、必须定义一个结构体,实现github.com/hyperledger/fabric/core/chaincode/shim.Chaincode接口
2、必须定义并实现一个func New() shim.Chaincode格式函数,一般就是返回1中定义的结构体实例
因为Hyperledger Fabric官方的peer镜像并不支持系统链码的可插拔特性,因此需要自行编译peer容器。而官方Makefile和peer的Dockerfile并不会把我们的插件集成进去,因此需要进行定制:
step 1:打开编译选项,使peer支持系统链码插件:export set GO_TAGS=pluginsenabled
step 2:设置环境变量,export set DOCKER_DYNAMIC_LINK=true
step 3:checkout Fabric的源码,并把系统链码放置在$GOPATH/src/github.com/hyperledger/fabric/myscc目录下
step 4:修改$GOPATH/src/github.com/hyperledger/fabric/Makefile文件:
增加编译系统链码的规则:
$(BUILD_DIR)/docker/bin/myscc.so:
$(eval TARGET = ${patsubst $(BUILD_DIR)/docker/bin/%,%,${@}})
@echo "Building $@"
@$(DRUN) \
-v $(abspath $(BUILD_DIR)/docker/bin):/opt/gopath/bin \
$(BASE_DOCKER_NS)/fabric-baseimage:$(BASE_DOCKER_TAG) \
go build --buildmode=plugin -tags "$(GO_TAGS)" -o /opt/gopath/bin/myscc.so ./myscc/ $(pkgmap.$(@F))
修改$(BUILD_DIR)/image/peer/payload规则:
$(BUILD_DIR)/image/peer/payload: $(BUILD_DIR)/docker/bin/peer \
$(BUILD_DIR)/sampleconfig.tar.bz2 \
$(BUILD_DIR)/docker/bin/myscc.so
Makefile的不能有空格,需要使用Tab键
step 5:修改peer镜像的$GOPATH/src/github.com/hyperledger/fabric/image/peer/payload/Dockerfile.in文件,创建/opt/lib目录,并将插件拷贝到该目录下:
# Copyright Greg Haskins All Rights Reserved
#
# SPDX-License-Identifier: Apache-2.0
#
FROM _BASE_NS_/fabric-baseos:_BASE_TAG_
ENV FABRIC_CFG_PATH /etc/hyperledger/fabric
RUN mkdir -p /opt/lib /var/hyperledger/production $FABRIC_CFG_PATH
COPY payload/peer /usr/local/bin
COPY payload/myscc.so /opt/lib/
ADD payload/sampleconfig.tar.bz2 $FABRIC_CFG_PATH
CMD ["peer","node","start"]
step 6:在$GOPATH/src/github.com/hyperledger/fabric目录下执行:make peer-docker即可生成定制的peer镜像。
执行:docker run hyperledger/fabric-peer:amd64-1.4.4-snapshot-e09c726
日志:
2019-09-24 10:14:14.259 UTC [sccapi] deploySysCC -> INFO 01e system chaincode myscc/(/opt/lib/myscc.so) deployed