Stress test on private geth node

Ko
4 min readMar 29, 2018

I am developing a plasma inspired system to use blockchain for micro transactions. But while experimentation, I experienced private node crashes a couple of times. So I setup a private geth node and sent multiple transactions to see what happens.

By the way plasma is one of scaling solution for Ethereum network which use side chains very elegantly.

Prepare a private network

Install geth on ubuntu

sudo apt-get install software-properties-common 
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

Prepare geth node

I created 3 accounts and used Proof of Authority as consensus algorithm. A json file to create a genesis block is names as test1.json.

mkdir data
cd test
# Step 1. Create accounts
geth account new --datadir ./data
Address: {d01427b6c3c2728a2ba8153558c36eac10015ba4}
geth account new --datadir ./data
Address: {00bf79c20d3524c4cf75217f0781af0f9ef3bd41}
geth account new --datadir ./data
Address: {7ed7574c4f3d3de2ba3e11910244dfca0f89d063}
# Step2. Create a genesis json file with puppeth
puppeth
# Step3. Create a genesis block
geth --datadir ./data init test1.json

Test1.json

{
"config": {
"chainId": 34787,
"homesteadBlock": 1,
"eip150Block": 2,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
"byzantiumBlock": 4,
"clique": {
"period": 5,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x5abbd26b",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000d01427b6c3c2728a2ba8153558c36eac10015ba40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"00bf79c20d3524c4cf75217f0781af0f9ef3bd41": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
},
"7ed7574c4f3d3de2ba3e11910244dfca0f89d063": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

Start a private geth node

# start a geth node
nohup geth --datadir ./data --port 30303 --nodiscover --networkid 34787 --rpc --rpcapi eth,web3,personal,admin,net,txpool --cache=128 --rpcport 8545 --rpcaddr 127.0.0.1 --rpccorsdomain "*" &
# start mining(sealing)
geth attach data/geth.ipc
personal.unlockAccount(eth.accounts[0], "password", 0)
miner.star()

Prepare codes

Install Node
I just followed the steps on this article.

sudo apt-get update
sudo apt-get install build-essential libssl-dev
curl -sL https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh -o install_nvm.sh
bash install_nvm.s
source ~/.profile
nvm install 8.11.0

Prepare node scripts

npm init

Prepare bable

# create .babelrc
{
"presets": ["env"]
}
# Add devDependencies in package.json
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.6.0",
"babel-preset-es2015": "^6.24.1"
},
# install dev babel related codes
npm install
# install dependancies like web3
npm install web3@1.0.0-beta.26

Let’s create scripts for stress test

Case 1. send 300 simple ether transfer transactions
This works without a hiccup

// prepare web3
import Web3 from 'web3'
const web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
const addresses = [
"0x0000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000002",
...
"0x0000000000000000000000000000000000000299",
"0x0000000000000000000000000000000000000300",
]
const test = () => {console.log("stress test started")const sender_address = "0x00bf79c20d3524c4cf75217f0781af0f9ef3bd41"
const sender_password = "_password_"
Promise.resolve()
.then((results) => {
// unlock an account
return web3.eth.personal.unlockAccount(sender_address, sender_password)
})
.then(() => {
// give away ethers
console.log("addresses count: ", addresses.length)// promise sequence
return new Promise((resolve, reject) => {
let sequence = Promise.resolve()
for ( let idx=0; idx<addresses.length; idx++ ) {
const receiver = addresses[idx]sequence = sequence.then(() => {
// transfer ether
const params = {
from: sender_address,
to: receiver,
value: 500000000000,
}
// return web3.eth.sendTransaction(params)
web3.eth.sendTransaction(params)
})
.then((results) => {console.log("results: ", results)if ( idx === ( addresses.length - 1 ) ){
resolve()
} else {
return "OK"
}
})
.catch((err) =>{
console.log("err: ", err)
if ( idx === ( addresses.length - 1 ) ){
resolve()
} else {
return "NG"
}
})
}
})
})
.then(() => {
console.log("stress test ended")
})
.catch((err) => {
console.log("err: ", err)
})
}
test();

Case 2. send 300 ERC20 transfer trnasactions

Then I tried 300 ERC20 token transfer transactions.
The difference is just ERC20 token parts
This also worked without any problem.

// prepare web3
import Web3 from 'web3'
const web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
const addresses = [
"0x0000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000002",
...
"0x0000000000000000000000000000000000000299",
"0x0000000000000000000000000000000000000300",
]
import CHILD_TOKEN_JSON from './TokenChild.json'
let token_instance = null
// prepare token contarct instance
const prepareToken = ({ web3 }) => {
return Promise.resolve()
.then(() => {
// get instance if needed
if (token_instance !== null){
return Promise.resolve()
}
return new web3.eth.Contract( CHILD_TOKEN_JSON.abi, "0x893e5301c1ab7a745d355c8d8d92b0d617b4232f")
})
.then((results) => {
// set instance if needed
if (token_instance !== null){
return Promise.resolve()
}
token_instance = results
return Promise.resolve()
})
}
const test = () => {console.log("stress test started")const sender_address = "0x00bf79c20d3524c4cf75217f0781af0f9ef3bd41"
const sender_password = "_password_"
Promise.resolve().then((results) => {
// prepare token
return prepareToken({ web3: web3 })
})
.then((results) => {
// unlock an account
return web3.eth.personal.unlockAccount(sender_address, sender_password)
})
.then(() => {
// give away ethers
// promise sequence
return new Promise((resolve, reject) => {
let sequence = Promise.resolve()
for ( let idx=0; idx<addresses.length; idx++ ) {
const receiver = addresses[idx]sequence = sequence.then(() => {// get gas estimates
return token_instance.methods.transfer(
receiver,
500000000000
).estimateGas({
from: sender_address,
})
})
.then((results) => {
const estimatedGas = parseInt(results)
// execute transfer
// return token_instance.methods.transfer(
token_instance.methods.transfer(
receiver,
500000000000
).send({
from: sender_address,
gasPrice: 100000000000,
gas: estimatedGas,
})
})
.then((results) => {
console.log("results: ", results)if ( idx === ( addresses.length - 1 ) ){
resolve()
} else {
return "OK"
}
})
.catch((err) =>{
console.log("err: ", err)
if ( idx === ( addresses.length - 1 ) ){
resolve()
} else {
return "NG"
}
})
}
})
})
.then(() => {
console.log("stress test ended")
})
.catch((err) => {
console.log("err: ", err)
})
}
test();

Hmmmm…

Looks like Geth node is good enough and my Geth node crashed again and again may be because of running out of memory….or other bugs…

[UPDATED!] I finally found the cause of problem. In my code, I unlocked the account every time I submitted the transactions.
I mean I unlocked the account 100 times to submit 100 transactions.

And looks like unlocking account used significant amount of memory and this caused out of memory error.

Thanks for reading….

--

--

Ko

I'm a serial entrepreneur. I enjoy AI, UI, and blockchain. I like history and reading too.