Ethereum Light client with React

This is a basic way to setup Ethereum light client with private network. I will talk about 3 things.

  1. What is Ethereum light client?
  2. How to setup server
  3. How to setup client

What is light client?

Light client is an ethereum client which keeps only keys. You can check accurate definition here. Traditionally in blockchain world, everyone keep same databases. That’s why we can trust entire system. I mean we can trust at least our own data. But light client don’t keep data.

Why do light client matter?

But problem is keeping entire database is tough. When I created an ethereum wallet first time I needed to download entire ethereum database. That syncing process took almost whole day and the file size was huge. A web site said the storage size was more than 75GB in 2016 and was growing 1GB per month. This is insane and only blockchain enthusiast can keep it.

You can not run ethereum full node on mobile devices. Or normal users don’t want to keep full node on their devices anyway.

That’s why we need light clients. With light clients, user don’t need to keep ethereum node. They can just enjoy decentralized services. This is pretty cool.

How do we setup light client?

I experimented light client with my private network. Because we can develop dapps for mobile, this has a huge potential.

1. Prepare private network

You need at least 1 ethereum node. Since light client don’t run ethereum node, we need to run nodes for clients to connect.
I installed geth which is Go implementation of the Ethereum protocol. I think geth is most popular. You can check how to install geth here.

Once you install geth, you can setup private network.

First, you have to prepare genesis block. 
You can easily create new genesis block with a command line tool called puppeth which is installed with geth. Here’s my example. I choose proof of authorization as my consensus algorithm because I don’t want heavy computation for my experiment.

{
"config": {
"chainId": <Your ChainID>,
"homesteadBlock": 1,
"eip150Block": 2,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
"byzantiumBlock": 4,
"clique": {
"period": 10,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x5a00bccf",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000<Your address 1>0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"<Your address 1>": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
},
"<Your address 2>": {
"balance": "0x100000000000000000000000000000000000000000000000000000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

I saved this json file as genesis_poa.json.

You can initialize your network with command like this.

geth --datadir <Your data directory> init genesis_poa.json

Now you can start your private network with command like this.

geth --datadir /mnt/privatenet/data_poa \n
--nodiscover --networkid <Your networkID> \n
--rpc --rpcapi db,eth,net,web3,personal,miner,admin \n
--cache=128 \n
--rpcport 8545 --rpcaddr 127.0.0.1 --rpccorsdomain "*"

That’s it. Your geth node is ready.

2. Use Haproxy to make rpc service public

Now your node (rpc service) is running at 127.0.0.1.

But since light clients are not on your server, we need to make this rpc service public.

To do that, I use Haproxy. Haproxy is stable and easy to use load balancer. 
I use Haproxy just as a proxy this time.

And we can use https which I think is very important for security.

Here’s an example of /etc/haproxy/haproxy.cfg

global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon

# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private

# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL). This list is from:
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3
tune.ssl.default-dh-param 2048
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend www
bind 0.0.0.0:80
mode http
timeout client 5s
redirect scheme https code 301 if !{ ssl_fc }
frontend wwws
mode http
bind :443 ssl crt /etc/haproxy/certs/<You Cert>.pem
timeout client 1h

tcp-request inspect-delay 500ms
tcp-request content accept if HTTP

default_backend privatenetwork_backend
acl is_privatenetwork hdr_end(host) -i <Your domain>
use_backend privatenetwork_backend if is_privatenetwork
backend privatenetwork_backend
mode http
option forwardfor
option http-server-close
option forceclose
no option httpclose
balance roundrobin
option forwardfor
timeout server 86400000
timeout connect 4000
server API.js1 127.0.0.1:8545 weight 1 maxconn 10000 check

Now your light client can access to your node (rpc server) through https . 
Let’s prepare client.

React web app with ethereum light client

First, we create React app and prepare modules

I used create-react-app to create React starter project.
create-react-app is a cool tool to create React app quickly.

create-react-app sample_lightclient_app

Install eth-lightwallet, hooked-web3-provider and web3 modules.

eth-lightwallet does the most of magic. eth-lightwallet is developed by ConsenSys and you can use the library to develop light client easily.
we also need hooked-web3-provider which work with eth-lightwallet.
And let’s install web3 too. web3 is official Javascript library for ethereum.

npm install --save eth-lightwallet
npm install --save hooked-web3-provider
npm install --save web3
# Or you may do just npm install

A bit of hack

In your Javascript file,

// step 1: import web3, eth-lightwallet, and hooked-web3-provider
import Web3 from 'web3'
import lightwallet from 'eth-lightwallet'
import HookedWeb3Provider from 'hooked-web3-provider'
// step 2: you may need to tweak crypto module
// THIS IS A BIT OF HACK
import crypto from 'crypto'
const sourceCreateHash = crypto.createHash
crypto.createHash = function createHash(alg) {
if (alg === 'ripemd160') {
alg = 'rmd160'
}
return sourceCreateHash(alg)
}

As of 11/10/2017, you man encounter an error relating to bitcore-lib.

Error: More than one instance of bitcore-lib found

It looks like eth-lightwallet version 2.5.6 has dependancy of bitcore-lib version 0.14.0 because we can find bitcore-lib version 0.14.0 in node_module/eth-lightwallet/package.json.

But bitcore-lib version 0.15.0 seems to be used somewhere else.

So you have to …

1. cd node_modules/eth-lightwallet
2. vi package.json to modify bitcore-lib's version number
# from ^0.14.0 to: ^0.15.0
3. npm install at node_modules/eth-lightwallet directory

I’m not sure whether that change has side effect or not. But for me it’s working. It’s a bit troublesome though because every time I add new module, I need to do “npm install” at eth-lightwallet directory.

Now you are ready to create UI

First step is creating keystore or wallet with keystore. 
I call it wallet.

# step.1: generate seed text

let randomSeed = lightwallet.keystore.generateRandomSeed();
const password = "password"
# This is equivalent to keystore in the document of 
# eth-lightwallet github page.
# I pick the name wallet because in the original document, the term keystore is used both medhod name and generated wallet with keystore and bit confusing.
let wallet
# get pwDerivedKey < 
lightwallet.keystore.deriveKeyFromPassword(
password, (err, pwDerivedKey) => {
  if (err){
console.log("err 1:", err)
}

# goto step.2

Step 2 is generate address. 
It was interesting to me first time that we can generate same address from same seed text. So you don’t need server or other network services to generate an address.

# step.2: add new address
wallet.generateNewAddress(pwDerivedKey);

Step 3 connect to our node (rpc server) with hooked-web3-provider

# step.3: prepare provider
var web3Provider = new HookedWeb3Provider({
host: "https://<your rpc server>",
transaction_signer: wallet
});
const web3 = new Web3(web3Provider);

Here you need hooked-web3-provider. 
That’s it.
From here you can check balance and transfer eth or even execute smart contract.

This is an example of checking balances.

web3.eth.getBalance(address, (err, data) => {
if (err) {
console.log(err);
} else {
let balance = web3.utils.fromWei( data, 'ether');
eth_balance[address] = balance
}
})

And you can save wallet (keystore ) locally.

const serialized_keystore = wallet.serialize()
localStorage.setItem("local storage key", serialized_keystore )

Problem?

That’s it. we can develop dapps for normal users now. I think it’s a big deal because there is a chance for mass adoption!

But is there any problem of light client?
Actually there are a few concerns.

  1. As far as I understand, light client need to connect to a node. And this part is not actually a decentralized network but rather distributed network.
  2. Light client may be a way for free ride. Philosophically blockchain world is a ideal democracy. We expect everyone contribute something.
  3. Security

Security could be the biggest.
Number 1 and 2 are not real problems but philosophical ones.
I think this is a sign of blockchain becoming a main stream service.

Software versions.

eth-lightwallet: 2.5.6
hooked-web3-provider: 1.0.0
web3: 1.0.0-beta.24
node: 8.9.1
haproxy: 1.6.3
geth: 1.6.3
Some software and some technique may be depreciated. Sorry about that.

Here is source code and demo.
Since I added uPort login feature, the codes are slightly different but still it’s simple and easy.

And oops. I have to say this source code is not production ready…

Conclusion

Light client is a huge step for ethereum/blockchain to go to main stream. I think we can even develop React native apps with ethereum light client feature. 
* I have not tested it yet.

Thanks for reading.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.