Abstract

写到这里的时候,比起吐槽踩过的坑,倒是更想吐槽Caliper的定位

我想大部分想用Caliper来测试合约的人应该是对区块链组网,协议等都有一定熟悉程度了
想看看自己的合约性能如何,能否支持商用等等

然而Caliper的文档和框架包含太多东西了
Docker启动区块链什么的,明白是想让开发者快速上手,但是,真的是多余

更多的文档和测试用例可以多关注在测试部分
毕竟测试部分还是有很多可以深入挖掘的东西

不过,还是感谢这个开源项目,下点功夫吃透之后用起来还是有点香

这篇文章主要打算集中在测试部分,跳过Caliper前期Docker的启动合约的部署,默认已经有一个现有运行中的网络和合约

如果你也有测试现网这个需求,那一定不要错过

Caliper版本为v0.4.2

Repository

  • Github
    准备中:打算新弄个Repo,懒癌发病中,后续更新
    可以先凑活用之前的
    https://blog.csdn.net/weixin_44565484/article/details/123062975?spm=1001.2014.3001.5501

Table of contents

  1. Chapter1:安装Caliper
  2. Chapter2:网络配置
  3. Chapter3:执行
  4. Summary

Chapter1

跟之前的安装过程一样
https://blog.csdn.net/weixin_44565484/article/details/123062975?spm=1001.2014.3001.5501

Chapter2

networkconfig.json

测试已有网络首先要建立个Besu网络
然后将合约部署到网络上,将网络的url,合约地址,Owner私钥设置到下面的文件中
文件位置随意,之后运行npx caliper ... 的时候会指定路径

{
    "caliper": {
        "blockchain": "ethereum",
        "command": {}
    },
    "ethereum": {
        "url": "ws://127.0.0.1:8541",
        // 这里0x后面是Owner的私钥
        "fromAddressSeed": "0x8d5...",
        "transactionConfirmationBlocks": 2,
        "contracts": {
             // 合约名
            "contract_name": {
                // 合约Deploy后生成的地址
                "address": "0x8e89...",
                "estimateGas": true,
                "gas": {
                	// 函数名并设置一个gas值,如果太小会被Revert
                    "contract_function":1000000
                },
                // 合约Deployhou生成的abi.json,仅测试函数部分就可以
                "abi": [
                  {
                    "inputs": [
                      {
                        "internalType": "uint256",
                        "name": "issuerId",
                        "type": "uint256"
                      },
                    ],
                    "name": "contract_function",
                    "outputs": [],
                    "stateMutability": "nonpayable",
                    "type": "function"
                  }
                  ]
            }
            }
        }
    }
}

config.yaml

workload的config文件主要用来设置测试方式
如果仅测试转账功能就可以简单一点,把发送账户,接收账户,金额当作参数传递给workload就可以,这里要看合约函数的输入写的什么

config除了传递参数,非常重要的就是txNumber,workers和tps
rounds就是执行次数,如果workers -> number设置为1,意思就是一个工作者一个一个round按顺序执行

  • txNumber是每个round的workload执行次数,每个round都可以单独设置
  • workers是同时执行的工作者数,比如10round可以设置3个workers,每个人同时随机执行10个rounds
  • tps是每秒发送的tx数,例如txNumber设置为500,tps设置为10,就是一秒钟发送10个tx一共发完500

Caliper有监视功能,发送出去的tx会一直按一定间隔监视,log里可以看到发送了多少tx,执行了多少,还有多少没执行,多少成功了,多少失败了

// 设置参数传递给workload.js
simpleArgs: &params
  count: 1
  fromAccountId: 3000
  moneyToTransfer: 100
  toAccountId: 3000
  
test:
  name: test
  description: >-
    test
  workers:
    type: local
    number: 1
  rounds:
    - label: round1
      description: Test description
      txNumber: 1
      rateControl:
        type: fixed-rate
        opts:
          tps: 1
      workload:
        // workload的路径
        module: dir/to/workload.js
        arguments:
          <<: *params
     - label: round2
      description: Test description
      txNumber: 1
      ...

workload.js

这部分主要生成合约函数的input
比如transfer,要把相应的发送,接收,金额,通过满足合约函数的格式发给Besu网络

  • Caliper负责发送给Besu网络的Class是sutAdapter,这里主要做的就是把参数传递给它
  • sutAdapter可以直接接收参数,也可以打包成数组,它会自动认识如果是数组就把里面每一个元素都生成一个对Besu网络的请求
  • 写了个Template,只关注getParameters()即可
'use strict';

const { WorkloadModuleBase } = require('@hyperledger/caliper-core');

class Workload extends WorkloadModuleBase {
    constructor() {
        super();
    }
     async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) {
        await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext);
    }
    
    /**
     * Custom parameters setting
     */
    getParameters() {
    	// this.roundArguments.count是获取的config传递过来的参数
    	// 有时候每次执行的Request的参数都不同,可以用来生成不同内容
    	// 比如登陆账号,需要每个账号都不同
        const txs = [...Array(this.roundArguments.count).keys()].map((v) => {
        const Args = [这里写合约输入参数]; 
            return {
                // Contract名
                contract: 'contract_name',
                // Method名
                verb: 'contract_function',
                // 此函数内设置的参数
                args: Args
            }
        })
        return txs
    }
    async submitTransaction() {
        const txs = await this.getParameters();
        await this.sutAdapter.sendRequests(txs);
    }
}

function createWorkloadModule() {
    return new Workload();
}

module.exports.createWorkloadModule = createWorkloadModule;
  • 根据合约的内容,参数会有很大不同,如果有AccessControl类的合约函数,还需要本地生成Signature一起发
  • 如果需要区分每个Round,还可以直接在getParameters用this.roundIndex,数值是从0开始到总round数减一

这样在config里设置起始值和count后,就可以得到不同round也每个request都不同的index

 const testids = this.roundArguments.testidstart + v + (this.roundIndex * this.roundArguments.count);

Chapter3

执行测试的时候

  • --caliper-flow-only-test=true设置为仅执行test
  • 设置Chapter2中创建的config.yaml文件为benchconfig
  • 设置Chapter2中创建的networkconfig.json文件为networkconfig
  • 设置当前路径为workspace
npx caliper launch manager \
  --caliper-flow-only-test=true \
  --caliper-benchconfig dir/to/config.yaml \
  --caliper-networkconfig dir/to/networkconfig.json \
  --caliper-workspace .

最后除了Ternimal会生成结果,workspace路径下还会生成一个report.html文件,粘贴到报告上可以体面一点

执行过程中如果出现异常有几种情况

  • 出现异常为 cant open ... connenct() 类似(记不太清了),是网络没连接上,需要查看端口是否匹配,Besu网络是否运行正常
  • 如果被Revert返回的异常与gas有关,需要修改networkconfig.json中的gas值

Summary

  • 中途遇到过很多的异常,没有记录笔记有些记不清了,下次要好好记笔记
  • 测试下来的tps不理想,IBFT2.0还是太慢了
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐