以太坊数据存证性能与膨胀率测试
我们基于区块链在企业中的应用最广泛的就是“存证”功能需求,这是利用了区块链不可篡改和数据共享的特点,存证的业务数据一方面可以保证留痕和追溯,另一方面也实现了多个节点(如果部署在不同企业和部门)之间的数据共享。如果要实现存证,我们最关心并不是图灵完备,也不是去中心化,而是 存证的性能(也就是TPS)和数据膨胀率(也就是存1M的业务数据,单个节点要消耗多少M的磁盘空间)。
在开源的区块链系统中,最常使用的就是长安链、Fabric和以太坊。
长安链的优势自不必说,国产自主可控(支持国密、支持国产操作系统、国产数据库、国产芯片),性能高(信通院测试存证性能可达到10W TPS),膨胀率低(基于泓存储引擎,对冷数据可启用压缩,可以将膨胀率做到1以下)。
下面我们主要来看看如果用以太坊做存证,那么性能和膨胀率怎么样。
一、搭建以太坊私有链
因为只是测试,所以我搭建的是POA共识的单节点私有链。具体操作过程如下:
1. 环境准备
git checkout v1.10.20 make all
2.创建以太坊账号
./geth account new --datadir=./ INFO [09-13|14:25:52.668] Maximum peer count ETH=50 LES=0 total=50 INFO [09-13|14:25:52.670] Smartcard socket not found, disabling err="stat /run/pcscd/pcscd.comm: no such file or directory" Your new account is locked with a password. Please give a password. Do not forget this password. Password: Repeat password: Your new key was generated Public address of the key: 0x70dA66C22f52f1869B028Ae2D2A86ffF8558cA38 Path of the secret key file: keystore/UTC--2023-09-13T06-25-57.998394704Z--70da66c22f52f1869b028ae2d2a86fff8558ca38 - You can share your public address with anyone. Others need it to interact with you. - You must NEVER share the secret key with anyone! The key controls access to your funds! - You must BACKUP your key file! Without the key, it's impossible to access account funds! - You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
3. 创建创世块配置json
./puppeth +-----------------------------------------------------------+ | Welcome to puppeth, your Ethereum private network manager | | | | This tool lets you create a new Ethereum network down to | | the genesis block, bootnodes, miners and ethstats servers | | without the hassle that it would normally entail. | | | | Puppeth uses SSH to dial in to remote servers, and builds | | its network components out of Docker containers using the | | docker-compose toolset. | +-----------------------------------------------------------+ Please specify a network name to administer (no spaces, hyphens or capital letters please) > test1 Sweet, you can set this via --network=test1 next time! INFO [09-13|14:11:02.594] Administering Ethereum network name=test1 WARN [09-13|14:11:02.594] No previous configurations found path=/data/home/devinyzeng/.puppeth/test1 What would you like to do? (default = stats) 1. Show network stats 2. Configure new genesis 3. Track new remote server 4. Deploy network components > 2 What would you like to do? (default = create) 1. Create new genesis from scratch 2. Import already existing genesis > 1 Which consensus engine to use? (default = clique) 1. Ethash - proof-of-work 2. Clique - proof-of-authority > 2 How many seconds should blocks take? (default = 15) > 1 Which accounts are allowed to seal? (mandatory at least one) > 0x70dA66C22f52f1869B028Ae2D2A86ffF8558cA38 > 0x Which accounts should be pre-funded? (advisable at least one) > 0x Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes) > no Specify your chain/network ID if you want an explicit one (default = random) > 42 INFO [09-13|14:12:36.658] Configured new genesis block What would you like to do? (default = stats) 1. Show network stats 2. Manage existing genesis 3. Track new remote server 4. Deploy network components > 2 1. Modify existing configurations 2. Export genesis configurations 3. Remove genesis configuration > 2 Which folder to save the genesis specs into? (default = current) Will create test1.json, test1-aleth.json, test1-harmony.json, test1-parity.json > INFO [09-13|14:13:53.792] Saved native genesis chain spec path=test1.json ERROR[09-13|14:13:53.792] Failed to create Aleth chain spec err="unsupported consensus engine" ERROR[09-13|14:13:53.792] Failed to create Parity chain spec err="unsupported consensus engine" INFO [09-13|14:13:53.792] Saved genesis chain spec client=harmony path=test1-harmony.json
{ "config": { "chainId": 42, "homesteadBlock": 0, "eip150Block": 0, "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "istanbulBlock": 0, "clique": { "period": 1, "epoch": 30000 } }, "nonce": "0x0", "timestamp": "0x6501568c", "extraData": "0x000000000000000000000000000000000000000000000000000000000000000070da66c22f52f1869b028ae2d2a86fff8558ca380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x47b760000", "difficulty": "0x1", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "alloc": { "70da66c22f52f1869b028ae2d2a86fff8558ca38": { "balance": "50000000000000000000000" } }, "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "baseFeePerGas": null }
这里我们需要去修改一下test1.json文件,可以把gasLimit改大一些,这样我们一个区块中才能放下更多的交易,另外alloc要设置一个初始的ETH在账户1手中,因为是Wei做单位,所以我这里设置的balance是50000000000000000000000看上去很大,只有账户1有ETH,后续才能发起存证交易。
4. 初始化链并启动链
./geth --datadir=./test1 init test1.json
打印日志:Successfully wrote genesis state database=lightchaindata hash=122ef9..a11196
geth会根据刚才的配置文件,在./test1文件夹下创建geth文件夹,里面包含了创世区块数据库。初始化成功后,我们就可以启动链了,启动命令:
./geth --datadir=./test1 --networkid 42 --nodiscover --maxpeers 0 --allow-insecure-unlock console
链启动后,我们这个终端会打印链成功启动的Log,而且进入了与链进行交互的模式。
Welcome to the Geth JavaScript console! instance: Geth/v1.10.20-stable-8f2416a8/linux-amd64/go1.20.7 coinbase: 0x70da66c22f52f1869b028ae2d2a86fff8558ca38 at block: 0 (Wed Sep 13 2023 14:28:28 GMT+0800 (CST)) datadir: /data/go/src/github.com/ethereum/go-ethereum/build/bin/test1 modules: admin:1.0 clique:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0 To exit, press ctrl-d or type exit >
5.解锁账号并开始产块
personal.unlockAccount(eth.accounts[0], "123", 0)
miner.start(1)
二、压测存证交易
6.尝试发送存证交易
因为产块后终端会不断的打印日志,所以我们要发送交易最好是开启一个新的终端,然后在新终端输入命令:
./geth attach ./test1/geth.ipc
personal.newAccount("123")
web3.eth.getBalance(eth.accounts[0])
eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(0.001, "ether"), data: web3.toHex("HelloWorld")})
7.发起大量存证交易进行压测
我们这里先准备一个产生随机字符串的函数randomString,将以下内容粘贴到交互终端中:
function randomString(length) { var result = ''; var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var charactersLength = characters.length; for (var i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; }
然后,我们写一个for循环的逻辑,不断的往链上发送转账交易,交易内容是账号1向账号2转账1Wei,转账的同时附加上1024字节的随机字符串。为了统计一下TPS,我们再在循环开始之前和循环结束之后各记录一下时间。下面是完整的代码:
var startTime = new Date().getTime(); for (var i = 0; i < 10000; i++) { var randomData = 'xx'+randomString(1022); eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: 1, data: web3.toHex(randomData)}); } var endTime = new Date().getTime(); var elapsedTime = endTime - startTime; console.log("For loop execution time: " + elapsedTime + " milliseconds");
8.观察产块和交易池状态
我们回到终端1,可看到大量的交易被接收和被打包的日志,我们再新建一个终端3,同样是附加到交互界面中:
./geth attach ./test1/geth.ipc
然后执行:
txpool.status
miner.stop()
du -h -d 1
三、总结
性能
因为只是我们低配的Linux服务器,所以在TPS上数字并不大,也就是196 TPS,不具有生产环境的参考意义,在POA共识下,高性能服务器的以太坊链TPS肯定是可以轻松上千的。而且我用的是1个账号循环,所以在产生交易上就是串行的,如果真要测性能,可能需要准备几十甚至几百个有账户余额的账号,然后每个账号独立发送交易。
膨胀率
而膨胀率是和机器的配置无关的,只和要存证的数据大小以及每个区块能打包多少笔交易有关。我们简单计算一下:
- 10W笔存证交易,1K/Tx,业务数据大小:100000*1024/1024/1024=97.6M
- 磁盘占用113M
- 所以膨胀率是113/97.6=1.16