source code
https://github.com/meitu/go-ethereum
Node network startup
1. For the sake of simple testing, you need to modify the maximum number of validators of the node to the minimum number
consensus/dpos/dpos.go
maxValidatorSize = 3
2. Build the Ethereum docker image
cd $GOPATH/src/github.com/meitu/go-ethereum
docker build . -t mgeth
3. Create a node data directory
mkdir meitu
cd meitu
mkdir node1
mkdir node2
mkdir node3
4. Write the docker-compose.yml startup file
version: '3.3'
services:
mgeth_node_1:
image: mgeth
container_name: mgeth_node_1
build:
context: ..
command: --ipcpath "/root/.ethereum/geth.ipc" --port 30303
ports:
- "15450:8545"
- "15460:8546"
- "10303:30303"
- "10303:30303/udp"
- "10304:30304/udp"
volumes:
- /etc/localtime:/etc/localtime
- ./node1/:/root/.ethereum/
environment:
- TZ=Asia/Shanghai
mgeth_node_2:
image: mgeth
container_name: mgeth_node_2
build:
context: ..
command: --ipcpath "/root/.ethereum/geth.ipc" --port 30303
depends_on:
- mgeth_node_1
ports:
- "25450:8545"
- "25460:8546"
- "20303:30303"
- "20303:30303/udp"
- "20304:30304/udp"
volumes:
- /etc/localtime:/etc/localtime
- ./node2/:/root/.ethereum/
environment:
- TZ=Asia/Shanghai
mgeth_node_3:
image: mgeth
container_name: mgeth_node_3
build:
context: ..
command: --ipcpath "/root/.ethereum/geth.ipc" --port 30303
depends_on:
- mgeth_node_1
ports:
- "45450:8545"
- "45460:8546"
- "40303:30303"
- "40303:30303/udp"
- "40304:30304/udp"
volumes:
- /etc/localtime:/etc/localtime
- ./node3/:/root/.ethereum/
environment:
- TZ=Asia/Shanghai
5. Start 3 Ethereum nodes in the meitu directory
docker-compose up -d
Start idea:
- Configure the first batch of validating nodes in the genesis block and start it.
- Mix POW and DPOS, vote with POW, generate the first batch of validating nodes, and automatically switch to DPOS.
6. Enter the Ethereum node container
docker exec -it mgeth_node_1 /bin/sh
docker exec -it mgeth_node_2 /bin/sh
docker exec -it mgeth_node_3 /bin/sh
7. Get the geth JavaScript console
geth attach ipc:/root/.ethereum/geth.ipc
or can
docker exec -it mgeth_node_1 geth attach ipc:/root/.ethereum/geth.ipc
8. Create a coinbase on node1
personal.newAccount('xxxx')
"0x8807fa0db2c60675a8f833dd010469e408428b83"
Record the above address.
9. Exit the node1 container or open a new window to enter node2, node3
Repeat steps 6-9 to set up coinbase on node2 and node3 respectively.
The addresses of the three nodes are:
0x8807fa0db2c60675a8f833dd010469e408428b83
0xdf5f5a7abc5d0821c50deb4368528d8691f18737
0xe0d64bfb1a30d66ae0f06ce36d5f4edf6835cd7c
10. Write the genesis block configuration file and include the 3 node addresses in the first batch of validators.
mt_genesis.json
{
"config": {
"chainId": 8888,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock":0,
"dpos":{
"validators":[
"0x8807fa0db2c60675a8f833dd010469e408428b83",
"0xdf5f5a7abc5d0821c50deb4368528d8691f18737",
"0xe0d64bfb1a30d66ae0f06ce36d5f4edf6835cd7c"
]
}
},
"nonce": "0x0000000000000042",
"difficulty": "0x99999",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x500000",
"alloc": {}
}
11. Delete the geth directory under each node, keep the keystore directory, and re-init datadir with the founding configuration.
rm -rf node1/geth
rm -rf node2/geth
rm -rf node3/geth
Copy the founding configuration to the data directory so that it can be accessed in the container.
cp mt_genesis.json node1
cp mt_genesis.json node2
cp mt_genesis.json node3
Like step 6, after entering node1, execute init
geth init /root/.ethereum/mt_genesis.json
Repeat init on node2 and node3 respectively.
12. Restart the node network
docker-compose down
docker-compose up -d
13. Enter a node casually and check whether the validator is set successfully.
dpos.getValidators()
["0x8807fa0db2c60675a8f833dd010469e408428b83", "0xdf5f5a7abc5d0821c50deb4368528d8691f18737", "0xe0d64bfb1a30d66ae0f06ce36d5f4edf6835cd7c"]
14. Check whether the nodes are interconnected
admin.peers
[]
If it is empty, it means that the nodes do not discover each other.
15. Make nodes interconnected
View each node information
admin.nodeInfo
Confirm: the enodes are different, and the protocols are the same.
Note down the three enodes
"enode://97caa3fe607663197d8741be1f26a1e53fc936cdef7187cdec737495fa01ae501d95a659bdf48e7d8505a8fc1dacfb715b17775ba86f1575a46d8cffa1f6b509@[::]:30303"
"enode://15bc1789ff8b5108a1e47cf257bae7928d5bfc1eb44c5d61c8dad86f7ac6b985c82dc32e3ffcdf06bd142f4a19183d89c51e4a5fdd1c5447e7172f2e5aadb428@[::]:30303"
"enode://0031048c457163671377bd197d05a78b1c11d474705b29b1b2473b37356d0a4fdf96563fcbda1d6cdf21a6e30f3082935276c4538a82b14da1d660b04ef99213@[::]:30303"
View docker container network information
docker network inspect mgeth_default
"Containers": { "0cd2b0fd97fd53650767302162038878d95b7f39dce48a3db7181d12d3ee673f": { "Name": "meitu_node2_1", "EndpointID": "2fc2b79fb840ba3a2deae61ef4cef8e944684e5259ff88f43283f3a206b3bb90", "MacAddress": "02:42:ac:15:00:03", "IPv4Address": "172.21.0.3/16", "IPv6Address": "" }, "73c6bafac7173b737495df70d091ea7c1a966826edd79e08c20cd3f0f19ce479": { "Name": "meitu_node3_1", "EndpointID": "e16177540cb80ae1f731008e1975135119a12ddaf626f07a160d55e3c2d5b0ba", "MacAddress": "02:42:ac:15:00:04",
"IPv4Address": "172.21.0.4/16",
"IPv6Address": ""
},
"a67590ead292ed4d905eb868c6d8c6049b7796be8dd6464f20f96a019749560b": { "Name": "meitu_node1_1", "EndpointID": "a852916334db8149e43ffcc51176589bf4a379cff7efe48a44eb207e7883d448", "MacAddress": "02:42:ac:15:00:02", "IPv4Address": "172.21.0.2/16", "IPv6Address": "" } },
Record the ip of each node, you can also use 127.0.0.1 plus node to map to different network ports of the machine.
Execute inside node1 container
admin.addPeer("enode://15bc1789ff8b5108a1e47cf257bae7928d5bfc1eb44c5d61c8dad86f7ac6b985c82dc32e3ffcdf06bd142f4a19183d89c51e4a5fdd1c5447e7172f2e5aadb428@[172.21.0.3]:30303")
admin.addPeer("enode://0031048c457163671377bd197d05a78b1c11d474705b29b1b2473b37356d0a4fdf96563fcbda1d6cdf21a6e30f3082935276c4538a82b14da1d660b04ef99213@[172.21.0.4]:30303")
Look at the node network again
admin.peers
[{ caps: ["eth/62", "eth/63"], id: "0031048c457163671377bd197d05a78b1c11d474705b29b1b2473b37356d0a4fdf96563fcbda1d6cdf21a6e30f3082935276c4538a82b14da1d660b04ef99213", name: "Geth/v1.7.4-stable-6be4cd4b/linux-amd64/go1.9.7", network: { localAddress: "172.21.0.2:48258", remoteAddress: "172.21.0.4:30303" }, protocols: { eth: { difficulty: 131072, head: "0x19d4e0c4422cfe81c35cb9420127a6dc590c3e6b09ca835c840d53a601ed1d32", version: 63 } } }, { caps: ["eth/62", "eth/63"],
id: "15bc1789ff8b5108a1e47cf257bae7928d5bfc1eb44c5d61c8dad86f7ac6b985c82dc32e3ffcdf06bd142f4a19183d89c51e4a5fdd1c5447e7172f2e5aadb428",
name: "Geth/v1.7.4-stable-6be4cd4b/linux-amd64/go1.9.7",
network: { localAddress: "172.21.0.2:49844", remoteAddress: "172.21.0.3:30303" }, protocols: { eth: { difficulty: 131072, head: "0x19d4e0c4422cfe81c35cb9420127a6dc590c3e6b09ca835c840d53a601ed1d32", version: 63 } } }]
Note that the interconnection method in step 15 is temporary, and admin.peers will be empty again after each restart of docker.
To facilitate interconnection, bootnodes can be configured in the startup file.
version: '3.3'
services:
mgeth_node_1:
image: mgeth
container_name: mgeth_node_1
build:
context: ..
command: --ipcpath "/root/.ethereum/geth.ipc" --port 30303
ports:
- "15450:8545"
- "15460:8546"
- "10303:30303"
- "10303:30303/udp"
- "10304:30304/udp"
volumes:
- /etc/localtime:/etc/localtime
- ./node1/:/root/.ethereum/
environment:
- TZ=Asia/Shanghai
mgeth_node_2:
image: mgeth
container_name: mgeth_node_2
build:
context: ..
command: --ipcpath "/root/.ethereum/geth.ipc" --port 30303 --bootnodes enode://97caa3fe607663197d8741be1f26a1e53fc936cdef7187cdec737495fa01ae501d95a659bdf48e7d8505a8fc1dacfb715b17775ba86f1575a46d8cffa1f6b509@[172.21.0.2]:30303
depends_on:
- mgeth_node_1
ports:
- "25450:8545"
- "25460:8546"
- "20303:30303"
- "20303:30303/udp"
- "20304:30304/udp"
volumes:
- /etc/localtime:/etc/localtime
- ./node2/:/root/.ethereum/
environment:
- TZ=Asia/Shanghai
mgeth_node_3:
image: mgeth
container_name: mgeth_node_3
build:
context: ..
command: --ipcpath "/root/.ethereum/geth.ipc" --port 30303 --bootnodes enode://97caa3fe607663197d8741be1f26a1e53fc936cdef7187cdec737495fa01ae501d95a659bdf48e7d8505a8fc1dacfb715b17775ba86f1575a46d8cffa1f6b509@[172.21.0.2]:30303
depends_on:
- mgeth_node_1
ports:
- "45450:8545"
- "45460:8546"
- "40303:30303"
- "40303:30303/udp"
- "40304:30304/udp"
volumes:
- /etc/localtime:/etc/localtime
- ./node3/:/root/.ethereum/
environment:
- TZ=Asia/Shanghai
Verify that the nodes are connected again
amdin.peers
[{ caps: ["eth/62", "eth/63"], id: "0031048c457163671377bd197d05a78b1c11d474705b29b1b2473b37356d0a4fdf96563fcbda1d6cdf21a6e30f3082935276c4538a82b14da1d660b04ef99213", name: "Geth/v1.7.4-stable-6be4cd4b/linux-amd64/go1.9.7", network: { localAddress: "172.21.0.2:48356", remoteAddress: "172.21.0.4:30303" }, protocols: { eth: { difficulty: 131072, head: "0x19d4e0c4422cfe81c35cb9420127a6dc590c3e6b09ca835c840d53a601ed1d32", version: 63 } } }, { caps: ["eth/62", "eth/63"],
id: "15bc1789ff8b5108a1e47cf257bae7928d5bfc1eb44c5d61c8dad86f7ac6b985c82dc32e3ffcdf06bd142f4a19183d89c51e4a5fdd1c5447e7172f2e5aadb428",
name: "Geth/v1.7.4-stable-6be4cd4b/linux-amd64/go1.9.7",
network: { localAddress: "172.21.0.2:30303", remoteAddress: "172.21.0.3:47174" }, protocols: { eth: { difficulty: 131072, head: "0x19d4e0c4422cfe81c35cb9420127a6dc590c3e6b09ca835c840d53a601ed1d32", version: 63 } } }]
"Mainnet" launched successfully!
Voting, block verification
Unlock the validator indefinitely on 3 nodes respectively, whoever does not unlock will not generate blocks and skip you. The source code here defaults to 1 block per 10 seconds.
personal.unlockAccount(eth.validator,'xxxx', 0)
According to Meitu’s explanation, here is the difference between validator and coinbase:
coinbase collects mining rewards, validator can be set to other addresses, but the default is the same as coinbase.
3 nodes start mining separately
miner.start()
View block information
eth.getBlock(15)
{ coinbase: "0x8807fa0db2c60675a8f833dd010469e408428b83", difficulty: 1, extraData: "0xd783010704846765746887676f312e392e37856c696e757800000000000000000c50642f0a32b6ad0ac5c94ee6a8e4ef2043aa0a763a6eb817223c9d585454a604e836f37b281fdf7962631aaf2963763783556fee7c6bcebf3df2113f93815001", gasLimit: 5166620, gasUsed: 0, hash: "0x2d598f908d0535d58a82019ff0ec88e666065d3efcfc93293decbe1f26ea5d16",
Logsblॊm: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
Mikshःas: "0ksh0000000000000000000000000000000000000000000000000000000000000000"
Notrrche: "0ksh0000000000000000",
No. 15,
Prentaas: "0ksh2b52l52d2c5a60ac873c850blbefa78l2bfa940fa04a8756944dfad0fa85fa8fade3"
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 794,
stateRoot: "0x45e73d97158ba78c2afae887038b18d70d92e865dc3b0b175423061bf6bde29b",
timestamp: 1530254800,
totalDifficulty: 131087,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: [],
validator: "0x8807fa0db2c60675a8f833dd010469e408428b83"
}
eth.getBlock(16)
{ coinbase: "0xe0d64bfb1a30d66ae0f06ce36d5f4edf6835cd7c", difficulty: 1, extraData: "0xd783010704846765746887676f312e392e37856c696e757800000000000000000a6277cd4e5153f10abb221aa850ed64059b6fe93b1451417321cf17190a1f6b56707ca05550d4727f48874ce0899c3012990b361fe79e88d4ca51788eb6500e01", gasLimit: 5161576, gasUsed: 0, hash: "0x4de33e49fb536a710737fb1e09c4f713a0ca7e5511682451e006898e71af365d",
Logsblॊm: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
Mikshःas: "0ksh0000000000000000000000000000000000000000000000000000000000000000"
Notrrche: "0ksh0000000000000000",
No. 16,
Prentaas: "0ksh2d598fa908d0535d58a820l9farfa0ac88a666065d3afarchfach93293decbelfa26aa5dl6"
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 794,
stateRoot: "0xe50a0e2181198d8543dcaf26be57f2c004e92ee446195d75fe447fcf6c4f0992",
timestamp: 1530254810,
totalDifficulty: 131088,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: [],
validator: "0xe0d64bfb1a30d66ae0f06ce36d5f4edf6835cd7c"
}
eth.getBlock(17)
{ coinbase: "0xdf5f5a7abc5d0821c50deb4368528d8691f18737", difficulty: 1, extraData: "0xd783010704846765746887676f312e392e37856c696e757800000000000000004bbf4fbec4706ecae18a4cbb0d92a9c9546e13503ca818f7b22860c78f5bac0748196864c697dabf6891c372e6911c1664ced36fdebfaa9ac2b608a9cb1beba701", gasLimit: 5156537, gasUsed: 0, hash: "0x743db2aa9732c26e80d21fa81fca75e8a952ea9f219b5720b5af7ebf7f832abd",
Logsblॊm: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
Mikshःas: "0ksh0000000000000000000000000000000000000000000000000000000000000000"
Notrrche: "0ksh0000000000000000",
No. 17,
Prentaas: "0ksh4de33a49fab536a7l0737fabla09c4fa7l3a0c7a55ll68245la006898a7lafa365d"
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 794,
stateRoot: "0x3dcb37771aceff66ff2af6f281e7306ead90824f0ecea18f60ffd87fd722731c",
timestamp: 1530254820,
totalDifficulty: 131089,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: [],
validator: "0xdf5f5a7abc5d0821c50deb4368528d8691f18737"
}
You can see that 3 nodes take turns to output blocks, 1 block every 10 seconds.
Test 1:
If node 2 cannot produce blocks for some reason, then in the next round, when node 1 produces a block, node 3 produces a block after an interval of 20 seconds.