我們基於區塊鏈在企業中的應用最廣泛的就是“存證”功能需求,這是利用了區塊鏈不可篡改和數據共享的特點,存證的業務數據一方面可以保證留痕和追溯,另一方面也實現了多個節點(如果部署在不同企業和部門)之間的數據共享。如果要實現存證,我們最關心並不是圖靈完備,也不是去中心化,而是 存證的性能(也就是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