以太坊數據存證性能與膨脹率測試

我們基於區塊鏈在企業中的應用最廣泛的就是“存證”功能需求,這是利用了區塊鏈不可篡改和數據共享的特點,存證的業務數據一方面可以保證留痕和追溯,另一方面也實現了多個節點(如果部署在不同企業和部門)之間的數據共享。如果要實現存證,我們最關心並不是圖靈完備,也不是去中心化,而是 存證的性能(也就是TPS)和數據膨脹率(也就是存1M的業務數據,單個節點要消耗多少M的磁盤空間)。

在開源的區塊鏈系統中,最常使用的就是長安鏈、Fabric和以太坊。

長安鏈的優勢自不必說,國產自主可控(支持國密、支持國產操作系統、國產數據庫、國產芯片),性能高(信通院測試存證性能可達到10W TPS),膨脹率低(基於泓存儲引擎,對冷數據可啓用壓縮,可以將膨脹率做到1以下)。

下面我們主要來看看如果用以太坊做存證,那麼性能和膨脹率怎麼樣。

一、搭建以太坊私有鏈

因爲只是測試,所以我搭建的是POA共識的單節點私有鏈。具體操作過程如下:

1. 環境準備

在Linux服務器上下載並安裝Go環境,安裝git,clone go-ethereum代碼到本地。這裏注意我們採用的是v1.10.20版,並不是最新版本,因爲以太坊代碼升級改動很大,新版本可能一些命令已經不支持了。
git checkout v1.10.20
make all
會準備好我們需要建測試鏈用的工具到build/bin文件夾中。

2.創建以太坊賬號

執行以下命令,geth會創建一個新的以太坊私鑰和地址:
./geth account new --datadir=./test1
這裏注意我們的測試鏈用的是test1文件夾,所以在命令中要指定文件夾路徑。以下是我執行結果:
./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來生成創世區塊配置文件,(這個工具在新版本的go-ethereum中已經被刪除了。)
./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
這裏我們需要注意的是,共識算法要選Clique,這個算法在測試鏈上性能比傳統的Pow高很多,而且不耗資源。出塊時間可以寫的比較小,我這裏選的是1s出一個塊。
至此我們要用的創世配置文件已經導出好了。按Control+D退出當前命令行界面。下面是我剛纔生成的創世區塊配置文件:
{
  "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)
可以解鎖剛纔創建的以太坊賬號,因爲我密碼設的是123,所以第二個參數是123,第三個參數0是表示這個解鎖不會過期,一直處於解鎖狀態。
解鎖賬號後,我們就可以用這個賬號進行產塊了。命令行輸入:
miner.start(1)
開始產塊。

二、壓測存證交易

 6.嘗試發送存證交易

因爲產塊後終端會不斷的打印日誌,所以我們要發送交易最好是開啓一個新的終端,然後在新終端輸入命令:

./geth attach ./test1/geth.ipc
進入了與鏈交互的模式。
personal.newAccount("123")
可以創建一個新的賬號2,我們使用以下命令查詢賬號1的餘額:
web3.eth.getBalance(eth.accounts[0])
如果返回0,那麼賬號1是不能發起任何交易的。所以這裏確保返回值不是0。如果是0,那麼回到步驟3,重新修改json中的“alloc”字段,然後重新初始化,重新啓動鏈哈。
使用以下命令,我們嘗試從賬號1給賬號2轉賬1Wei,轉賬的同時附加上字符串“HelloWorld”:
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
可以查看交易池的狀態,如果發送交易的速度遠快於交易被打包到區塊中的速度,那麼交易池中就會堆積大量交易。所以我們在終端2中如果交易發送完了,還需要再在終端3中查看一下交易池狀態,確保所有交易被打包完畢,也就是交易池爲0,則說明我們的壓測纔算完成了。
9.結束鏈並統計
我們回到終端1,因爲一直在產塊,所以終端1一直在打印日誌,不過沒關係,我們任然可以輸入命令:
miner.stop()
即可結束產塊,然後輸入exit即可退出終端1的交互,與此同時整個鏈進程也結束了。
我們來到./test1/geth文件夾,運行如下命令:
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
這個膨脹率還算是很優秀的,只比長安鏈泓存儲引擎開啓冷熱分離後的0.9高出一點點。需要注意這是在存證1K業務數據的情況下,如果存證32字節,那麼膨脹率就會高很多,10W筆32字節是3.05M業務數據,上鍊後磁盤佔用18M,膨脹率達到6.0。
其他業務數據大小的時候膨脹率大家可以根據我上面給出的腳本實際測試。
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章