考虑到虚拟机的复杂和服务器的方便又昂贵,最终选择两台服务器,模拟搭建一个orderer
服务节点,一个peer
节点,实现了简单的fabric
平台记账功能。本文记录了Hyperledger Fabric从环境搭建,到跨机器间通过证书、公密钥等方式部署,实现了fabric自带样例example02
中智能合约的记账功能,可通过如下方式拓展至多节点、多组织机构,构建更广的分布式区块链网络,并在运行、更新智能合约的时候增加背书功能。以下是我的主机分布和配置:
HOSTS_NAME | IP |
---|---|
peer0.org1.example.com | 47.100.178.167 |
orderer.example.com | 129.211.29.99 |
[root@VM_0_7_centos orderer]# cat /proc/version
Linux version 3.10.0-514.21.1.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) ) #1 SMP Thu May 25 17:04:51 UTC 2017
[root@izuf6aqowtvq0ud222hha1z org]# cat /proc/version
Linux version 3.10.0-514.26.2.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) ) #1 SMP Tue Jul 4 15:04:05 UTC 2017
# Hyperledger Fabric version
v1.0.0
# go version
go version go1.12.2 linux/amd64
# docker version
Client:
Version: 18.09.6
API version: 1.39
Go version: go1.10.8
Git commit: 481bc77156
Built: Sat May 4 02:34:58 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.6
API version: 1.39 (minimum version 1.12)
Go version: go1.10.8
Git commit: 481bc77
Built: Sat May 4 02:02:43 2019
OS/Arch: linux/amd64
Experimental: false
# docker-compose version
docker-compose version 1.11.2, build dfed245
docker-py version: 2.1.0
CPython version: 2.7.13
OpenSSL version: OpenSSL 1.0.1t 3 May 2016
# git version
git version 1.8.3.1
# gcc version (gcc -v)
gcc 版本 4.8.5
# python version
Python 2.7.5
# pip version (pip -V)
pip 8.1.2 from /usr/lib/python2.7/site-packages (python 2.7)
接下来构建以上环境配置。
yum install -y git bzip2 gcc gcc-c++ libtool libltdl-dev libtool-ltdl-devel openssl
官网下载go-1.12.2.linux-amd64包(当然现在可以下载最新版)
sudo tar -C /usr/local -xzf go1.12.2.linux-amd64.tar.gz
编辑当前用户的环境变量:
vi ~/.profile
添加以下内容:
export PATH=$PATH:/usr/local/go/bin
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$HOME/go/bin
编辑保存并退出vi后,记得把这些环境载入:
source ~/.profile
然后在/etc/profile下加以下内容并保存:
export PATH=$PATH:/usr/local/go/bin
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$HOME/go/bin
source ~/.profile
接着在终端执行:
source /etc/profile
我们把go的目录GOPATH设置为当前用户的文件夹下,所以记得创建go文件夹
cd ~
mkdir go
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新并安装Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启Docker服务
sudo service docker start
# 注意:
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ee.repo
# 将[docker-ce-test]下方的enabled=0修改为enabled=1
#
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# yum list docker-ce.x86_64 --showduplicates | sort -r
# Loading mirror speeds from cached hostfile
# Loaded plugins: branch, fastestmirror, langpacks
# docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
# docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable
# docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
# Available Packages
# Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)
# sudo yum -y install docker-ce-[VERSION]
开机自启动:
chkconfig docker on
curl -L https://github.com/docker/compose/releases/download/1.11.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# docker-compose version可查看docker-compose版本
我们可以使用Git命令下载源码,也可以使用go get命令,偷懒一点,我们直接用go get命令获取最新的Fabric源码:
go get github.com/hyperledger/fabric
这个可能等的时间比较久,等完成后,我们可以在~/go/src/github.com/hyperledger/fabric
中找到所有的最新的源代码。如果上述方法被那个了(无法访问,原因不可描述[滑稽]),那么可创建目录/root/go/src/github.com/hyperledger/
,并执行:
git clone https://github.com/hyperledger/fabric.git
由于Fabric一直在更新,所有我们并不需要最新最新的源码,需要切换到v1.0.0版本的源码即可:
cd ~/go/src/github.com/hyperledger/fabric
git checkout v1.0.0
这个其实很简单,因为我们已经设置了Docker Hub镜像地址,所以下载也会很快。官方文件也提供了批量下载的脚本。我们直接运行:
cd ~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/
source download-dockerimages.sh -c x86_64-1.0.0 -f x86_64-1.0.0
# 如果发现某些镜像缺失,可用如下方式下载对应缺失版本的镜像:
# docker pull hyperledger/fabric-baseimage:x86_64-0.3.2
# docker pull hyperledger/fabric-baseos:x86_64-0.3.2
# docker pull hyperledger/fabric-tools:x86_64-1.0.5
# docker pull hyperledger/fabric-kafka:x86_64-1.0.5
# docker pull hyperledger/fabric-zookeeper:x86_64-1.0.5
# docker pull hyperledger/fabric-orderer:x86_64-1.0.5
# docker pull hyperledger/fabric-ccenv:x86_64-1.0.5
# 如果出现创建channel失败,删除 /etc/resolv.conf文件即可
# 但/etc/resolv.conf是DNS配置文件,尽量不要删除,若误删重启服务器即可恢复
上述步骤即完成了fabric-v1.0.0的基本环境搭建,如go语言,docker环境,docker fabric镜像,docker-compose及一些基本的依赖环境等,接下来我们构建自己的1peer+1orderer网络。
在fabric下的examples目录的中新建一个自定义文件夹orderer:
[root@VM_0_7_centos orderer]# pwd
/root/go/src/github.com/hyperledger/fabric/examples/orderer
准备fabric提供的二进制编译工具:Fabric平台特定使用的二进制文件cryptogen
,configtxgen
,configtxlator
,以及peer
。我们可以通过configtxgen
和cryptogen
手动生成证书/密钥以及各项配置文件。也可通过e2c_cli例子中的generateArtifacts.sh
自动生成,由于我们需要自定义各节点的域名,及联盟链的统一域名,下面手动生成。通过运行generateArtifacts.sh
脚本,会在release
下创建目录linux-amd64
,找到fabric目录release/linux-amd64/bin
中的二进制文件:
[root@VM_0_7_centos bin]# pwd
/root/go/src/github.com/hyperledger/fabric/release/linux-amd64/bin
[root@VM_0_7_centos bin]# ll
总用量 85296
-rwxr-xr-x 1 root root 16457479 5月 13 10:27 configtxgen
-rwxr-xr-x 1 root root 17530145 5月 13 10:27 configtxlator
-rwxr-xr-x 1 root root 8585051 5月 13 10:27 cryptogen
-rwxr-xr-x 1 root root 441 5月 13 10:27 get-byfn.sh
-rwxr-xr-x 1 root root 757 5月 13 10:27 get-docker-images.sh
-rwxr-xr-x 1 root root 20798659 5月 13 10:27 orderer
-rwxr-xr-x 1 root root 23959314 5月 13 10:27 peer
将bin
目录复制到刚刚新建的文件夹目录orderer
中,并创建新文件夹base
,即orderer
下有两个文件夹bin
、base
:
/root/go/src/github.com/hyperledger/fabric/examples/orderer目录关系:
orderer
├── base
└── bin
在bin目录中新建crypto-config.yaml
文件,可参照e2e_cli例子中的crypto-config.yaml
,文件内容如下:
OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
PeerOrgs:
- Name: Demo
Domain: demo.example.com
Template:
Count: 1
Users:
Count: 1
文件定义了orderer节点以及peer节点的域名及数量等信息。我们组建一个名为example的联盟,且我们自己的组织名称为Demo,我们会创建一个Orderer排序服务节点,同时还会创建一个peer节点。接下来,使用crytogen工具给我们不同的网络实体(peer/client)生成加密证书(X509 certs)。这些证书代表了身份,当我们的网络实体在进行通信以及transact的时候进行签名与验证身份。
crypto-config.yaml
配置文件将被crytogen
工具调用,文件中包括了网络拓扑,同时允许我们给organization(Demo)以及component(隶属于organization的组件)生成一个证书与私钥的集合。每一个organization(Demo)被分配一个唯一的根证书(绑定了隶属于organization(Demo)的具体的component,包括peers与orderers)。Hyperledger Fabric的transaction与通信均被节点的私钥(keystore)进行签名,截止被公钥进行验证(signcerts)。 这个配置文件中有一个计数(count)的变量,我们使用其定义organization(Demo)中peer的数量,在本例中我们定义Demo组织有一个peer。
在bin目录下执行命令:
./cryptogen generate --config=./crypto-config.yaml
执行成功后,bin目录下生成crypto-config
文件夹,其中会有ordererOrganizations
和peerOrganizations
两个目录:
[root@VM_0_7_centos crypto-config]# pwd
/root/go/src/github.com/hyperledger/fabric/examples/orderer/bin/crypto-config
[root@VM_0_7_centos crypto-config]# ll
总用量 8
drwxr-xr-x 3 root root 4096 5月 13 15:47 ordererOrganizations
drwxr-xr-x 3 root root 4096 5月 13 15:47 peerOrganizations
使用configtxgen
工具来执行configtx.yaml
文件创建orderer/Genesis/block
,在此之前需要为configtxgen
工具指定configtx.yaml
文件的路径,我们需要设置一个环境变量,进入bin
目录,执行如下命令:
export FABRIC_CFG_PATH=$PWD
在bin
目录下创建channel-artifacts
目录,用来存放各种渠道的源文件。新建configtx.yaml
文件,可参照e2e_cli中示例文件。文件内容如下:
Profiles:
ExampleOrdererGenesis:
Orderer:
<<: *OrdererExample
Organizations:
- *OrdererDemo
Consortiums:
ExampleConsortium:
Organizations:
- *Demo
ExampleChannel:
Consortium: ExampleConsortium
Application:
<<: *ApplicationExample
Organizations:
- *Demo
Organizations:
- &OrdererDemo
Name: OrdererDemo
ID: OrdererMSP
MSPDir: crypto-config/ordererOrganizations/example.com/msp
- &Demo
Name: DemoMSP
ID: DemoMSP
MSPDir: crypto-config/peerOrganizations/demo.example.com/msp
AnchorPeers:
- Host: peer0.demo.example.com
Port: 7051
Orderer: &OrdererExample
OrdererType: solo
Addresses:
- orderer.example.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 10
AbsoluteMaxBytes: 99 MB
PreferredMaxBytes: 512 KB
Kafka:
Brokers:
- 127.0.0.1:9092
Organizations:
Application: &ApplicationExample
Organizations:
在该文件中,我们定义了组织名称peer0.demo.example.com
、组织排序服务名称、组织渠道名称、应用渠道名称、应用联盟名称等。
接下来,通过configtxgen
工具生成初始区块genesis.block
:
./configtxgen -profile ExampleOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
命令执行完成后,在bin
目录下可见genesis.block
文件。
生成ID为examplechannel
的通道文件:
./configtxgen -profile ExampleChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID examplechannel
为examplechannel
通道生成ID为DemoMSP
的认证文件DEMOMSPanchors.tx
./configtxgen -profile ExampleChannel -outputAnchorPeersUpdate ./channel-artifacts/DEMOMSPanchors.tx -channelID examplechannel -asOrg DemoMSP
完成以上步骤后,bin
目录下将生成orderer
节点所需的所有配置文件。
channel-artifacts
文件夹下文件:
[root@VM_0_7_centos channel-artifacts]# pwd
/root/go/src/github.com/hyperledger/fabric/examples/orderer/bin/channel-artifacts
[root@VM_0_7_centos channel-artifacts]# ll
总用量 16
-rw-r--r-- 1 root root 354 5月 13 16:05 channel.tx
-rw-r--r-- 1 root root 260 5月 13 16:06 DEMOMSPanchors.tx
-rw-r--r-- 1 root root 6349 5月 13 16:02 genesis.block
在orderer目录下创建一个docker-compose-orderer.yaml
文件:
version: '2'
services:
orderer.example.com:
extends:
file: base/docker-compose-base.yaml
service: orderer.example.com
container_name: orderer.example.com
这里有一个协助启动文件,是位于base
目录下的docker-compose-base.yaml
文件,这个文件的参数配置如下:
version: '2'
services:
orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer
environment:
- ORDERER_GENERAL_LOGLEVEL=debug
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_GENESISMETHOD=file
- ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
# enabled TLS
- ORDERER_GENERAL_TLS_ENABLED=true
- ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
- ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
- ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
command: orderer
volumes:
- ../bin/channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
- ../bin/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
- ../bin/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls
ports:
- 7050:7050
随后在orderer
目录下执行启动命令:
docker-compose -f docker-compose-orderer.yaml up -d
查看docker容器启动情况:
[root@VM_0_7_centos orderer]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed359d5eca1c hyperledger/fabric-orderer "orderer" 6 seconds ago Up 5 seconds 0.0.0.0:7050->7050/tcp orderer.example.com
# orderer节点启动成功
通过scp
将orderer
节点在bin
目录下的生成的channel-artifacts/
目录和crypto-config/
目录拷贝至peer
节点的新建org/bin/
目录下,并提前在org
下创建base
目录。
新建在org/
下docker-compose-org.yaml
文件:
version: '2'
services:
peer0.demo.example.com:
container_name: peer0.demo.example.com
extends:
file: base/docker-compose-base.yaml
service: peer0.demo.example.com
extra_hosts:
- "orderer.example.com:129.211.29.99"
cli:
container_name: cli
image: hyperledger/fabric-tools
tty: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_LOGGING_LEVEL=DEBUG
- CORE_PEER_ID=cli
- CORE_PEER_ADDRESS=peer0.demo.example.com:7051
- CORE_PEER_LOCALMSPID=DemoMSP
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/demo.example.com/peers/peer0.demo.example.com/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/demo.example.com/peers/peer0.demo.example.com/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/demo.example.com/peers/peer0.demo.example.com/tls/ca.crt
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/demo.example.com/users/[email protected]/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
volumes:
- /var/run/:/host/var/run/
- ../chaincode/go/:/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go
- ./bin/crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
- ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
- ./bin/channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
depends_on:
- peer0.demo.example.com
extra_hosts:
- "orderer.example.com:129.211.29.99"
- "peer0.demo.example.com:47.100.178.167"
volumes
项!这里与orderer
不同,有两个协助启动文件,分别是位于base
目录下的docker-compose-base.yaml
和peer-base.yaml
文件,这两个文件的参数配置分别如下:
docker-compose-base.yaml:
version: '2'
services:
peer0.demo.example.com:
container_name: peer0.demo.example.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer0.demo.example.com
- CORE_PEER_ADDRESS=peer0.demo.example.com:7051
- CORE_PEER_CHAINCODELISTENADDRESS=peer0.demo.example.com:7052
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.demo.example.com:7051
- CORE_PEER_LOCALMSPID=DemoMSP
volumes:
- /var/run/:/host/var/run/
- ../bin/crypto-config/peerOrganizations/demo.example.com/peers/peer0.demo.example.com/msp:/etc/hyperledger/fabric/msp
- ../bin/crypto-config/peerOrganizations/demo.example.com/peers/peer0.demo.example.com/tls:/etc/hyperledger/fabric/tls
ports:
- 7051:7051
- 7052:7052
- 7053:7053
peer-base.yaml:
version: '2'
services:
peer-base:
image: hyperledger/fabric-peer
environment:
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
# the following setting starts chaincode containers on the same
# bridge network as the peers
# https://docs.docker.com/compose/networking/
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=org_default
#- CORE_LOGGING_LEVEL=ERROR
- CORE_LOGGING_LEVEL=DEBUG
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_GOSSIP_USELEADERELECTION=true
- CORE_PEER_GOSSIP_ORGLEADER=false
- CORE_PEER_PROFILE_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start
docker-compose -f docker-compose-org.yaml up -d
查看docker容器,启动了peer0.demo.example.com
和 cli
两个容器
[root@izuf6aqowtvq0ud222hha1z org]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6d658097f3b1 hyperledger/fabric-tools "/bin/bash" 8 seconds ago Up 8 seconds cli
61a7765829d9 hyperledger/fabric-peer "peer node start" 9 seconds ago Up 8 seconds 0.0.0.0:7051-7053->7051-7053/tcp peer0.demo.example.com
peer节点启动成功。
首先我们进入映射的docker cli
容器中:
docker exec -it cli bash
执行生成channel id
文件命令:
peer channel create -o orderer.example.com:7050 -c examplechannel -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peer节点加入channel:
peer channel join -b examplechannel.block
仍在docker cli
容器中安装链码,链码为fabric自带的chaincode_example02实例:
peer chaincode install -n test -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
chaincode_example02
实现了一个简单的键值对存储账单功能和转帐功能,代码如下供参考:
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of
//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has
//to be modified as well with the new ID of chaincode_example02.
//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of
//hard-coding.
import (
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Init")
_, args := stub.GetFunctionAndParameters()
var A, B string // Entities
var Aval, Bval int // Asset holdings
var err error
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
// Initialize the chaincode
A = args[0]
Aval, err = strconv.Atoi(args[1])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
B = args[2]
Bval, err = strconv.Atoi(args[3])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Invoke")
function, args := stub.GetFunctionAndParameters()
if function == "invoke" {
// Make payment of X units from A to B
return t.invoke(stub, args)
} else if function == "delete" {
// Deletes an entity from its state
return t.delete(stub, args)
} else if function == "query" {
// the old "Query" is now implemtned in invoke
return t.query(stub, args)
}
return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}
// Transaction makes payment of X units from A to B
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A, B string // Entities
var Aval, Bval int // Asset holdings
var X int // Transaction value
var err error
if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3")
}
A = args[0]
B = args[1]
// Get the state from the ledger
// TODO: will be nice to have a GetAllState call to ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
return shim.Error("Failed to get state")
}
if Avalbytes == nil {
return shim.Error("Entity not found")
}
Aval, _ = strconv.Atoi(string(Avalbytes))
Bvalbytes, err := stub.GetState(B)
if err != nil {
return shim.Error("Failed to get state")
}
if Bvalbytes == nil {
return shim.Error("Entity not found")
}
Bval, _ = strconv.Atoi(string(Bvalbytes))
// Perform the execution
X, err = strconv.Atoi(args[2])
if err != nil {
return shim.Error("Invalid transaction amount, expecting a integer value")
}
Aval = Aval - X
Bval = Bval + X
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state back to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
// Deletes an entity from state
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
A := args[0]
// Delete the key from the state in ledger
err := stub.DelState(A)
if err != nil {
return shim.Error("Failed to delete state")
}
return shim.Success(nil)
}
// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A string // Entities
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
}
A = args[0]
// Get the state from the ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return shim.Error(jsonResp)
}
if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
return shim.Error(jsonResp)
}
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
fmt.Printf("Query Response:%s\n", jsonResp)
return shim.Success(Avalbytes)
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
初始化链码,指定a有100rmb
,b有200rmb
:
peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C examplechannel -n test -v 1.0 -c '{"Args":["init","a","100","b","200"]}'
调用invoke函数方法,从b向a转50rmb
:
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C examplechannel -n test -c '{"Args":["invoke","b","a","50"]}'
然后我们可以查询a或b的余额,具体调用query方法,比如查询a余额:
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C examplechannel -n test -c '{"Args":["query","a"]}'
至此,基本的环境搭建和实例测试就完成了。网上遇到的很多各种各样的错误,许多都来源于yaml文件中的配置出错。如下即是我参考的博客,他们写的博客或多或少在表达和逻辑上有很多错误,我也是慢慢摸索出来的,如果以上博客有任何问题,请及时向我提出宝贵意见。
参考博客: