链码不需要“实例化”,可以同时运行java和go链码,同一个链码多次实例化。
想要了解上面的特性,请看下面的分解。
Fabric 2.0 在2020年1月29号终于release了,我们来看下有哪些新的变化。
主要体现在:对新应用和隐私的支持,增强了智能合约的管理,增加了对节点的操作。
需要注意的是,只能由fabric-1.4.x升级到2.0。
ps:在网上有个翻译,那一字一句的翻译,真的让我很难受。
下面我尝试使用自己的理解来解读。
(ps:我是在本地写的,导致导入的时候,有些图片未能上传,有需要看的,请移步到我的blog:https://haojunsheng.github.io/2020/02/fabric-relase-2/)
fabric 2.0引入了智能合约的去中心化管理,在此之前,链码的安装和实例化都是一个由组织在操作,现在则发生了变化。新的链码的生命周期中,只有多个组织达成了共识,才可以和账本才可以进行交互。
新的链码周期要求组织对链码的名字,版本,背书策略达成一致,需要执行以下四步,但不需要每个组织都执行:
下面来详细的看上面4步:
链码在安装前需要打包为tar文件。我们可以使用peer命令,node sdk,或者第三方工具。
第三方的打包工具需要满足以下要求:
链码以tar.gz结尾;
tar文件需要包含2个文件(不是目录),元文件Chaincode-Package-Metadata.json和chaincode文件。
Chaincode-Package-Metadata.json文件长成下面这样。
{"Path":"fabric-samples/chaincode/fabcar/go","Type":"golang","Label":"fabcarv1"}
一个demo如下。2个组织不需要使用相同的名字。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tgtM5BdB-1582029897382)(…/images/posts/fabric/Lifecycle-package.png)]
每个节点上都需要安装。强烈建议每个组织只打包一次链码,然后把该链码安装在该组织的所有节点上。如果一个channel想要保证所有的组织运行相同的链码,那么打包命令应该由一个组织来进行。
安装成功后会返回*MYCC_1:hash.*这样的格式,我们需要进行保存,方便后面的使用,如果忘记了,可以进行查询。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2cqLO8D-1582029897383)(…/images/posts/fabric/Lifecycle-install.png)]
我的理解是,在上面,每个组织都给chaincode起了一个名字,这样,在实际中是无法使用的,所以现在大家来投票来确定一个统一的名字,包含下面的参数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QjcOOW7T-1582029897383)(…/images/posts/fabric/Lifecycle-approve.png)]
一旦得到了绝大多数成员的同意,就可以提交链码的定义了。
我们可以使用checkcommitreadiness命令来检查是否已经有链码的定义了,首先会发送给所有的peer节点,在发送给order节点。提交必须是组织的管理员来完成的。
Channel/Application/LifecycleEndorsement来管理认可的组织的数量,默认是大多数。LifecycleEndorsement和chaincode的背书策略是分离的,没有任何关系的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1wIoDGz-1582029897384)(…/images/posts/fabric/Lifecycle-commit.png)]
即使一个组织没有安装链码,仍然可以响应链码的定义。
当链码的定义被确认后,将会在所有安装链码的节点上启动链码容器。如果我们在定义链码的时候要求使用init函数,那么init函数将会被调用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KSkVwjYw-1582029897384)(…/images/posts/fabric/Lifecycle-start.png)]
升级和安装类似,我们既可以升级链码的内容,还可以升级链码的背书策略。
打包链码。只有在升级链码内容的时候需要。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pN4ACywl-1582029897386)(…/images/posts/fabric/Lifecycle-upgrade-package-20200217233850395.png)]
安装新链码。同上。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UN0fCSd6-1582029897387)(…/images/posts/fabric/Lifecycle-upgrade-install.png)]
链码定义投票。sequence将会自增1。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wntCVO9Q-1582029897388)(…/images/posts/fabric/Lifecycle-upgrade-approve-20200218163958279.png)]
提交定义。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZPOieSz-1582029897388)(…/images/posts/fabric/Lifecycle-upgrade-commit.png)]
将会启动新的链码容器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IU1hSe4v-1582029897389)(…/images/posts/fabric/Lifecycle-upgrade-start.png)]
下面是chaincode的比较完整的操作。来自fabric-samples。
## at first we package the chaincode
packageChaincode 1
## Install chaincode on peer0.org1 and peer0.org2
echo "Installing chaincode on peer0.org1..."
installChaincode 1
echo "Install chaincode on peer0.org2..."
installChaincode 2
## query whether the chaincode is installed
queryInstalled 1
## approve the definition for org1
approveForMyOrg 1
## check whether the chaincode definition is ready to be committed
## expect org1 to have approved and org2 not to
checkCommitReadiness 1 "\"Org1MSP\": true" "\"Org2MSP\": false"
checkCommitReadiness 2 "\"Org1MSP\": true" "\"Org2MSP\": false"
## now approve also for org2
approveForMyOrg 2
## check whether the chaincode definition is ready to be committed
## expect them both to have approved
checkCommitReadiness 1 "\"Org1MSP\": true" "\"Org2MSP\": true"
checkCommitReadiness 2 "\"Org1MSP\": true" "\"Org2MSP\": true"
## now that we know for sure both orgs have approved, commit the definition
commitChaincodeDefinition 1 2
## query on both orgs to see that the definition committed successfully
queryCommitted 1
queryCommitted 2
## Invoke the chaincode
chaincodeInvokeInit 1 2
sleep 10
## Invoke the chaincode
chaincodeInvoke 1 2
# Query chaincode on peer0.org1
echo "Querying chaincode on peer0.org1..."
chaincodeQuery 1
下面来看图:
加入通道:如果一个channel已经有了定义好的chaincode,那么新加入的组织在安装链码后可以直接使用原来的名字。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CMSQvQ3G-1582029897390)(…/images/posts/fabric/Lifecycle-join-approve.png)]
如果背书策略是默认的大多数,那么背书策略会自动更新,把新的组织计算在内。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2qG6GXQC-1582029897391)(…/images/posts/fabric/Lifecycle-join-start.png)]
升级背书策略
我们不必重新打包或者安装链码即可升级背书策略。channel中的成员会重新生成一个chaincode定义。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Z7D7LYF-1582029897391)(…/images/posts/fabric/Lifecycle-endorsement-approve.png)]
新的背书策略在新的链码定义通过后,即可生效,我们不必重启容器即可更新背书策略。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKnURxDs-1582029897392)(…/images/posts/fabric/Lifecycle-endorsement-commit.png)]
无法安装链码即可同意链码的定义:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vaWVnNX-1582029897392)(…/images/posts/fabric/Lifecycle-no-package.png)]
不同意链码定义的组织将不能使用链码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ueV6I1vz-1582029897393)(…/images/posts/fabric/Lifecycle-no-package-20200217235444051.png)]
上图中的组织三不可以使用链码。
channel不认可链码的定义:这里比较绕,说的是channel中的组织没有对链码的定义达成共识。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qQxHw9xK-1582029897393)(…/images/posts/fabric/Lifecycle-majority-disagree.png)]
组织安装了不同类型的链码:这里的意思是说只要链码产生相同的读写集,那么可以安装不同语言写的链码,比如java和go。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-woH2XOU7-1582029897394)(…/images/posts/fabric/Lifecycle-languages.png)]
一次打包,多次使用:
我们可以打包一次,给链码创建不同的定义,从而运行多个智能合约实例(但是背书策略要有区别)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BsrJfrpy-1582029897394)(…/images/posts/fabric/Lifecycle-multiple.png)]
做了个表格,把旧的声明周期和新的进行了比较。
1.x | 2.0 | |
---|---|---|
package | 有 | 有 |
install | 有 | 有 |
approve | 无 | 有 |
commit | 无 | 有 |
instantiate | 有 | 无 |
upgrade | 有 | 有 |
Fabric 2.0增强了private data,我们不需要创建私有数据集合即可使用私有数据。做了以下增强:
在同一个channel中,A组织的数据不想给其他的组织看的数据。从v1.2开始,创造了private data collections,我们可以背书,提交和查询私有数据,在不创建一个独立channel的情况下。
private data collections由两部分组成:
当集合中的成员需要把该私有数据向第三方共享时,第三方可以通过比较该数据的hash和链上保存的hash,看是否一致。
还有一些特殊情况,每个组织都可以创建一个私有数据集合,之后可以共享给其他成员。
我们把private data和channel进行一个比较。
有下面5个角色:
Farmer出售商品,Distributor分销商负责把商品运到海外,Shipper负责在两个角色之间运货,Wholesaler批发商从distributors批发商品,Retailer零售商从shippers和wholesaler购买商品。
场景是:
为了满足上面的场景,我们不需要建立这么多的channel,可以使用PDC。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lRHH8fyY-1582029897394)(…/images/posts/fabric/PrivateDataConcept-1.png)]
上面场景下,peer节点的账本如下,也称为SideDB。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vkfx0HxF-1582029897395)(…/images/posts/fabric/PrivateDataConcept-3.png)]
我们可能会有把私有数据向其他组织或者其他集合共享的需求,接受方需要验证hash:
在实际中,我们可能会创建大量的私有数据集合,这个不利于我们的维护。更好的情况是每个组织都是一个集合,然后共享就可以了。更好的是我们不必为此进行定义,因为在2.0中默认设置了。
下面这个是每个组织一个集合的模型:
把私有数据模型和链码结合可以发挥出很大的作用,具体如下所示:
具体的交易流如下所示:
对于非常敏感的数据,比如政府要求的。我们可以从peer节点上彻底的删除,只留下hash来证明该数据确实存在过。数据删除后,无法从链码进行查询,其他的peer节点也不可查询。
从fabric 2.0开始,在chaincode定义阶段来进行定义:
我们可以使用自己喜欢的方式来构建和启动链码,不必使用docker。
在Hyperledger Fabric 2.0之前,用于构建和启动链码的过程是peer节点实现的一部分,无法轻松自定义。必须使用特定的语言。这种方法限制了链码的语言,必须依赖容器,chaincode无法作为单独运行的服务。
从2.0开始,我们在peer的core.yaml中,加入了一个externalBuilder的配置来自定义自己的服务。
# List of directories to treat as external builders and launchers for
# chaincode. The external builder detection processing will iterate over the
# builders in the order specified below.
externalBuilders: []
# - path: /path/to/directory
# name: descriptive-builder-name
# environmentWhitelist:
# - ENVVAR_NAME_TO_PROPAGATE_FROM_PEER
# - GOPROXY
fabric的构建器使用了Heroku Buildpacks。
外部构建和运行期由下面四个部分组成:
bin/detect
: 判断是否由我们自定义的模型来运行。bin/build
: 把打包后的链码变为可执行版本。用来构建,编译链码。bin/release
(optional): 提供chaincode的元数据。bin/run
(optional): 运行链码。下面分别是四个脚本的内容:
detect
:#!/bin/bash
CHAINCODE_METADATA_DIR="$2"
# use jq to extract the chaincode type from metadata.json and exit with
# success if the chaincode type is golang
if [ "$(jq -r .type "$CHAINCODE_METADATA_DIR/metadata.json" | tr '[:upper:]' '[:lower:]')" = "golang" ]; then
exit 0
fi
exit 1
build
#!/bin/bash
CHAINCODE_SOURCE_DIR="$1"
CHAINCODE_METADATA_DIR="$2"
BUILD_OUTPUT_DIR="$3"
# extract package path from metadata.json
GO_PACKAGE_PATH="$(jq -r .path "$CHAINCODE_METADATA_DIR/metadata.json")"
if [ -f "$CHAINCODE_SOURCE_DIR/src/go.mod" ]; then
cd "$CHAINCODE_SOURCE_DIR/src"
go build -v -mod=readonly -o "$BUILD_OUTPUT_DIR/chaincode" "$GO_PACKAGE_PATH"
else
GO111MODULE=off go build -v -o "$BUILD_OUTPUT_DIR/chaincode" "$GO_PACKAGE_PATH"
fi
# save statedb index metadata to provide at release
if [ -d "$CHAINCODE_SOURCE_DIR/META-INF" ]; then
cp -a "$CHAINCODE_SOURCE_DIR/META-INF" "$BUILD_OUTPUT_DIR/"
fi
release
#!/bin/bash
BUILD_OUTPUT_DIR="$1"
RELEASE_OUTPUT_DIR="$2"
# copy indexes from META-INF/* to the output directory
if [ -d "$BUILD_OUTPUT_DIR/META-INF" ] ; then
cp -a "$BUILD_OUTPUT_DIR/META-INF/"* "$RELEASE_OUTPUT_DIR/"
fi
run
BUILD_OUTPUT_DIR="$1"
RUN_METADATA_DIR="$2"
# setup the environment expected by the go chaincode shim
export CORE_CHAINCODE_ID_NAME="$(jq -r .chaincode_id "$RUN_METADATA_DIR/chaincode.json")"
export CORE_PEER_TLS_ENABLED="true"
export CORE_TLS_CLIENT_CERT_FILE="$RUN_METADATA_DIR/client.crt"
export CORE_TLS_CLIENT_KEY_FILE="$RUN_METADATA_DIR/client.key"
export CORE_PEER_TLS_ROOTCERT_FILE="$RUN_METADATA_DIR/root.crt"
export CORE_PEER_LOCALMSPID="$(jq -r .mspid "$RUN_METADATA_DIR/chaincode.json")"
# populate the key and certificate material used by the go chaincode shim
jq -r .client_cert "$RUN_METADATA_DIR/chaincode.json" > "$CORE_TLS_CLIENT_CERT_FILE"
jq -r .client_key "$RUN_METADATA_DIR/chaincode.json" > "$CORE_TLS_CLIENT_KEY_FILE"
jq -r .root_cert "$RUN_METADATA_DIR/chaincode.json" > "$CORE_PEER_TLS_ROOTCERT_FILE"
if [ -z "$(jq -r .client_cert "$RUN_METADATA_DIR/chaincode.json")" ]; then
export CORE_PEER_TLS_ENABLED="false"
fi
# exec the chaincode to replace the script with the chaincode process
exec "$BUILD_OUTPUT_DIR/chaincode" -peer.address="$(jq -r .peer_address "$ARTIFACTS/chaincode.json")"
上面说了,这个是在core.yaml中配置的,一个demo如下所示:
chaincode:
externalBuilders:
- name: my-golang-builder
path: /builders/golang
environmentWhitelist:
- GOPROXY
- GONOPROXY
- GOSUMDB
- GONOSUMDB
- name: noop-builder
path: /builders/binary
从 v2.0 开始,Hyperledger Fabric Docker 镜像将使用 Alpine Linux 作为基础镜像,这是一个面向安全的轻量级 Linux 发行版。这意味着 Docker 镜像现在要小得多,提供更快的下载和启动时间,以及占用主机系统上更少的磁盘空间。Alpine Linux 的设计从一开始就考虑到了安全性,Alpine 发行版的最小化特性大大降低了安全漏洞的风险。
**FAB-11237:**去中心化的智能合约管理
新的应用程序模式:
FAB-103: State database cache for CouchDB
FAB-5177: The ccenv build image no longer includes the shim
FAB-15366: Logger removed from chaincode shim
FAB-16213: The go chaincode entities extension has been removed
FAB-12075: Client Identity (CID) library has moved
FAB-14720: Support for CAR chaincode package format removed
FAB-15285: Support for invoking system chaincodes from user chaincodes
has been removed.
FAB-15390: Support for peer’s Admin service has been removed.
FAB-16303: GetHistoryForKey returns results from newest to oldest
FAB-16722: The ‘provisional’ genesis method of generating the system channel
for orderers has been removed.
FAB-16477 and FAB-17116: New configuration for orderer genesismethod and genesisfile
FAB-15343: System Chaincode Plugins have been removed.
FAB-11096: Docker images with Alpine Linux
FAB-11096: Bash not available in Docker images with Alpine Linux
FAB-15499: Ledger data format upgrade
FAB-16866: Chaincode built upon installation on peer
FAB-15837: Orderer FileLedger location moved if specified with relative path
FAB-14271: Policies must be specified in configtx.yaml
FAB-17000: Warn when certificates are about to expire
FAB-16987: Go version has been updated to 1.13.4.
--outputAnchorPeersUpdate
is deprecated.