Recently, I have been going through the source of Ethereum to see if I can change it’s proof of work consensus algorithm with our own algorithm. By doing that I developed an insight of Ethereum source code which I would like to share. The goal of this blog post is to first setup Ethereum development environment and then build very simple consensus algorithm. The consensus algorithm will be very simple as main goal of this activity is to understand the internals of Ethereum. To follow along reader is expected to be equipped with following skills:
- Understanding of ‘go’ language : As Ethereum (geth implementation) is written in go language it is expected that you should have good grasp of language. If you are not familiar with go language it is good motivation to learn now.
- Understanding of how blockchain works : Understanding how blockchain works in general is required. There are quite amazing blogs which explain the working of blockchain.
- Understanding of Ethereum : You cannot hack the application if you don’t know how it works at high level. So understanding of Ethereum at high level is required.
Good development set up makes life easier for making changes to the source code. I have been using ‘goLang’ from jetBrains for a year now. It is very good IDE for go development and have very good debug support. I also use Atom IDE for small projects but for dealing big projects goLang is amazing. Following are the step you need to perform to setup development environment:
- Make sure go is setup properly. To check if it installed on your machine or not type following command in terminal:
> go version
go version go1.9.2 darwin/amd64
If command responds with proper version, you have go installed. If it is not, install command will throw an error “ -bash: go: command not found”.
To install ‘go’, follow instruction specific to your OS platform listed here : https://golang.org/doc/install
- Make sure GOPATH environment variable is properly set up
> echo $GOPATH /Users/hemants/Projects/mist/go-workspace
/Users/hemants/Projects/mist/go-workspace
- Clone Ethereum GitHub repository
> cd $GOPATH/src/github.com/ > mkdir ethereum > git clone git@github.com:ethereum/go-ethereum.git
- Import Ethereum code into GoLand IDE by clicking on ‘open project’ button and locating the directory where you have checked out Ethereum code.
- Once imported successfully run to file go-ethereum/cmd/geth/main.go, right click and run. This will report following errors:
cmd/geth/main.go:125:3: undefined: configFileFlag cmd/geth/main.go:156:3: undefined: initCommand cmd/geth/main.go:157:3: undefined: importCommand cmd/geth/main.go:158:3: undefined: exportCommand cmd/geth/main.go:159:3: undefined: importPreimagesCommand cmd/geth/main.go:160:3: undefined: exportPreimagesCommand cmd/geth/main.go:161:3: undefined: copydbCommand cmd/geth/main.go:162:3: undefined: removedbCommand cmd/geth/main.go:163:3: undefined: dumpCommand cmd/geth/main.go:165:3: undefined: monitorCommand cmd/geth/main.go:165:3: too many errors
- To resolve above mentioned problem go to Run > Edit Configurations > Go Applications > select the run configuration you want to edit > Run kind and change it to File from Package. Then type the name of the package, github.com/ethereum/go-ethereum/cmd/geth and save the settings.
Our development environment is ready. Congrats !! :).
Set up local private Ethereum blockchain
To set up private Ethereum blockchain running locally, let’s first install ‘geth’ tool into $GOPATH/bin directory. To do that, open terminal and go to location $GOPATH/src/github.com/ethereum/go-ethereum, then execute following command from terminal
> go install -v ./cmd/geth
Go to location $GOPATH/bin/ to test if geth is installed properly or not. Issue following command, you should see following result.
> ./geth version
Geth Version: 1.8.12-unstable Architecture: amd64 Protocol Versions: [63 62] Network Id: 1 Go Version: go1.9.2 Operating System: darwin GOPATH=/Users/hemants/Projects/mist/go-workspace GOROOT=/usr/local/Cellar/go/1.9.2/libexec
Next, create file name ‘privategensis.json’ with following content :
{ "config": { "chainId": 15, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 }, "difficulty": "2000000", "gasLimit": "21000000", "alloc": { } }
Go $GOPATH/bin and initialise local Ethereum blockchain by issue following command:
> ./geth --datadir ~/.ethereum/myprivatenet init privategensis.json
INFO [06-21|13:43:05.226227] Maximum peer count ETH=25 LES=0 total=25 INFO [06-21|13:43:05.240084] Allocated cache and file handles database=/Users/hemants/.ethereum/myprivatenet/geth/chaindata cache=16 handles=16 INFO [06-21|13:43:05.244943] Writing custom genesis block INFO [06-21|13:43:05.245018] Persisted trie from memory database nodes=0 size=0.00B time=10.217µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B INFO [06-21|13:43:05.24539] Successfully wrote genesis state database=chaindata hash=07185f…82bcc4 INFO [06-21|13:43:05.245414] Allocated cache and file handles database=/Users/hemants/.ethereum/myprivatenet/geth/lightchaindata cache=16 handles=16 INFO [06-21|13:43:05.24758] Writing custom genesis block INFO [06-21|13:43:05.247618] Persisted trie from memory database nodes=0 size=0.00B time=2.699µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B INFO [06-21|13:43:05.247787] Successfully wrote genesis state database=lightchaindata hash=07185f…82bcc4
This will generate the genesis block for our private blockchain at location : “/.ethereum/myprivatenet”. Lets run our blockchain by issuing following command :
./geth -rpc -rpcapi 'web3,eth,debug,personal' -rpcport 8545 --rpccorsdomain '*' --datadir ~/.ethereum/myprivatenet --networkid 15
INFO [06-21|13:49:27.007881] Maximum peer count ETH=25 LES=0 total=25 INFO [06-21|13:49:27.01546] Starting peer-to-peer node instance=Geth/v1.8.12-unstable/darwin-amd64/go1.9.2 INFO [06-21|13:49:27.015503] Allocated cache and file handles database=/Users/hemants/.ethereum/myprivatenet/geth/chaindata cache=768 handles=128 INFO [06-21|13:49:27.028411] Initialised chain configuration config="{ChainID: 15 Homestead: 0 DAO: DAOSupport: false EIP150: EIP155: 0 EIP158: 0 Byzantium: Constantinople: Engine: unknown}" INFO [06-21|13:49:27.028465] Disk storage enabled for ethash caches dir=/Users/hemants/.ethereum/myprivatenet/geth/ethash count=3 INFO [06-21|13:49:27.028479] Disk storage enabled for ethash DAGs dir=/Users/hemants/.ethash count=2 INFO [06-21|13:49:27.028513] Initialising Ethereum protocol versions="[63 62]" network=16 INFO [06-21|13:49:27.029799] Loaded most recent local header number=0 hash=07185f…82bcc4 td=2000000 INFO [06-21|13:49:27.029836] Loaded most recent local full block number=0 hash=07185f…82bcc4 td=2000000 INFO [06-21|13:49:27.029845] Loaded most recent local fast block number=0 hash=07185f…82bcc4 td=2000000 INFO [06-21|13:49:27.03009] Loaded local transaction journal transactions=0 dropped=0 INFO [06-21|13:49:27.030331] Regenerated local transaction journal transactions=0 accounts=0 INFO [06-21|13:49:27.030806] Starting P2P networking INFO [06-21|13:49:29.146101] UDP listener up self=enode://1dd1494242ee403a69fbb58a57505056f5fea5c9f4b207050ca0a1aecc36d74b4687ec1779320c6282d044aae0078437e5aa7688c29f04e1281cc3f5a0f954e2@[::]:30303 INFO [06-21|13:49:29.146426] RLPx listener up self=enode://1dd1494242ee403a69fbb58a57505056f5fea5c9f4b207050ca0a1aecc36d74b4687ec1779320c6282d044aae0078437e5aa7688c29f04e1281cc3f5a0f954e2@[::]:30303 INFO [06-21|13:49:29.150067] IPC endpoint opened url=/Users/hemants/.ethereum/myprivatenet/geth.ipc INFO [06-21|13:49:29.150472] HTTP endpoint opened url=http://127.0.0.1:8545cors=* vhosts=localhost
In logs, look for line which says “IPC endpoint opened: url=/Users/hemants/.ethereum/myprivatenet/geth.ipc”.
This url allow us to interact with blockchain via IPC channel. Copy the path and fire new terminal and go to $GOPATH/bin and execute following command to attach to IPC channel :
./geth attach /Users/hemants/.ethereum/myprivatenet/geth.ipc
Welcome to the Geth JavaScript console!
modules: admin: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 >
With this console we can execute various commands to interact with blockchain. If you look carefully, after connecting to IPC channel, console prints out all available modules with which you can interact. Namely modules: admin: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 are the modules. To see what functionality is available to use from the console just enter the module name, It will print out all the function names which we can call from the console. Like type ‘personal’ in the console it will response with following function names and properties:
> personal { listAccounts: [], listWallets: [], deriveAccount: function(), ecRecover: function(), getListAccounts: function(callback), getListWallets: function(callback), importRawKey: function(), lockAccount: function(), newAccount: function github.com/ethereum/go-ethereum/console.(*bridge).NewAccount-fm(), openWallet: function github.com/ethereum/go-ethereum/console.(*bridge).OpenWallet-fm(), sendTransaction: function(), sign: function github.com/ethereum/go-ethereum/console.(*bridge).Sign-fm(), signTransaction: function(), unlockAccount: function github.com/ethereum/go-ethereum/console.(*bridge).UnlockAccount-fm() }
Let’s call listAccounts property to see if we have any account.
> personal.listAccounts
[ ]
As expected we don’t have any account yet. So let’s create one.
personal.newAccount(“password”)
“0x66c4c909098df782ef0a52464749de9ef294762c”
Now call personal.listAccounts
personal.listAccounts
[“0x66c4c909098df782ef0a52464749de9ef294762c”]
Let’s check the balance in this account, It should be zero.
> eth.getBalance(personal.listAccounts[0]).toNumber()
0
To generated some ether we need to mine some blocks. Before mining the block we need to tell our node to run as miner node. To do that issue following command
> miner.start(1)
After some time new blocks get generated. As you are the only miner in this network you will be rewarded with ether. Lets check the balance again
> eth.getBalance(personal.listAccounts[0]).toNumber()
10000000000000000000
Great we now have ethers. We can stop miner by specifying ‘miner.stop()’, to stop unnecessary mining. We can check the height of blockchain by running following command :
> web3.eth.getBlockNumber(function(e,r){ console.log(r)})
2
Which is telling us that current height of blockchain is 2. We can view any block by specifying it number by issuing following command :
> web3.eth.getBlock(1)
{ difficulty: 1903376, extraData: "0xd88301080c846765746887676f312e392e328664617277696e", gasLimit: 20979494, gasUsed: 0, hash: "0x6d719d3447497302f59aac284cbb7df585c4cc591c4d351d994d4c234c667ac7", logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner: "0x66c4c909098df782ef0a52464749de9ef294762c", mixHash: "0x094731b8b22a8efba3327b61fe602d4e76e8857ff12db14c8d53afbd651260ff", nonce: "0x082d1975e5cbe2c8", number: 1, parentHash: "0x07185fe5cd8ac52781a3893b7d08b1240c1ded9f84eb97ee68fe2901ed82bcc4", receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 537, stateRoot: "0xa3c4d51416278ab6c77c407027870d9a2c5e214d40e7d32f0275d692ca5ca69f", timestamp: 1529571309, totalDifficulty: 3903376, transactions: [], transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] }
Amazing, now we have properly running Ethereum private network with development environment set up. We are ready to move on to next tutorial.