Meitu DPOS consensus mechanism ETH node network launched and tested

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.

Guess you like

Origin blog.csdn.net/watson2017/article/details/122809104