前置条件
如果你不想处理也可以去我的资源里拿,或者找我要我的机器,我的机器也有搭建教程,在置顶文档
安装一个Ubuntu20+的镜像
基础环境安装
Git cURL vim jq
sudo apt install -y git curl vim jq
Docker和Docker-compose
这个命令会自动安装docker
sudo apt install docker-compose
sudo chmod +x /usr/bin/docker-compose
docker version
docker-compose version
sudo systemctl enable docker
systemctl status docker
sudo usermod -aG docker $USER
docker version
如果出现版本信息就不用重启 如果没有版本信息就需要重启再查看
docker version
完成后给docker 换源
sudo mkdir -p /etc/docker
vim命令如果没有本文件就会在wq保存之后创建一个这个文件
sudo vim /etc/docker/daemon.json
这里使用更多的源,因为,目前docker还不是很稳定
{
"registry-mirrors" : ["https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.iscas.ac.cn",
"https://docker.rainbond.cc",
"https://do.nark.eu.org",
"https://dc.j8.work",
"https://dockerproxy.com",
"https://gst6rzl9.mirror.aliyuncs.com",
"https://registry.docker-cn.com",
"http://hub-mirror.c.163.com",
"http://mirrors.ustc.edu.cn/",
"https://mirrors.tuna.tsinghua.edu.cn/",
"http://mirrors.sohu.com/"
],
"insecure-registries" : [
"registry.docker-cn.com",
"docker.mirrors.ustc.edu.cn"
],
"debug": true,
"experimental": false
}
sudo systemctl daemon-reload
sudo systemctl restart docker
查看换源是否成功,他会显示刚才复制进去的一坨内容
docker info
Golang
这里有两种方案,一种是压缩包安装 一种是apt安装自行选择,我这里选择的是apt安装 版本尽量高于1.18如果apt安装小于这个版本就先执行apt update 和upgrade
sudo apt install golang
安装包安装
wget https://dl.google.com/go/go1.22.4.linux-amd64.tar.gz
sudo tar -C /usr/local -zxvf go1.22.4.linux-amd64.tar.gz
sudo vim ~/.bashrc
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
source ~/.bashrc
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
查看内容是否生效
go env
Node.js
使用nvm不要影响到其他应用的node包
git clone https://gitee.com/mirrors/nvm.git ~/.nvm && cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`
./install.sh
vim .bash_profile
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
source .bash_profile
nvm install v16.13.0
node -v && npm -v
nvm use v16.13.0
npm config get registry
npm config list
安装fabric
赋予管理员权限 然后进入根目录
su root && cd ~
这里不需要创建工作目录因为下载下来后就直接是fabric,当然如果你要管理不同版本的fabric那还是可以创一个工作目录
mkdir fabric && cd fabric
git clone https://github.com/hyperledger/fabric.git
目前测试成功的版本是2.4.9 拉下来的是最新版 直接更换文件内容
cd script && vim bootstrap.sh
#!/bin/bash
#
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
# if version not passed in, default to latest released version
VERSION=2.4.9
# if ca version not passed in, default to latest released version
CA_VERSION=1.5.5
REGISTRY=${FABRIC_DOCKER_REGISTRY:-docker.io/hyperledger}
OS=$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')
ARCH=$(uname -m | sed 's/x86_64/amd64/g' | sed 's/aarch64/arm64/g')
PLATFORM=${OS}-${ARCH}
# Fabric < 1.2 uses uname -m for architecture.
MARCH=$(uname -m)
: ${CONTAINER_CLI:="docker"}
printHelp() {
echo "Usage: bootstrap.sh [version [ca_version]] [options]"
echo
echo "options:"
echo "-h : this help"
echo "-d : bypass docker image download"
echo "-s : bypass fabric-samples repo clone"
echo "-b : bypass download of platform-specific binaries"
echo
echo "e.g. bootstrap.sh 2.4.9 1.5.5 -s"
echo "will download docker images and binaries for Fabric v2.4.9 and Fabric CA v1.5.5"
}
# dockerPull() pulls docker images from fabric and chaincode repositories
# note, if a docker image doesn't exist for a requested release, it will simply
# be skipped, since this script doesn't terminate upon errors.
dockerPull() {
#three_digit_image_tag is passed in, e.g. "1.4.7"
three_digit_image_tag=$1
shift
#two_digit_image_tag is derived, e.g. "1.4", especially useful as a local tag for two digit references to most recent baseos, ccenv, javaenv, nodeenv patch releases
two_digit_image_tag=$(echo "$three_digit_image_tag" | cut -d'.' -f1,2)
while [[ $# -gt 0 ]]
do
image_name="$1"
echo "====> ${REGISTRY}/fabric-$image_name:$three_digit_image_tag"
${CONTAINER_CLI} pull "${REGISTRY}/fabric-$image_name:$three_digit_image_tag"
${CONTAINER_CLI} tag "${REGISTRY}/fabric-$image_name:$three_digit_image_tag" "${REGISTRY}/fabric-$image_name"
${CONTAINER_CLI} tag "${REGISTRY}/fabric-$image_name:$three_digit_image_tag" "${REGISTRY}/fabric-$image_name:$two_digit_image_tag"
shift
done
}
cloneSamplesRepo() {
# clone (if needed) hyperledger/fabric-samples and checkout corresponding
# version to the binaries and docker images to be downloaded
if [ -d test-network ]; then
# if we are in the fabric-samples repo, checkout corresponding version
echo "==> Already in fabric-samples repo"
elif [ -d fabric-samples ]; then
# if fabric-samples repo already cloned and in current directory,
# cd fabric-samples
echo "===> Changing directory to fabric-samples"
cd fabric-samples
else
echo "===> Cloning hyperledger/fabric-samples repo"
git clone -b main https://github.com/hyperledger/fabric-samples.git && cd fabric-samples
fi
if GIT_DIR=.git git rev-parse v${VERSION} >/dev/null 2>&1; then
echo "===> Checking out v${VERSION} of hyperledger/fabric-samples"
git checkout -q v${VERSION}
else
echo "fabric-samples v${VERSION} does not exist, defaulting to main. fabric-samples main branch is intended to work with recent versions of fabric."
git checkout -q main
fi
}
# This will download the .tar.gz
download() {
local BINARY_FILE=$1
local URL=$2
echo "===> Downloading: " "${URL}"
curl -L --retry 5 --retry-delay 3 "${URL}" | tar xz || rc=$?
if [ -n "$rc" ]; then
echo "==> There was an error downloading the binary file."
return 22
else
echo "==> Done."
fi
}
pullBinaries() {
echo "===> Downloading version ${FABRIC_TAG} platform specific fabric binaries"
download "${BINARY_FILE}" "https://github.com/hyperledger/fabric/releases/download/v${VERSION}/${BINARY_FILE}"
if [ $? -eq 22 ]; then
echo
echo "------> ${FABRIC_TAG} platform specific fabric binary is not available to download <----"
echo
exit
fi
echo "===> Downloading version ${CA_TAG} platform specific fabric-ca-client binary"
download "${CA_BINARY_FILE}" "https://github.com/hyperledger/fabric-ca/releases/download/v${CA_VERSION}/${CA_BINARY_FILE}"
if [ $? -eq 22 ]; then
echo
echo "------> ${CA_TAG} fabric-ca-client binary is not available to download (Available from 1.1.0-rc1) <----"
echo
exit
fi
}
pullDockerImages() {
command -v ${CONTAINER_CLI} >& /dev/null
NODOCKER=$?
if [ "${NODOCKER}" == 0 ]; then
FABRIC_IMAGES=(peer orderer ccenv tools)
case "$VERSION" in
2.*)
FABRIC_IMAGES+=(baseos)
shift
;;
esac
echo "FABRIC_IMAGES:" "${FABRIC_IMAGES[@]}"
echo "===> Pulling fabric Images"
dockerPull "${FABRIC_TAG}" "${FABRIC_IMAGES[@]}"
echo "===> Pulling fabric ca Image"
CA_IMAGE=(ca)
dockerPull "${CA_TAG}" "${CA_IMAGE[@]}"
echo "===> List out hyperledger docker images"
${CONTAINER_CLI} images | grep hyperledger
else
echo "========================================================="
echo "${CONTAINER_CLI} not installed, bypassing download of Fabric images"
echo "========================================================="
fi
}
DOCKER=true
SAMPLES=true
BINARIES=true
# Parse commandline args pull out
# version and/or ca-version strings first
if [ -n "$1" ] && [ "${1:0:1}" != "-" ]; then
VERSION=$1;shift
if [ -n "$1" ] && [ "${1:0:1}" != "-" ]; then
CA_VERSION=$1;shift
if [ -n "$1" ] && [ "${1:0:1}" != "-" ]; then
THIRDPARTY_IMAGE_VERSION=$1;shift
fi
fi
fi
# prior to 1.2.0 architecture was determined by uname -m
if [[ $VERSION =~ ^1\.[0-1]\.* ]]; then
export FABRIC_TAG=${MARCH}-${VERSION}
export CA_TAG=${MARCH}-${CA_VERSION}
export THIRDPARTY_TAG=${MARCH}-${THIRDPARTY_IMAGE_VERSION}
else
# starting with 1.2.0, multi-arch images will be default
: "${CA_TAG:="$CA_VERSION"}"
: "${FABRIC_TAG:="$VERSION"}"
: "${THIRDPARTY_TAG:="$THIRDPARTY_IMAGE_VERSION"}"
fi
# Prior to fabric 2.5, use amd64 binaries on darwin-arm64
if [[ $VERSION =~ ^2\.[0-4]\.* ]]; then
PLATFORM=$(echo $PLATFORM | sed 's/darwin-arm64/darwin-amd64/g')
fi
BINARY_FILE=hyperledger-fabric-${PLATFORM}-${VERSION}.tar.gz
CA_BINARY_FILE=hyperledger-fabric-ca-${PLATFORM}-${CA_VERSION}.tar.gz
# then parse opts
while getopts "h?dsb" opt; do
case "$opt" in
h|\?)
printHelp
exit 0
;;
d) DOCKER=false
;;
s) SAMPLES=false
;;
b) BINARIES=false
;;
esac
done
if [ "$SAMPLES" == "true" ]; then
echo
echo "Clone hyperledger/fabric-samples repo"
echo
cloneSamplesRepo
fi
if [ "$BINARIES" == "true" ]; then
echo
echo "Pull Hyperledger Fabric binaries"
echo
pullBinaries
fi
if [ "$DOCKER" == "true" ]; then
echo
echo "Pull Hyperledger Fabric docker images"
echo
pullDockerImages
fi
./bootstrap.sh
执行 bootstrap.sh 脚本,主要会做以下3步:
1. 克隆 fabric samples 的代码;
2. 下载 fabric 和 fabric-ca 的二进制文件;
3. 拉取 fabric 所需要的一些镜像 image 。由于直接执行 bootstrap.sh 脚本文件很有可能会出现卡死的问题或者由于网络问题而无法连接的问题,第一步一般都没有问题,主要是第二步和第三步,对于第二步,可以采用手动下载的方式,挂个梯子去 github 官网下载 fabric 和 fabric-ca 编译后的压缩包,这样速度就很快,然后将压缩包直接拖入虚拟机中,最后将其存放在当前目录下,即:~/go/src/github.com/hyperledger/fabric/scripts
fabric:
Releases · hyperledger/fabric · GitHub
fabric-ca:
参考链接:https://blog.csdn.net/qq_46380363/article/details/143177862
将这两个压缩包解压到 fabric-samples 目录下:
sudo tar -zxvf hyperledger-fabric-linux-amd64-2.4.9.tar.gz -C ~/go/src/github.com/hyperledger/fabric/scripts/fabric-samples
sudo tar -zxvf hyperledger-fabric-ca-linux-amd64-1.5.5.tar.gz -C ~/go/src/github.com/hyperledger/fabric/scripts/fabric-samples
./bootstrap.sh -sb
在上面命令中,参数 -sb
s 表示不执行第一步,即不克隆 fabric-samples 的代码
b 表示不执行第二步,即不下载 fabric 和 fabric-ca 的二进制文件
由于在前面的步骤中我们完成了第一步和第二步,所以只需要执行第三步就可以了,即拉取 fabric 所需的一些镜像 image 。
sudo vim ~/.bashrc
你首先使用pwd找一下路径,然后将路径给写进环境变量,如果你按照我的来做的话,那么你的路径应该是
export FABRIC_BIN=/root/fabric/scripts/fabric-samples/bin
export PATH=$PATH:$FABRIC_BIN
source ~/.bashrc
peer version
启动测试网络
cd fabric-samples/test-network
启动命令
sudo ./network.sh up
关闭命令
sudo ./network.sh down
创建通道命令
sudo ./network.sh createChannel
通道默认的名字是: mychannel ,也可以使用 -c
来指定通道名称,以下命令将创建一个名为 channel1 的通道:
sudo ./network.sh createChannel -c Channel1
注意:确保通道的名称应用以下限制:
仅包含小写 ASCII 字母数字、点 "." 和短划线"_"
少于 250 个字符
以字母开头
首先配置环境变量,修改 ~/.bashrc 文件:
启动链码
vim ~/.bashrc
alias sudo='sudo env PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH'
source ~/.bashrc
cd ../asset-transfer-basic/chaincode-go
这一步就相当于npm i 或者是 mvc install
go mod vendor
cd ../../test-network
sudo ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
注意:如果通道名称不是 mychannel ,在命令中添加 -c 通道名称,表示在该通道上部署链码,如下命令就是在名为 channel1 的通道上部署链码:
sudo ./network.sh deployCC -c channel1 -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
与网络交互
在启用测试网络后,可以使用 peer cli 客户端与网络进行交互,通过 peer cli 客户端可以调用已部署的智能合约,更新通道,或安装和部署新的智能合约。
这里都是临时变量如果需要持久化使用vim去编辑
执行以下命令将 cli 客户端添加到环境变量中:
export PATH=${PWD}/../bin:$PATH
将 fabric-samples 代码库中的 FABRIC_CFG_PATH 设置为指向其中的 core.yaml 文件:
export FABRIC_CFG_PATH=$PWD/../config/
设置允许 org1 操作 peer cli 的环境变量:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
CORE_PEER_TLS_ROOTCERT_FILE 和 CORE_PEER_MSPCONFIGPATH 环境变量指向 Org1 的 organizations 文件夹中的的加密材料。
执行invoke命令用一些资产来初始化账本:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"InitLedger","Args":[]}'
修改资产所有者,将 asset1 资产的所有者修改为 IDY:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"TransferAsset","Args":["asset1","IDY"]}'
查看修改后的结果:
peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}'
Caliper 0.5.0部署和测试
Caliper安装
创建Caliper工作区
在 fabric-samples 目录的同一级别创建一个名为 caliper-workspace 的文件夹,然后在 caliper-workspace 文件夹中,分别创建三个名为 networks 、 benchmarks 和 workload 的文件夹:
mkdir -p ../../caliper-workspace/{networks,benchmarks,workload}
初始化项目
如果这个时候你遇到npm不存在的报错请你按照本逻辑再执行一遍
# 切换到root用户 sudo -i # 为root用户安装nvm git clone https://gitee.com/mirrors/nvm.git ~/.nvm cd ~/.nvm git checkout `git describe --abbrev=0 --tags` ./install.sh # 使配置生效 source ~/.bashrc # 安装node nvm install v16.13.0
执行完后,在当前目录生成了 package.json 文件。
安装caliper-cli
在 caliper-workspace 目录中,使用以下终端命令安装 caliper CLI:
npm install --only=prod @hyperledger/caliper-cli@0.5.0
执行完后,会将依赖下载到当前目录下的 node_modules 文件下。
验证 caliper-cli 是否安装成功:
npx caliper --version
绑定Fabric 2.4
npx caliper bind --caliper-bind-sut fabric:2.4
构建网络配置文件
在低版本中采用的是 .json 文件,而在新版本中需要采用 .yaml 文件,在 networks 文件夹下创建一个名为 networkConfig.yaml 的文件:
cd networks && vim networkConfig.yaml
name: Caliper Benchmarks
version: "2.0.0"
caliper:
blockchain: fabric
channels:
- channelName: mychannel
contracts:
- id: basic
organizations:
- mspid: Org1MSP
identities:
certificates:
- name: 'Admin@org1.example.com'
clientPrivateKey:
path: '../fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/priv_sk'
clientSignedCert:
path: '../fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem'
connectionProfile:
path: '../fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/connection-org1.yaml'
discover: true
构建测试工作负载模块
在 workload 文件夹中,创建一个名为 readAsset.js 的文件
cd ../workload && vim readAsset.js
'use strict';
const { WorkloadModuleBase } = require('@hyperledger/caliper-core');
class MyWorkload extends WorkloadModuleBase {
constructor() {
super();
}
async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) {
await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext);
for (let i=0; i<this.roundArguments.assets; i++) {
const assetID = `${this.workerIndex}_${i}`;
console.log(`Worker ${this.workerIndex}: Creating asset ${assetID}`);
const request = {
contractId: this.roundArguments.contractId,
contractFunction: 'CreateAsset',
invokerIdentity: 'Admin@org1.example.com',
contractArguments: [assetID,'blue','20','penguin','500'],
readOnly: false
};
await this.sutAdapter.sendRequests(request);
}
}
async submitTransaction() {
const randomId = Math.floor(Math.random()*this.roundArguments.assets);
const myArgs = {
contractId: this.roundArguments.contractId,
contractFunction: 'ReadAsset',
invokerIdentity: 'Admin@org1.example.com',
contractArguments: [`${this.workerIndex}_${randomId}`],
readOnly: true
};
await this.sutAdapter.sendRequests(myArgs);
}
async cleanupWorkloadModule() {
for (let i=0; i<this.roundArguments.assets; i++) {
const assetID = `${this.workerIndex}_${i}`;
console.log(`Worker ${this.workerIndex}: Deleting asset ${assetID}`);
const request = {
contractId: this.roundArguments.contractId,
contractFunction: 'DeleteAsset',
invokerIdentity: 'Admin@org1.example.com',
contractArguments: [assetID],
readOnly: false
};
await this.sutAdapter.sendRequests(request);
}
}
}
function createWorkloadModule() {
return new MyWorkload();
}
module.exports.createWorkloadModule = createWorkloadModule;
构建基准测试配置文件
在 benchmarks 文件夹下创建一个名为 myAssetBenchmark.yaml 的文件:
cd ../benchmarks && vim myAssetBenchmark.yaml
test:
name: basic-contract-benchmark
description: test benchmark
workers:
type: local
number: 2
rounds:
- label: readAsset
description: Read asset benchmark
txDuration: 30
rateControl:
type: fixed-load
opts:
transactionLoad: 2
workload:
module: workload/readAsset.js
arguments:
assets: 10
contractId: basic
monitors:
resource:
- module: docker
options:
interval: 5
containers:
- all
运行Caliper基准测试
回退到 ~/fabric/scripts/caliper-workspace
cd ..
npx caliper launch manager --caliper-workspace ./ --caliper-networkconfig networks/networkConfig.yaml --caliper-benchconfig benchmarks/myAssetBenchmark.yaml --caliper-flow-only-test --caliper-fabric-gateway-enabled --caliper-fabric-gateway-discovery
如果出现如下错误:
error [caliper] [caliper-engine] Error while performing "test" step: Error: EACCES: permission denied, open '/home/idy/go/src/github.com/hyperledger/fabric/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/priv_sk'
这个错误是没有权限,无法打开 priv_sk 文件,执行如下命令授予权限:
chmod 666 ~/go/src/github.com/hyperledger/fabric/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/priv_sk
执行成功
运行成功之后就可以去文件夹节点开查看相应报告
安装FISCOBCOS
第一步. 安装
建立一个工作目录
mkdir benchmarks && cd benchmarks
对NPM项目进行初始化,如果公司没有命名需求的话使用
npm init
npm init -y
安装caliper-cli
由于Caliper所有依赖项的安装较为耗时,因此使用--only=prod
选项用于指定NPM只安装Caliper的核心组件,而不安装其他的依赖项(如各个区块链平台针对Caliper的适配器)。在部署完成后,可以通过caliper-cli
显式绑定需要测试的区块链平台及相应的适配器。
npm install --only=prod @hyperledger/caliper-cli@0.2.0
我这里要适配其他的所以我使用了
npm install @hyperledger/caliper-cli@0.2.0
验证caliper-cli
安装成功
npx caliper --version
第二步. 绑定
由于Caliper采用了轻量级的部署方式,因此需要显式的绑定步骤指定要测试的平台及适配器版本,caliper-cli
会自动进行相应依赖项的安装。使用npx caliper bind
命令进行绑定,命令所需的各项参数可以通过如下命令查看:
root@admin:~/benchmarks$ npx caliper bind --help
Usage:
caliper bind --caliper-bind-sut fabric --caliper-bind-sdk 1.4.1 --caliper-bind-cwd ./ --caliper-bind-args="-g"
Options:
--help Show help [boolean]
-v, --version Show version number [boolean]
--caliper-bind-sut The name of the platform to bind to [string]
--caliper-bind-sdk Version of the platform SDK to bind to [string]
--caliper-bind-cwd The working directory for performing the SDK install [string]
--caliper-bind-args Additional arguments to pass to "npm install". Use the "=" notation when setting this parameter [string]
其中, –-caliper-bind-sut :用于指定需要测试的区块链平台,即受测系统(***S***ystem ***u***nder ***T***est); –-caliper-bind-sdk:用于指定适配器版本; –-caliper-bind-cwd:用于绑定caliper-cli的工作目录,caliper-cli在加载配置文件等场合时均是使用相对于工作目录的相对路径; --caliper-bind-args:用于指定caliper-cli在安装依赖项时传递给npm的参数,如用于全局安装的-g。
对于FISCO BCOS,可以采用如下方式进行绑定:
npx caliper bind --caliper-bind-sut fisco-bcos --caliper-bind-sdk latest
如果你报这个错请检查是否是因为权限的问题,请选择提权或者是加sudo
sudo npx caliper bind --caliper-bind-sut fisco-bcos --caliper-bind-sdk latest
第三步. 快速体验FISCO BCOS基准测试
为方便测试人员快速上手,FISCO BCOS已经为Caliper提供了一组预定义的测试样例,测试对象涵盖HelloWorld合约、Solidity版转账合约及预编译版转账合约。同时在测试样例中,Caliper测试脚本会使用docker在本地自动部署及运行4个互连的节点组成的链,因此测试人员无需手工搭链及编写测试用例便可直接运行这些测试样例。
在工作目录下下载预定义测试用例
git clone https://github.com/vita-dounai/caliper-benchmarks.git
注意 若出现网络问题导致的长时间拉取代码失败,则尝试以下方式:
# 拉取gitee代码
git clone https://gitee.com/vita-dounai/caliper-benchmarks.git
执行HelloWorld合约测试
npx caliper benchmark run --caliper-workspace caliper-benchmarks --caliper-benchconfig benchmarks/samples/fisco-bcos/helloworld/config.yaml --caliper-networkconfig networks/fisco-bcos/4nodes1group/fisco-bcos.json
caliper 运行错误一:
这个错误请按照如下方案解决
✔ Compiling /root/benchmarks/caliper-benchmarks/src/fisco-bcos/helloworld/HelloWorld.sol ...
2025.06.03-16:20:29.300 error [caliper] [installSmartContract.js] Failed to install smart contract helloworld, path=/root/benchmarks/caliper-benchmarks/src/fisco-bcos/helloworld/HelloWorld.sol
2025.06.03-16:20:29.301 error [caliper] [installSmartContract.js] Depolying error: TypeError: secp256k1.sign is not a function
2025.06.03-16:20:29.302 error [caliper] [fiscoBcos.js] FISCO BCOS smart contract install failed: TypeError: secp256k1.sign is not a function
at Object.ecsign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/utils.js:141:29)
at Transaction.sign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/transactionObject.js:272:23)
at signTransaction (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:42:8)
at Object.getSignDeployTx (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:121:12)
at Object.module.exports.deploy (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/fiscoBcosApi.js:165:27)
2025.06.03-16:20:29.302 error [caliper] [caliper-flow] Error: TypeError: secp256k1.sign is not a function
at Object.ecsign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/utils.js:141:29)
at Transaction.sign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/transactionObject.js:272:23)
at signTransaction (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:42:8)
at Object.getSignDeployTx (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:121:12)
at Object.module.exports.deploy (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/fiscoBcosApi.js:165:27)
2025.06.03-16:20:29.303 info [caliper] [caliper-utils] Executing command: cd /root/benchmarks/caliper-benchmarks;docker-compose -f networks/fisco-bcos/4nodes1group/docker-compose.yaml down
Stopping node3 ...
Stopping node2 ...
Stopping node1 ...
Stopping node0 ...
Stopping node3 ... done
Stopping node2 ... done
Stopping node1 ... done
Stopping node0 ... done
Removing node3 ...
Removing node2 ...
Removing node1 ...
Removing node0 ...
Removing node3 ... done
Removing node2 ... done
Removing node1 ... done
Removing node0 ... done
Removing network 4nodes1group_default
Benchmark failure
Error: Benchmark failure
at Function.handler (/root/benchmarks/node_modules/@hyperledger/caliper-cli/lib/benchmark/lib/runBenchmark.js:70:23)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Command failed
你可以选择修改或替换
修改请按照如下进行修改,我的建议是直接替换
修改fiscoBcos.js文件
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib
gedit fiscoBcos.js
/* 在40行后进行添加 */
if (this.fiscoBcosSettings.network && this.fiscoBcosSettings.network.authentication) {
for (let k in this.fiscoBcosSettings.network.authentication) {
this.fiscoBcosSettings.network.authentication[k] = CaliperUtils.resolvePath(this.fiscoBcosSettings.network.authentication[k], workspace_root);
}
}
/*在57行进行替换 const fiscoBcosSettings = CaliperUtils.parseYaml(this.configPath)['fisco-bcos'];*/
const fiscoBcosSettings = this.fiscoBcosSettings;
源文档 https://github.com/hyperledger/caliper/pull/677/files
替换fiscoBcos.js文件
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib
gedit fiscoBcos.js
/* * 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. */ 'use strict'; const { BlockchainInterface, CaliperUtils } = require('@hyperledger/caliper-core'); const installSmartContractImpl = require('./installSmartContract'); const invokeSmartContractImpl = require('./invokeSmartContract'); const generateRawTransactionImpl = require('./generateRawTransactions'); const Color = require('./common').Color; const commLogger = CaliperUtils.getLogger('fiscoBcos.js'); /** * Implements {BlockchainInterface} for a FISCO BCOS backend. */ class FiscoBcos extends BlockchainInterface { /** * Create a new instance of the {FISCO BCOS} class. * @param {string} config_path The absolute path of the FISCO BCOS network configuration file. * @param {string} workspace_root The absolute path to the root location for the application configuration files. */ constructor(config_path, workspace_root) { super(config_path); this.bcType = 'fisco-bcos'; this.workspaceRoot = workspace_root; this.fiscoBcosSettings = CaliperUtils.parseYaml(this.configPath)['fisco-bcos']; if (this.fiscoBcosSettings.network && this.fiscoBcosSettings.network.authentication) { for (let k in this.fiscoBcosSettings.network.authentication) { this.fiscoBcosSettings.network.authentication[k] = CaliperUtils.resolvePath(this.fiscoBcosSettings.network.authentication[k], workspace_root); } } } /** * Initialize the {FISCO BCOS} object. * @async * @return {Promise<object>} The promise for the result of the execution. */ async init() { return Promise.resolve(); } /** * Deploy the smart contract specified in the network configuration file to all nodes. * @async */ async installSmartContract() { const fiscoBcosSettings = this.fiscoBcosSettings; try { await installSmartContractImpl.run(fiscoBcosSettings, this.workspaceRoot); } catch (error) { commLogger.error(Color.error(`FISCO BCOS smart contract install failed: ${(error.stack ? error.stack : error)}`)); throw error; } } /** * Get a context for subsequent operations * 'engine' attribute of returned context object must be reserved for benchmark engine to extend the context * engine = { * submitCallback: callback which must be called once new transaction(s) is submitted, it receives a number argument which tells how many transactions are submitted * } * @param {String} name name of the context * @param {Object} args adapter specific arguments * @param {Integer} clientIdx the client index * @return {Promise<object>} The promise for the result of the execution. */ async getContext(name, args, clientIdx) { return Promise.resolve(); } /** * Release a context as well as related resources * @param {Object} context adapter specific object * @return {Promise<object>} The promise for the result of the execution. */ async releaseContext(context) { return Promise.resolve(); } /** * Invoke the given smart contract according to the specified options. Multiple transactions will be generated according to the length of args. * @param {object} context The FISCO BCOS context returned by {getContext}. * @param {string} contractID The name of the smart contract. * @param {string} contractVer The version of the smart contract. * @param {Array} args Array of JSON formatted arguments for transaction(s). Each element contains arguments (including the function name) passing to the smart contract. JSON attribute named transaction_type is used by default to specify the function name. If the attribute does not exist, the first attribute will be used as the function name. * @param {number} timeout The timeout to set for the execution in seconds. * @return {Promise<object>} The promise for the result of the execution. */ async invokeSmartContract(context, contractID, contractVer, args, timeout) { let promises = []; try { args.forEach((arg) => { let fcn = null; let fcArgs = []; for (let key in arg) { if (key === 'transaction_type') { fcn = arg[key].toString(); } else { fcArgs.push(arg[key].toString()); } } promises.push(invokeSmartContractImpl.run(context, this.fiscoBcosSettings, contractID, fcn, fcArgs, this.workspaceRoot)); }); return await Promise.all(promises); } catch (error) { commLogger.error(Color.error(`FISCO BCOS smart contract invoke failed: ${(error.stack ? error.stack : JSON.stringify(error))}`)); throw error; } } /** * Query state from the ledger * @param {Object} context The FISCO BCOS context returned by {getContext} * @param {String} contractID Identity of the contract * @param {String} contractVer Version of the contract * @param {String} key lookup key * @param {String} fcn The smart contract query function name * @return {Promise<object>} The result of the query. */ async queryState(context, contractID, contractVer, key, fcn) { try { return invokeSmartContractImpl.run(context, this.fiscoBcosSettings, contractID, fcn, key, this.workspaceRoot, true); } catch (error) { commLogger.error(Color.error(`FISCO BCOS smart contract query failed: ${(error.stack ? error.stack : error)}`)); throw error; } } /** * Generate an raw transaction and store in local file * @param {Object} context The FISCO BCOS context returned by {getContext} * @param {String} contractID Identity of the contract * @param {Object} arg Arguments of the transaction * @param {String} file File path which will be used to store then transaction * @return {TaskStatus} Indicates whether the transaction is written to the file successfully or not */ async generateRawTransaction(context, contractID, arg, file) { return generateRawTransactionImpl.run(this.fiscoBcosSettings, this.workspaceRoot, context, contractID, arg, file); } /** * Send raw transactions * @param {Object} context The FISCO BCOS context returned by {getContext} * @param {Array} transactions List of raw transactions * @return {Promise} The promise for the result of the execution */ async sendRawTransaction(context, transactions) { return sendRawTransactionImpl.run(this.fiscoBcosSettings, context, transactions); } } module.exports = FiscoBcos;
-----------分割线---------------
修改请按照如下进行修改,我的建议是直接替换
修改channelPromise.js
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib
gedit channelPromise.js
/*替换49行 let emitter = emitters.get(seq).emitter;*/
let emitter = emitters.get(seq);
if(!emitter) {
//Stale message receieved
return;
}
emitter = emitter.emitter;
gedit fiscoBcos.js
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib
/*替换25行 const Color = require('./common').Color;*/
const fiscoBcosSettings = this.fiscoBcosSettings;
替换channelPromise.js
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib
/* * 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. */ 'use strict'; const tls = require('tls'); const fs = require('fs'); const net = require('net'); const uuidv4 = require('uuid/v4'); const events = require('events'); /** * NetworkError exception class thrown in socket connection */ class NetworkError extends Error { /** * * @param {String} msg exception message */ constructor(msg) { super(msg); this.name = 'NetworkError'; } } let emitters = new Map(); let buffers = new Map(); let sockets = new Map(); let lastBytesRead = new Map(); /** * Parse response returned by node * @param {Buffer} response Node's response */ function parseResponse(response) { let seq = response.slice(6, 38).toString(); let result = JSON.parse(response.slice(42).toString()); let emitter = emitters.get(seq); if(!emitter) { //Stale message receieved return; } emitter = emitter.emitter; if (emitter) { let readOnly = Object.getOwnPropertyDescriptor(emitter, 'readOnly').value; if (readOnly) { if (result.error || result.result !== undefined ) { emitter.emit('gotresult', result); } } else { if (result.error || result.status || (result.result && result.result.status)) { emitter.emit('gotresult', result); } else { if (!result.result) { throw new NetworkError(`unknown message receieved, seq=${seq}, data=${response.toString()}`); } } } } else { throw new NetworkError(`unknown owner message receieved, seq=${seq}, data=${response.toString()}`); } } /** * Create a new TLS socket * @param {String} ip IP of channel server * @param {Number} port Port of channel server * @param {Object} authentication A JSON object contains certificate file path, private key file path and CA file path * @return {TLSSocket} A new TLS socket */ function createNewSocket(ip, port, authentication) { let secureContextOptions = { key: fs.readFileSync(authentication.key), cert: fs.readFileSync(authentication.cert), ca: fs.readFileSync(authentication.ca), ecdhCurve: 'secp256k1', }; let secureContext = tls.createSecureContext(secureContextOptions); let socket = new net.Socket(); socket.connect(port, ip); let clientOptions = { rejectUnauthorized: false, secureContext: secureContext, socket: socket }; let tlsSocket = tls.connect(clientOptions); tlsSocket.on('error', function (error) { throw new Error(error); }); let socketID = `${ip}:${port}`; lastBytesRead.set(socketID, 0); tlsSocket.on('data', function (data) { let response = null; if (data instanceof Buffer) { response = data; } else { response = Buffer.from(data, 'ascii'); } if (!buffers.has(socketID)) { // First time to read data from this socket let expectedLength = null; if (tlsSocket.bytesRead - lastBytesRead.get(socketID) >= 4) { expectedLength = response.readUIntBE(0, 4); } if (!expectedLength || tlsSocket.bytesRead < lastBytesRead.get(socketID) + expectedLength) { buffers.set(socketID, { expectedLength: expectedLength, buffer: response }); } else { lastBytesRead.set(socketID, lastBytesRead.get(socketID) + expectedLength); parseResponse(response); buffers.delete(socketID); } } else { // Multiple reading let cache = buffers.get(socketID); cache.buffer = Buffer.concat([cache.buffer, response]); if (!cache.expectedLength && tlsSocket.bytesRead - lastBytesRead.get(socketID) >= 4) { cache.expectedLength = cache.buffer.readUIntBE(0, 4); } if (cache.expectedLength && tlsSocket.bytesRead - lastBytesRead.get(socketID) >= cache.expectedLength) { lastBytesRead.set(socketID, lastBytesRead.get(socketID) + cache.expectedLength); parseResponse(buffers.get(socketID).buffer); buffers.delete(socketID); } } }); return tlsSocket; } /** * Prepare the data which will be sent to channel server * @param {String} data JSON string of load * @return {Object} UUID and packaged data */ function packageData(data) { const headerLength = 4 + 2 + 32 + 4; let length = Buffer.alloc(4); length.writeUInt32BE(headerLength + data.length); let type = Buffer.alloc(2); type.writeUInt16BE(0x12); let uuid = uuidv4(); uuid = uuid.replace(/-/g, ''); let seq = Buffer.from(uuid, 'ascii'); let result = Buffer.alloc(4); result.writeInt32BE(0); let msg = Buffer.from(data, 'ascii'); return { 'uuid': uuid, 'packagedData': Buffer.concat([length, type, seq, result, msg]) }; } /** * Clear context when a message got response or timeout * @param {String} uuid The ID of an `channelPromise`request */ function clearContext(uuid) { clearTimeout(emitters.get(uuid).timer); emitters.delete(uuid); buffers.delete(uuid); } /** * Return channel promise for a request * @param {Object} node A JSON object which contains IP and port configuration of channel server * @param {Object} authentication A JSON object contains certificate file path, private key file path and CA file path * @param {String} data JSON string of load * @param {Number} timeout Timeout to wait response * @param {Boolean} readOnly Is this request read-only? * @return {Promise} a promise which will be resolved when the request is satisfied */ function channelPromise(node, authentication, data, timeout, readOnly = false) { let ip = node.ip; let port = node.channelPort; let connectionID = `${ip}${port}`; if (!sockets.has(connectionID)) { let newSocket = createNewSocket(ip, port, authentication); newSocket.unref(); sockets.set(connectionID, newSocket); } let tlsSocket = sockets.get(connectionID); let dataPackage = packageData(JSON.stringify(data)); let uuid = dataPackage.uuid; tlsSocket.socketID = uuid; let packagedData = dataPackage.packagedData; let channelPromise = new Promise(async (resolve, reject) => { let eventEmitter = new events.EventEmitter(); Object.defineProperty(eventEmitter, 'readOnly', { value: readOnly, writable: false, configurable: false, enumerable: false }); eventEmitter.on('gotresult', (result) => { clearContext(uuid); if (result.error) { reject(result); } else { resolve(result); } return; // This `return` is not necessary, but it may can avoid future trap }); eventEmitter.on('timeout', () => { clearContext(uuid); reject({ 'error': 'timeout' }); return; // This `return` is not necessary, but it may can avoid future trap }); emitters.set(uuid, { emitter: eventEmitter, timer: setTimeout(() => { eventEmitter.emit('timeout'); }, timeout) }); tlsSocket.write(packagedData); }); return channelPromise; } module.exports = channelPromise;
-----------分割线---------------
修改web3sync.js
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib
gedit web3sync.js
/*替换27行 uuid = uuid.replace(/-/g, ''); */
uuid = '0x' + uuid.replace(/-/g, '');
/*替换91行 extraData: ''*/
extraData: '0x0'
/*替换118行 extraData: ''*/
extraData: '0x0'
替换 web3sync.js
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib
/* * 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. */ 'use strict'; const uuidv4 = require('uuid/v4'); const utils = require('./utils'); const Transaction = require('./transactionObject').Transaction; /** * Generate a random number via UUID * @return {Number} random number */ function genRandomID() { let uuid = uuidv4(); uuid = '0x' + uuid.replace(/-/g, ''); return uuid; } /** * Sign a transaction with private key and callback * @param {String} txData transaction data * @param {Buffer} privKey private key * @param {callback} callback callback function * @return {String} signed transaction data */ function signTransaction(txData, privKey, callback) { let tx = new Transaction(txData); let privateKey = Buffer.from(privKey, 'hex'); tx.sign(privateKey); // Build a serialized hex version of the tx let serializedTx = '0x' + tx.serialize().toString('hex'); if (callback !== null) { callback(serializedTx); } else { return serializedTx; } } /** * get transaction data * @param {String} func function name * @param {Array} params params * @return {String} transaction data */ function getTxData(func, params) { let r = /^\w+\((.*)\)$/g.exec(func); let types = []; if (r[1]) { types = r[1].split(','); } return utils.encodeTxData(func, types, params); } /** * get signed transaction data * @param {Number} groupId ID of the group where this transaction will be sent to * @param {Buffer} account user account * @param {Buffer} privateKey private key * @param {Buffer} to target address * @param {String} func function name * @param {Array} params params * @param {Number} blockLimit block limit * @return {String} signed transaction data */ function getSignTx(groupId, account, privateKey, to, func, params, blockLimit) { let txData = getTxData(func, params); let postdata = { data: txData, from: account, to: to, gas: 1000000, randomid: genRandomID(), blockLimit: blockLimit, chainId: 1, groupId: groupId, extraData: '0x0' }; return signTransaction(postdata, privateKey, null); } /** * get signed deploy tx * @param {Number} groupId ID of the group where this transaction will be sent to * @param {Buffer} account user account * @param {Buffer} privateKey private key * @param {Buffer} bin contract bin * @param {Number} blockLimit block limit * @return {String} signed deploy transaction data */ function getSignDeployTx(groupId, account, privateKey, bin, blockLimit) { let txData = bin.indexOf('0x') === 0 ? bin : ('0x' + bin); let postdata = { data: txData, from: account, to: null, gas: 1000000, randomid: genRandomID(), blockLimit: blockLimit, chainId: 1, groupId: groupId, extraData: '0x0' }; return signTransaction(postdata, privateKey, null); } module.exports.getSignDeployTx = getSignDeployTx; module.exports.signTransaction = signTransaction; module.exports.getSignTx = getSignTx; module.exports.getTxData = getTxData;
-----------分割线---------------
caliper 运行错误二:
✔ Compiling /root/benchmarks/caliper-benchmarks/src/fisco-bcos/helloworld/HelloWorld.sol ...
2025.06.03-16:29:30.765 error [caliper] [installSmartContract.js] Failed to install smart contract helloworld, path=/root/benchmarks/caliper-benchmarks/src/fisco-bcos/helloworld/HelloWorld.sol
2025.06.03-16:29:30.766 error [caliper] [installSmartContract.js] Depolying error: TypeError: secp256k1.sign is not a function
2025.06.03-16:29:30.767 error [caliper] [fiscoBcos.js] FISCO BCOS smart contract install failed: TypeError: secp256k1.sign is not a function
at Object.ecsign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/utils.js:141:29)
at Transaction.sign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/transactionObject.js:272:23)
at signTransaction (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:42:8)
at Object.getSignDeployTx (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:121:12)
at Object.module.exports.deploy (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/fiscoBcosApi.js:165:27)
2025.06.03-16:29:30.768 error [caliper] [caliper-flow] Error: TypeError: secp256k1.sign is not a function
at Object.ecsign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/utils.js:141:29)
at Transaction.sign (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/transactionObject.js:272:23)
at signTransaction (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:42:8)
at Object.getSignDeployTx (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/web3lib/web3sync.js:121:12)
at Object.module.exports.deploy (/root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos/lib/fiscoBcosApi.js:165:27)
相应开发者原话:
这是一个愚蠢的bug,指定secp256k1依赖包时版本限制没写对,导致在绑定时自动安装了4.0版本的secp256k1包,但是最新的4.0的包API全部变了,导致执行出错。
有一个临时的解决方案,进入node_modules/@hyperledger/caliper-fisco-bcos目录,编辑该目录下的package.json文件,在"dependencies"中添加一项"secp256k1": “^3.8.0”,随后在该目录下执行npm i,更新完成后测试程序就能启动了。
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos
替换或者修改
/*34行添加 */
"secp256k1": "^3.8.0"
gedit package.json
{ "name": "@hyperledger/caliper-fisco-bcos", "version": "0.2.0", "description": "FISCO BCOS adaptor for Caliper, enabling the running of a performance benchmarks that interact with FISCO BCOS", "repository": { "type": "git", "url": "https://github.com/hyperledger/caliper", "directory": "packages/caliper-fisco-bcos" }, "scripts": { "pretest": "npm run licchk", "licchk": "license-check-and-add", "test": "npm run lint", "lint": "npx eslint .", "nyc": "nyc mocha --recursive -t 10000" }, "engines": { "node": ">=8.10.0", "npm": ">=5.6.0" }, "dependencies": { "@hyperledger/caliper-core": "0.2.0", "nconf": "^0.10.0", "isarray": "^2.0.4", "fs-extra": "^8.0.1", "bn.js": "^4.11.8", "ethereumjs-util": "^6.1.0", "crypto-js": "^3.1.9-1", "ora": "^1.2.0", "uuid": "^3.3.2", "tls": "^0.0.1", "chalk": "^1.1.3", "request-promise": "^4.2.1", "secp256k1": "^3.8.0" }, "devDependencies": { "web3": "0.20.7", "license-check-and-add": "2.3.6" }, "license-check-and-add-config": { "folder": ".", "license": "../../LICENSE.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ "node_modules", ".nyc_output", "coverage", ".gitignore", "log" ], "file_type_method": "EXCLUDE", "file_types": [ ".yml", ".log" ], "insert_license": false, "license_formats": { "js": { "prepend": "/*", "append": "*/", "eachLine": { "prepend": "* " } }, "editorconfig": { "prepend": "#", "append": "#", "eachLine": { "prepend": "# " } } } }, "nyc": { "exclude": [ "lib/**" ], "reporter": [ "text-summary", "html" ], "all": true, "check-coverage": false, "statements": 5, "branches": 8, "functions": 7, "lines": 5 }, "license": "Apache-2.0" }
之后执行下载命令
cd /root/benchmarks/node_modules/@hyperledger/caliper-fisco-bcos && npm i
cd /root/benchmarks/node_modules/@hyperledger && npm i
执行Solidity版转账合约测试
cd /root/benchmarks/caliper-benchmarks
npx caliper benchmark run --caliper-benchconfig benchmarks/samples/fisco-bcos/transfer/solidity/config.yaml --caliper-networkconfig networks/fisco-bcos/4nodes1group/fisco-bcos.json
执行预编译版转账合约测试
cd /root/benchmarks/caliper-benchmarks
npx caliper benchmark run --caliper-benchconfig benchmarks/samples/fisco-bcos/transfer/precompiled/config.yaml --caliper-networkconfig networks/fisco-bcos/4nodes1group/fisco-bcos.json
测试完成后,会在命令行界面中展示测试结果(TPS、延迟等)及资源消耗情况,同时会在caliper-benchmarks
目录下生成一份包含上述内容的可视化HTML报告。
caliper benchmark run
所需的各项参数可以通过如下命令查看:
root@admin:~/benchmarks$ npx caliper benchmark run --help
caliper benchmark run --caliper-workspace ~/myCaliperProject --caliper-benchconfig my-app-test-config.yaml --caliper-networkconfig my-sut-config.yaml
Options:
--help Show help [boolean]
-v, --version Show version number [boolean]
--caliper-benchconfig Path to the benchmark workload file that describes the test client(s), test rounds and monitor. [string]
--caliper-networkconfig Path to the blockchain configuration file that contains information required to interact with the SUT [string]
--caliper-workspace Workspace directory that contains all configuration information [string]
其中,
–caliper-workspace:用于指定caliper-cli的工作目录,如果没有绑定工作目录,可以通过该选项动态指定工作目录;
–caliper-benchconfig:用于指定测试配置文件,测试配置文件中包含测试的具体参数,如交易的发送方式、发送速率控制器类型、性能监视器类型等;
–caliper-networkconfig:用于指定网络配置文件,网络配置文件中会指定Caliper与受测系统的连接方式及要部署测试的合约等。
自定义测试用例
本节将会以测试HelloWorld合约为例,介绍如何使用Caliper测试自定义的测试用例。
Caliper前后端分离的设计原则使得只要后端的区块链系统开放了相关网络端口,Caliper便可以对该系统进行测试。
网络配置
使用已有的链对HelloWorld合约测试(webase-deploy)
前提条件:需要先启动webase-deploy,使用webase-deploy的链进行测试。
cd /fisco/webase-deploy/
python3 deploy.py startAll
新建一个名为test-nw
的目录,本阶示例中的FISCO BCOS适配器的网络配置文件均会放置于此。新建一个名为fisco-bcos.json
的配置文件,文件内容如下:
{
"caliper": {
"blockchain": "fisco-bcos"
},
"fisco-bcos": {
"config": {
"privateKey": "bcec428d5205abe0f0cc8a734083908d9eb8563e31f943d760786edf42ad67dd",
"account": "0x64fa644d2a694681bd6addd6c5e36cccd8dcdde3"
},
"network": {
"nodes": [
{
"ip": "127.0.0.1",
"rpcPort": "8545",
"channelPort": "20200"
},
{
"ip": "127.0.0.1",
"rpcPort": "8546",
"channelPort": "20201"
}
],
"authentication": {
"key": "/fisco/webase-deploy/nodes/127.0.0.1/sdk/node.key",
"cert": "/fisco/webase-deploy/nodes/127.0.0.1/sdk/node.crt",
"ca": "/fisco/webase-deploy/nodes/127.0.0.1/sdk/ca.crt"
},
"groupID": 1,
"timeout": 100000
},
"smartContracts": [
{
"id": "helloworld",
"path": "src/fisco-bcos/helloworld/HelloWorld.sol",
"language": "solidity",
"version": "v0"
},
{
"id": "parallelok",
"path": "src/fisco-bcos/transfer/ParallelOk.sol",
"language": "solidity",
"version": "v0"
},
{
"id": "dagtransfer",
"address": "0x0000000000000000000000000000000000005002",
"language": "precompiled",
"version": "v0"
}
]
},
"info": {
"Version": "2.0.0",
"Size": "4 Nodes",
"Distribution": "Single Host"
}
}
验证一下
cd /root/benchmarks/caliper-benchmarks
npx caliper benchmark run --caliper-benchconfig benchmarks/samples/fisco-bcos/helloworld/config.yaml --caliper-networkconfig networks/fisco-bcos/test-nw/fisco-bcos.json
使用已有的链对HelloWorld合约测试(四节点,单机构,单群组)
前提条件:需要先启动联盟链,使用联盟链进行测试。
cd /root/tools/
bash build_chain.sh -l 127.0.0.1:4 -p 30300,20200,8545 -e ./fisco-bcos -v 2.8.0
bash nodes/127.0.0.1/start_all.sh
新建一个名为test-nw
的目录,本阶示例中的FISCO BCOS适配器的网络配置文件均会放置于此。新建一个名为fisco-bcos.json
的配置文件,文件内容如下:
{
"caliper": {
"blockchain": "fisco-bcos"
},
"fisco-bcos": {
"config": {
"privateKey": "bcec428d5205abe0f0cc8a734083908d9eb8563e31f943d760786edf42ad67dd",
"account": "0x0ff8981ebc130c7874ac7093a4d0c0e3d4f36b08"
},
"network": {
"nodes": [
{
"ip": "127.0.0.1",
"rpcPort": "8545",
"channelPort": "20200"
},
{
"ip": "127.0.0.1",
"rpcPort": "8546",
"channelPort": "20201"
},
{
"ip": "127.0.0.1",
"rpcPort": "8547",
"channelPort": "20202"
},
{
"ip": "127.0.0.1",
"rpcPort": "8548",
"channelPort": "20203"
}
],
"authentication": {
"key": "/root/tools/nodes/127.0.0.1/sdk/sdk.key",
"cert": "/root/tools/nodes/127.0.0.1/sdk/sdk.crt",
"ca": "/root/tools/nodes/127.0.0.1/sdk/ca.crt"
},
"groupID": 1,
"timeout": 100000
},
"smartContracts": [
{
"id": "helloworld",
"path": "src/fisco-bcos/helloworld/HelloWorld.sol",
"language": "solidity",
"version": "v0"
}
]
},
"info": {
"Version": "2.0.0",
"Size": "4 Nodes",
"Distribution": "Single Host"
}
}
}
]
},
"info": {
"Version": "2.0.0",
"Size": "4 Nodes",
"Distribution": "Single Host"
}
}
验证一下
cd /root/benchmarks/caliper-benchmarks
npx caliper benchmark run --caliper-benchconfig benchmarks/samples/fisco-bcos/helloworld/config.yaml --caliper-networkconfig networks/fisco-bcos/test-nw/fisco-bcos.json
使用已有的链对HelloWorld合约测试(四节点,四机构,单群组)
前提条件:需要先启动联盟链,使用联盟链进行测试。
cd /root/tools/
vim ipconf
127.0.0.1 agencyA 1
127.0.0.1 agencyB 1
127.0.0.1 agencyC 1
127.0.0.1 agencyD 1
bash build_chain.sh -f ipconf -e ./fisco-bcos
bash nodes/127.0.0.1/start_all.sh
新建一个名为test-nw
的目录,本阶示例中的FISCO BCOS适配器的网络配置文件均会放置于此。新建一个名为fisco-bcos.json
的配置文件,文件内容如下:
{
"caliper": {
"blockchain": "fisco-bcos"
},
"fisco-bcos": {
"config": {
"privateKey": "bcec428d5205abe0f0cc8a734083908d9eb8563e31f943d760786edf42ad67dd",
"account": "0x0ff8981ebc130c7874ac7093a4d0c0e3d4f36b08"
},
"network": {
"nodes": [
{
"ip": "127.0.0.1",
"rpcPort": "8545",
"channelPort": "20200"
}
],
"authentication": {
"key": "/root/tools/nodes/127.0.0.1/node0/conf/node.key",
"cert": "/root/tools/nodes/127.0.0.1/node0/conf/node.crt",
"ca": "/root/tools/nodes/127.0.0.1/node0/conf/ca.crt"
},
"groupID": 1,
"timeout": 100000
},
"smartContracts": [
{
"id": "helloworld",
"path": "src/fisco-bcos/helloworld/HelloWorld.sol",
"language": "solidity",
"version": "v0"
}
]
},
"info": {
"Version": "2.0.0",
"Size": "4 Nodes",
"Distribution": "Single Host"
}
}
验证一下
cd /root/benchmarks/caliper-benchmarks
npx caliper benchmark run --caliper-benchconfig benchmarks/samples/fisco-bcos/helloworld/config.yaml --caliper-networkconfig networks/fisco-bcos/test-nw/fisco-bcos.json
测试配置
测试配置用于指定测试的具体运行方式。测试配置是一个YAML文件,HelloWorld合约的测试配置文件内容如下所示:
---
test:
name: Hello World
description: This is a helloworld benchmark of FISCO BCOS for caliper
clients:
type: local
number: 1
rounds:
- label: get
description: Test performance of getting name
txNumber:
- 100
rateControl:
- type: fixed-rate
opts:
tps: 1000
callback: benchmarks/samples/fisco-bcos/helloworld/get.js
- label: set
description: Test performance of setting name
txNumber:
- 100
rateControl:
- type: fixed-rate
opts:
tps: 1000
callback: benchmarks/samples/fisco-bcos/helloworld/set.js
monitor:
type:
- docker
- process
docker:
name:
- node0
- node1
- node2
- node3
process:
- command: node
arguments: fiscoBcosClientWorker.js
multiOutput: avg
interval: 0.5
测试文件中主要包括两部分:
测试内容配置
test项负责对测试内容进行配置。配置主要集中在round字段中指定如何对区块链系统进行测试。每一个测试可以包含多轮,每一轮可以向区块链发起不同的测试请求。具体要HelloWorld合约测试,测试中包含两轮,分别对合约的get接口和set接口进行测试。在每一轮测试中,可以通过txNumber或txDuration字段指定测试的交易发送数量或执行时间,并通过rateControl字段指定交易发送时的速率控制器,在本节的示例中,使用了QPS为1000的匀速控制器,更多速率控制器的介绍可以参考官方文档。
性能监视器配置
如果是在本地搭好的链,则可以添加本地性能监视器,相应地监视器的配置更改如下:
monitor:
type:
- process
process:
- command: node0
multiOutput: avg
- command: node1
multiOutput: avg
- command: node2
multiOutput: avg
- command: node3
multiOutput: avg
interval: 0.1
其中每项配置项的解释如下:
1、monitor.type,需要指定为process,只对进程进行监控;
2、monitor.process,一个包含所有要监视的进称列表,其中每个进程的command属性为一个正则表达式,表示进程名称;每个进程还可以有一个arguments属性(未在上述示例中使用到),表示进程的参数。Caliper会先使用ps命令搜索commad + arguments,然后匹配以得到目标的进程的进程ID及系统资源的使用情况。每个进程的multiOutput属性用于指定结果的输出方式,目前支持平均值(avg)及总和(sum)两种方式;
3、monitor.interval,监视器的采样间隔,单位为秒。
需要注意的是,进程监控目前暂不支持监控进程对网络和磁盘的使用情况。
参考文献:
这一篇fabric写的真的没毛病,看得出来作者整合了很多内容
Hyperledger Fabric 2.4.9 环境搭建和 Caliper 0.5.0 测试工具部署教程(Ubuntu)_hyperledger fabric 部署-CSDN博客
这一篇是FSICOBCOS
fisco bcos Caliper部署完整教程(报错解析+自定义测试)_caliper安装-CSDN博客