Running Etherwall with a private chain

Table of Contents

In april/june 2021 I found myself struggling to run Etherwall with a private blockchain. It turned out to be way more tricky than expected and I decided to describe the actions I had to perform to get it work.

1 Requirements

Here I used Devuan GNU/Linux with a self-compiled Etherwall and Geth from the Ethereum PPA. I shall go into detail of installing the necessary software in another article, here I assume you are already running some GNU/Linux (or at least POSIX) system with Etherwall and Geth ready for use. You might need some basic knowledge of UNIX command line to be able to follow.

The tested versions of Etherwall and Geth are 3.0.2 and 1.10.3-stable, respectively.

2 Prepare Geth directories

We're going to have 3 instances of Geth running simultaneously. The first will be a miner and will be periodically signing new blocks. The second one will also be a full node, synced with the first one, and will be serving clients through a websocket. Please note Geth does not allow a single node to be a miner (which requires an unlocked account on it) and have websocket service enabled at the same time, hence the need for 2 separate nodes. The third node will be started automatically by Etherwall and will not connect to any other node.

Geth normally uses the ~/.ethereum/ directory to store its data. Here, we'll configure the 3 nodes to use directories ~/.ethereum1/, ~/.ethereum2/ and ~/.ethereum3/ instead. This means you can follow this guide without removing or modifying your mainnet Ethereum data that sits in ~/.ethereum/.

$ mkdir ~/.ethereum1 ~/.ethereum2 ~/.ethereum3

2.1 Create accounts

We are going to create 1 account for the first (miner) node and 2 accounts for the third (Etherwall) node. The geth account new command can be used for that. When run, it lets you choose a password for the account and prints some information.

$ geth --datadir ~/.ethereum1/ account new
INFO [06-10|13:11:07.341] Maximum peer count                       ETH=50 LES=0 total=50
Your new account is locked with a password. Please give a password. Do not forget this password.
Password: <you type in your chosen password>
Repeat password: <you re-type your chosen password>

Your new key was generated

Public address of the key:   0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E
Path of the secret key file: /home/urz/.ethereum1/keystore/UTC--2021-06-10T11-11-32.383412301Z--a3bed543dc5de03da9eb8cebdb885439db1c2c1e

- 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!

So, I just created a miner account with address 0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E. I also created 2 other accounts analogously by running geth --datadir ~/.ethereum3/ account new twice. Their addresses are 0x1268439f636479F527e2bE7512928f77AF2078d4 and 0x3fE005F3f85895100459cdcDf39648B5E5176e83.

It might prove useful to know that accounts data is normally kept under the keystore/ subdirectory of Ethereum data directory, one file for one account. You can easily transfer accounts between nodes by simply copying them over. You can even use the same account on different Ethereum blockchains (e.g. mainnet, rinkeby testnet and your private chain).

2.2 Configure the genesis block

As we'll be running a private chain, we need to supply a custom genesis block from which it will start. Geth allows us to specify this custom block's configuration in a json file. Here is the one I prepared:

{
  "config": {
    "chainId": 15,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "berlinBlock": 0,
    "clique": {
      "period": 30,
      "epoch": 30000
    }
  },
  "difficulty": "1",
  "gasLimit": "8000000",
  "extradata": "0x0000000000000000000000000000000000000000000000000000000000000000A3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "alloc": {
    "1268439f636479F527e2bE7512928f77AF2078d4": { "balance": "100000000000000000000" },
    "3fE005F3f85895100459cdcDf39648B5E5176e83": { "balance": "10000000000000000000" }
  }
}

Some of the relevant options are:

2.2.1 chainId

"chainId": 15,

Every Ethereum blockchain has its id. It prevents Ethereum nodes running on different blockchains from connecting with each other and allows transactions from other blockchains to be recognized as such and excluded from being mined. Mainnet uses id 1, we should use something else. Fortunately, as long as we are only running our private chain on a single machine, we don't have to worry about id collisions with other networks and almost any id will suffice.

2.2.2 fork blocks

"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,

From time to time, a change is being introduced to the Ethereum network that changes something in the protocol. Such change requires a so-called fork of the blockchain, where starting from some specific block every participating node starts respecting the new version of the protocol. When running a private chain, we can decide at which blocks certain forks will happen. A common approach is to set most fork blocks numbers to 0, so that all fetures introduced since the launch of Ethereum are immediately available.

2.2.3 difficulty

This field determines the initial difficulty of mining for proof-of-work networks. In our case it is of no interest.

2.2.4 clique

"clique": {
  "period": 30,
  "epoch": 30000
}

On Ethereum mainnet, blocks are mined through proof-of-work, with proof-of-stake in plans. This allows the blockchain to remain decentralized. If we're running a private chain, we're probably doing so for test purposes and we don't mind the control over it being centralized. Hence, we can replace the resource-intensive proof-of-work with a scheme where a group of pre-defined accounts is allowed to sign new blocks. That's what the clique key does. The period subkey determines the minimal time between subsequent blocks.

2.2.5 gasLimit

"gasLimit": "8000000",

Gas limit indirectly limits the number of transactions and smart contract executions that can happen in one block. On Ethereum mainnet, miners periodically vote on gas limit. Here, our miner also has the ability to adjust it after chain's launch, but we can nonetheless specify some intial value and that's how we do it.

2.2.6 extradata

"extradata": "0x0000000000000000000000000000000000000000000000000000000000000000A3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",

Here, we use extradata to specify the account(s) that will be allowed to sign new blocks in the clique scheme. This field's value consists of:

  • 32 zero bytes (64 `0' chars)
  • all signer addresses (in our case there's only one)
  • 65 zero bytes (130 `0' chars)

2.2.7 alloc

"alloc": {
  "1268439f636479F527e2bE7512928f77AF2078d4": { "balance": "100000000000000000000" },
  "3fE005F3f85895100459cdcDf39648B5E5176e83": { "balance": "10000000000000000000" }
}

Using this key we can assign initial ETH balances to certain accounts. Here, I gave 100 ETH to account 0x1268439f636479F527e2bE7512928f77AF2078d4 and 10 ETH to 0x3fE005F3f85895100459cdcDf39648B5E5176e83.

2.3 Initialize the private chain

I saved the json configuration under ~/genesis.json and initialized the chain in ~/.ethereum1/. Geth printed some information in the process.

$ geth --datadir ~/.ethereum1/ init ~/genesis.json 
INFO [06-10|14:17:53.124] Maximum peer count                       ETH=50 LES=0 total=50
INFO [06-10|14:17:53.128] Set global gas cap                       cap=25,000,000
INFO [06-10|14:17:53.128] Allocated cache and file handles         database=/home/urz/.ethereum1/geth/chaindata cache=16.00MiB handles=16
INFO [06-10|14:17:53.159] Writing custom genesis block 
INFO [06-10|14:17:53.163] Persisted trie from memory database      nodes=3 size=408.00B time=1.927914ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [06-10|14:17:53.164] Successfully wrote genesis state         database=chaindata                           hash=c8fa7d..b1dae2
INFO [06-10|14:17:53.164] Allocated cache and file handles         database=/home/urz/.ethereum1/geth/lightchaindata cache=16.00MiB handles=16
INFO [06-10|14:17:53.197] Writing custom genesis block 
INFO [06-10|14:17:53.200] Persisted trie from memory database      nodes=3 size=408.00B time=1.695456ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [06-10|14:17:53.200] Successfully wrote genesis state         database=lightchaindata                           hash=c8fa7d..b1dae2

I then initialized the remaining 2 chains with geth --datadir ~/.ethereum2/ init ~/genesis.json and geth --datadir ~/.ethereum3/ init ~/genesis.json.

3 Run Geth

Before Launching Etherwall, we'll run the first 2 Geth nodes, starting with the miner.

For miner I used the command:

$ geth --datadir ~/.ethereum1/ --netrestrict 127.0.0.1/32 --unlock 0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E --mine --miner.threads=1 --miner.etherbase=0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E --miner.gasprice 44000000000 --miner.gastarget 12000000 --light.serve 90 --port 30304

Geth outputted some information and asked me for miner account password:

INFO [06-10|14:29:19.973] Starting Geth on Ethereum mainnet... 
INFO [06-10|14:29:19.974] Bumping default cache on mainnet         provided=1024 updated=4096
INFO [06-10|14:29:19.980] Maximum peer count                       ETH=50 LES=100 total=150
WARN [06-10|14:29:19.984] LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited 
WARN [06-10|14:29:19.985] Sanitizing cache to Go's GC limits       provided=4096 updated=1285
INFO [06-10|14:29:19.987] Set global gas cap                       cap=25,000,000
INFO [06-10|14:29:19.988] Allocated trie memory caches             clean=192.00MiB dirty=321.00MiB
INFO [06-10|14:29:19.988] Allocated cache and file handles         database=/home/urz/.ethereum1/geth/chaindata cache=641.00MiB handles=2048
INFO [06-10|14:29:20.100] Opened ancient database                  database=/home/urz/.ethereum1/geth/chaindata/ancient readonly=false
INFO [06-10|14:29:20.104] Initialised chain configuration          config="{ChainID: 15 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: 0 EIP155: 0 EIP158: 0 Byzantium: 0 Constantinople: 0 Petersburg: 0 Istanbul: 0, Muir Glacier: <nil>, Berlin: 0, YOLO v3: <nil>, Engine: clique}"
INFO [06-10|14:29:20.107] Initialising Ethereum protocol           network=1 dbversion=<nil>
INFO [06-10|14:29:20.111] Loaded most recent local header          number=0 hash=c8fa7d..b1dae2 td=1 age=52y2mo1w
INFO [06-10|14:29:20.111] Loaded most recent local full block      number=0 hash=c8fa7d..b1dae2 td=1 age=52y2mo1w
INFO [06-10|14:29:20.111] Loaded most recent local fast block      number=0 hash=c8fa7d..b1dae2 td=1 age=52y2mo1w
WARN [06-10|14:29:20.112] Failed to load snapshot, regenerating    err="missing or corrupted snapshot"
INFO [06-10|14:29:20.112] Rebuilding state snapshot 
INFO [06-10|14:29:20.114] Resuming state snapshot generation       root=24ab88..bf5381 accounts=0 slots=0 storage=0.00B elapsed=1.582ms
INFO [06-10|14:29:20.114] Regenerated local transaction journal    transactions=0 accounts=0
INFO [06-10|14:29:20.117] Generated state snapshot                 accounts=2 slots=0 storage=93.00B elapsed=5.096ms
INFO [06-10|14:29:20.142] Allocated fast sync bloom                size=641.00MiB
INFO [06-10|14:29:20.144] Initialized state bloom                  items=3 errorrate=0.000 elapsed="698.832µs"
WARN [06-10|14:29:20.146] Error reading unclean shutdown markers   error="leveldb: not found"
INFO [06-10|14:29:20.147] Allocated cache and file handles         database=/home/urz/.ethereum1/geth/les.server cache=16.00MiB  handles=16
INFO [06-10|14:29:20.148] Stored checkpoint snapshot to disk       number=0 hash=c8fa7d..b1dae2
INFO [06-10|14:29:20.177] Checkpoint oracle is not enabled 
INFO [06-10|14:29:20.179] Starting peer-to-peer node               instance=Geth/v1.10.3-stable-991384a7/linux-arm64/go1.16.3
INFO [06-10|14:29:20.218] New local node record                    seq=1 id=c6c4f5d163aa6c68 ip=127.0.0.1 udp=30304 tcp=30304
INFO [06-10|14:29:20.223] Started P2P networking                   self=enode://79ea9e26dad94ebca4540f1cf3c56b0222686c28773f36eaee0eb38730b6ed81a5d42383628e6c0e0b3bec4672cf773a0e5c35b1cf3192520d8b5877fde5db71@127.0.0.1:30304
INFO [06-10|14:29:20.229] IPC endpoint opened                      url=/home/urz/.ethereum1/geth.ipc
Unlocking account 0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E | Attempt 1/3
Password: <typed in the password>
INFO [06-10|14:29:29.025] Unlocked account                         address=0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E
INFO [06-10|14:29:29.025] Transaction pool price threshold updated price=44,000,000,000
INFO [06-10|14:29:29.025] Transaction pool price threshold updated price=44,000,000,000

It then started continously generating its usual output:

INFO [06-10|14:29:29.025] Commit new mining work                   number=1 sealhash=251a29..1ed516 uncles=0 txs=0 gas=0 fees=0 elapsed="233.916µs"
INFO [06-10|14:29:29.029] Successfully sealed new block            number=1 sealhash=251a29..1ed516 hash=54289c..d3c946 elapsed=3.852ms
INFO [06-10|14:29:29.029] 🔨 mined potential block                  number=1 hash=54289c..d3c946
INFO [06-10|14:29:29.030] Commit new mining work                   number=2 sealhash=2d5d76..efb7af uncles=0 txs=0 gas=0 fees=0 elapsed="829.498µs"
INFO [06-10|14:29:30.465] Looking for peers                        peercount=0 tried=0 static=0
INFO [06-10|14:29:35.647] New local node record                    seq=2 id=c6c4f5d163aa6c68 ip=149.156.124.3 udp=30304 tcp=30304
INFO [06-10|14:29:40.697] Looking for peers                        peercount=0 tried=0 static=0
INFO [06-10|14:29:50.994] Looking for peers                        peercount=0 tried=0 static=0
INFO [06-10|14:29:59.002] Successfully sealed new block            number=2 sealhash=2d5d76..efb7af hash=9359fc..9d8af8 elapsed=29.972s
INFO [06-10|14:29:59.003] 🔨 mined potential block                  number=2 hash=9359fc..9d8af8

One line of output worth noting is the following:

INFO [06-10|14:29:20.223] Started P2P networking                   self=enode://79ea9e26dad94ebca4540f1cf3c56b0222686c28773f36eaee0eb38730b6ed81a5d42383628e6c0e0b3bec4672cf773a0e5c35b1cf3192520d8b5877fde5db71@127.0.0.1:30304

The `enode://' part can be used to tell other nodes how to connect to this one.

For second node, I issued:

geth --datadir ~/.ethereum2/ --syncmode=fast --port 30306 --bootnodes enode://79ea9e26dad94ebca4540f1cf3c56b0222686c28773f36eaee0eb38730b6ed81a5d42383628e6c0e0b3bec4672cf773a0e5c35b1cf3192520d8b5877fde5db71@127.0.0.1:30304 --netrestrict 127.0.0.1/32 --ws --ws.port 8546 --ws.api eth,net,web3 --ws.origins '*' --light.serve 90

And got some output:

INFO [06-10|14:37:05.213] Starting Geth on Ethereum mainnet... 
INFO [06-10|14:37:05.213] Bumping default cache on mainnet         provided=1024 updated=4096
INFO [06-10|14:37:05.214] Maximum peer count                       ETH=50 LES=100 total=150
WARN [06-10|14:37:05.217] LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited 
WARN [06-10|14:37:05.217] Sanitizing cache to Go's GC limits       provided=4096 updated=1285
INFO [06-10|14:37:05.218] Set global gas cap                       cap=25,000,000
INFO [06-10|14:37:05.218] Allocated trie memory caches             clean=192.00MiB dirty=321.00MiB
INFO [06-10|14:37:05.218] Allocated cache and file handles         database=/home/urz/.ethereum2/geth/chaindata cache=641.00MiB handles=2048
INFO [06-10|14:37:05.307] Opened ancient database                  database=/home/urz/.ethereum2/geth/chaindata/ancient readonly=false
INFO [06-10|14:37:05.309] Initialised chain configuration          config="{ChainID: 15 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: 0 EIP155: 0 EIP158: 0 Byzantium: 0 Constantinople: 0 Petersburg: 0 Istanbul: 0, Muir Glacier: <nil>, Berlin: 0, YOLO v3: <nil>, Engine: clique}"
INFO [06-10|14:37:05.312] Initialising Ethereum protocol           network=1 dbversion=<nil>
INFO [06-10|14:37:05.315] Loaded most recent local header          number=0 hash=c8fa7d..b1dae2 td=1 age=52y2mo1w
INFO [06-10|14:37:05.316] Loaded most recent local full block      number=0 hash=c8fa7d..b1dae2 td=1 age=52y2mo1w
INFO [06-10|14:37:05.316] Loaded most recent local fast block      number=0 hash=c8fa7d..b1dae2 td=1 age=52y2mo1w
WARN [06-10|14:37:05.316] Failed to load snapshot, regenerating    err="missing or corrupted snapshot"
INFO [06-10|14:37:05.317] Rebuilding state snapshot 
INFO [06-10|14:37:05.318] Resuming state snapshot generation       root=24ab88..bf5381 accounts=0 slots=0 storage=0.00B elapsed=1.836ms
INFO [06-10|14:37:05.319] Regenerated local transaction journal    transactions=0 accounts=0
INFO [06-10|14:37:05.347] Allocated fast sync bloom                size=641.00MiB
WARN [06-10|14:37:05.350] Error reading unclean shutdown markers   error="leveldb: not found"
INFO [06-10|14:37:05.350] Allocated cache and file handles         database=/home/urz/.ethereum2/geth/les.server cache=16.00MiB  handles=16
INFO [06-10|14:37:05.350] Initialized state bloom                  items=3 errorrate=0.000 elapsed=1.009ms
INFO [06-10|14:37:05.352] Generated state snapshot                 accounts=2 slots=0 storage=93.00B elapsed=34.923ms
INFO [06-10|14:37:05.352] Stored checkpoint snapshot to disk       number=0 hash=c8fa7d..b1dae2
INFO [06-10|14:37:05.391] Checkpoint oracle is not enabled 
INFO [06-10|14:37:05.392] Starting peer-to-peer node               instance=Geth/v1.10.3-stable-991384a7/linux-arm64/go1.16.3
INFO [06-10|14:37:05.425] New local node record                    seq=1 id=6f0172b8174a0bc7 ip=127.0.0.1 udp=30306 tcp=30306
INFO [06-10|14:37:05.429] Started P2P networking                   self=enode://1777be824c12e9c1af0b2362311f00c2418ab3e6ad9b2384bb99ea6c9a146f788e58e748d98f4ffd738498d0ee68581c3acae403809b37725aa46f61769dd0a8@127.0.0.1:30306
INFO [06-10|14:37:05.430] IPC endpoint opened                      url=/home/urz/.ethereum2/geth.ipc
INFO [06-10|14:37:05.431] WebSocket enabled                        url=ws://127.0.0.1:8546
INFO [06-10|14:37:15.432] Block synchronisation started 
INFO [06-10|14:37:15.477] Imported new block headers               count=16 elapsed=20.515ms number=16 hash=eb84ec..bd3d14
INFO [06-10|14:37:15.479] Downloader queue stats                   receiptTasks=0 blockTasks=0 itemSize=522.22B throttle=8192
INFO [06-10|14:37:15.492] Imported new chain segment               blocks=16 txs=0 mgas=0.000 elapsed=12.101ms mgasps=0.000 number=16 hash=eb84ec..bd3d14 dirty=0.00B
INFO [06-10|14:37:15.492] Fast sync complete, auto disabling 
INFO [06-10|14:37:19.852] Looking for peers                        peercount=1 tried=1 static=0

Now, I should probably explain what each command-line option does. Here we go:

3.1 –datadir

--datadir ~/.ethereum1/

Rather obvious, we tell Geth instance to use the specific directory to store data to and load it from.

3.2 –netrestrict

--netrestrict 127.0.0.1/32

This tells Geth to only accept connections from the specified subnet. Useful if we don't need to connect nodes over the internet and we want to eliminate the possibility of external nodes from other chains accidently interfering with ours.

3.3 –unlock

--unlock 0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E

This tells Geth to unlock an account when starting. In this case we unlock the miner account because it is needed for mining in clique scheme.

3.4 –mine

--mine

This tells Geth to mine new blocks for the chain.

3.5 –miner.threads

--miner.threads=1

This tells Geth to only use a single thread for mining (that's more than enough in case of a private chain).

3.6 –miner.etherbase

--miner.etherbase=0xA3bEd543dc5DE03dA9EB8Cebdb885439DB1c2c1E

With this option we specify what account the mined ETH should go to. Here, I chose the miner account.

3.7 –miner.gasprice

--miner.gasprice 44000000000

Given in wei, this option specifies the minimum gas price at which our miner will accept transactions.

3.8 –miner.gastarget

--miner.gastarget 12000000

As our miner works, it adjusts the gas limit. This option specifies the target gas limit it aims for.

3.9 –light.serve

--light.serve 90

This tells Geth to start serving light clients and to spend at most 90% of the tim doing so. Actually, this option is not needed in this setup as we don't have any nodes working in light mode - it's just a leftover from my earlier experiments ;)

3.10 –port

--port 30304

This tells Geth to listen for connections on port 30304. By default it would listen on port 30303. When running multiple instances simultaneously we need to use this option to make them all use different ports.

3.11 –syncmode

--syncmode=fast

This option specifies that a node, when syncing with chain, should not validate all of it, but instead only some number of topmost blocks. This is not really needed here, since out private chain very small.

3.12 –bootnodes

--bootnodes enode://79ea9e26dad94ebca4540f1cf3c56b0222686c28773f36eaee0eb38730b6ed81a5d42383628e6c0e0b3bec4672cf773a0e5c35b1cf3192520d8b5877fde5db71@127.0.0.1:30304

Here we copied the `enode://' value over from node 1's output. This way we instruct node 2 to connect to node 1.

3.13 –ws

--ws

Tells Geth o enable the websocket RPC server (this cannot be used on a node that has an account unlocked).

3.14 –ws.port

--ws.port 8546

With this option we explicitly specify the websocket listening port. The default would be the same I gave here, 8546.

3.15 –ws.api

--ws.api eth,net,web3

Tells Geth what Ethereum APIs to provide over websocket.

3.16 –ws.origins

--ws.origins '*'

Tells Geth what addresses to accept websocket connections from. In this case we allow connections from everywhere (There's nothing sensitive here anyway, it's just a test network).

4 Run Etherwall

We now need to properly configue Etherwall. While it is possible to do it through GUI, I prefer to just edit the configuration file, ~/.config/Etherdyne/Etherwall.conf.

4.1 Initial default configuration

Initially, my Etherwall instance filled its configuration file with the following:

[QQControlsFileDialog]
favoriteFolders=@Invalid()
height=0
sidebarSplit=104.62500000000001
sidebarVisible=true
sidebarWidth=80
width=0

[geth]
args="--syncmode=fast --cache 512"
datadir=/home/urz/.ethereum
logsize=99
path=/usr/bin/geth

[node]
geth\download_link=https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.3-991384a7.tar.gz
geth\lastrun=1623334160
geth\latest_tag=v1.10.3
geth\latest_version=110003

[program]
v2firstrun=@DateTime(\0\0\0\x10\0\0\0\0\0\0%\x86\xf0\x3w\x91\xc5\0)
window\height=623
window\width=890

If you've run Etherwall before and already have a configuration file, it might be a good idea to back it up now.

$ cp ~/.config/Etherdyne/Etherwall.conf ~/.config/Etherdyne/Etherwall.conf.back

4.2 Config file modifications

Now, let's look at what modifications we need to make to the file. All of them are under [geth].

4.2.1 args

This field specifies what custom command line arguments Ethereum should add to Geth invocation.

We change

args="--syncmode=fast --cache 512"

to

args=

In addition to that, Etherwall will add some options on its own. One will tell Geth to use the right data directory, other ones will cause it not to try connecting to other nodes. This was probably the trickiest part to figure out. Initially I didn't realize Etherwall (in thin mode) uses the local node only to sign transactions and I though Geth needs to be passed options that would allow it to find other nodes. And so, I unnecessarily tried adding `–bootnodes' and `–syncmode=light' arguments, etc. Those are not needed.

Another pitfall is the `–networkid' option Geth documentation describes. I thought I should add it here, together with the id previously chosen in genesis.json. It turns out I was wrong, the presence of initialized chain in Ethereum data directory is enough for Geth to learn the chain's id.

Right now it is probably not entirely obvious why we even need to initialize the custom chain in the third node's data directory. That node will not connect to others and will not be verifying blocks, right? That's true. We still need the node to know it is using custom chain, though, because it will be used to sign transactions and transactions from chain with a different id would be incompatible and would result in an invalid sender error when sent.

One thing worth mentioning is we don't really have to have Eherwall spawn a Geth instance. We could run it manually in a terminal tab, with ~/ethereum3/ as data directory and then start Etherwall. The wallet application would then notice a geth.ipc socket file under that data directory and connect to it instead of spawning a new Geth process.

4.2.2 custom

The `custom' field is needed to tell Etherwall to use a custom node for blockchain interaction. By default, in thin client mode, it would be using a public RPC service over websocket, which is obviously not going to work for our private chain.

We add the following line:

custom=true

4.2.3 datadir

We need to inform Etherwall about non-standard data directory location.

We change the default datadir line to:

datadir=~/.ethereum3/

4.2.4 remoteURL

We now need to tell Geth how to contact our custom node that bridges us to the blockchain. You remember we configured our second node to serve RPC APIs over websocket on port 8546 of localhost?

We add:

remoteURL=ws://127.0.0.1:8546

4.3 Final configuration

That's what the desired configuration looks like (keep in mind [geth] part is the relevant one):

[QQControlsFileDialog]
favoriteFolders=@Invalid()
height=0
sidebarSplit=104.62500000000001
sidebarVisible=true
sidebarWidth=80
width=0

[geth]
args=
custom=true
datadir=/home/urz/.ethereum3/
logsize=99
path=/usr/bin/geth
remoteURL=ws://127.0.0.1:8546

[node]
geth\download_link=https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.3-991384a7.tar.gz
geth\lastrun=1623334160
geth\latest_tag=v1.10.3
geth\latest_version=110003

[program]
v2firstrun=@DateTime(\0\0\0\x10\0\0\0\0\0\0%\x86\xf0\x3w\x91\xc5\0)
window\height=623
window\width=890

4.4 Start the application

First 2 Geth nodes are running, Etherwall is configured, we can now start the game. As you might guess, I prefer running Etherwall from the command line, so:

$ Etherwall

If we did everything correctly, Etherwall should quickly spawn node 3, connect to 2 and 3 and finally show our 2 accounts with their balances of 100 and 10 ETH respectively.

Etherwall window

Now we can use Etherwall just as we would when running on Ethereum mainnet.

5 Troubleshooting

Some errors might be obvious. Sometimes useful information can be found by looking at the Logs tab in Etherwall. Below are 2 problems that occured to me and were not obvious at all.

5.1 Etherwall hangs on Connecting to node…

That might indicate some error in the configuration. This thing seems to be fragile. Try resetting your Etherwall config to the good one (Etherwall might have modified it by itself) and re-initializing your Ethereum data directory with our non-mainnet genesis.json before trying again. Also, if you want Etherwall to spawn its own Geth instance, make sure there isn't any leftover old instance running in the data directory you want to use.

5.2 Etherwall reported Invalid sender when I was trying to send a transaction

That's most likely due to chain id mismatch. Ensure all Geth nodes are using the same genesis block.

6 Sources

I browsed zillions of web pages trying to figure out how to configure Etherwall and Geth to do what I want them to. I even tried changing the source of Etherwall. Still, if you simply want to get a better understanding of what is happening here and how to customize the configuration, your best bet is the official Geth documentation.

7 Copyright notice

This article is made available under the Creative Commons Zero license.

Author: Wojciech Kosior

Created: 2021-06-10 Thu 18:40

Validate