以太坊多节点私链搭建
区块链大火,配置个以太坊私链玩玩,了解一下这个疯狂的时代,不至于被割韭菜😂
环境准备
- go环境准备(略)
- go-ethereum安装(略)
生成私链创世块的配置
1.创建账户
# 创建privateNet目录
>mkdir privateNet
# 创建账户
>geth --datadir node0 account new
Your new account is locked with a password. Please give a password.
Passphrase:
Repeat passphrase:
Address: {57cbfdb91c9f78fc3b0ec995c44325e63d6e0a74}
# 密码记录到文件中
>echo node0 > node0/password
在其他节点服务器上,按上述说明创建账户,例如生成:Address: {d1be0a55270232c2dc03ee453871f43cc045e906}
2.生成genesis文件
genesis文件定义了私链的第一个块,这里我们使用puppeth工具来生成
>puppeth
+-----------------------------------------------------------+
| Welcome to puppeth, your Ethereum private network manager |
| |
| This tool lets you create a new Ethereum network down to |
| the genesis block, bootnodes, miners and ethstats servers |
| without the hassle that it would normally entail. |
| |
| Puppeth uses SSH to dial in to remote servers, and builds |
| its network components out of Docker containers using the |
| docker-compose toolset. |
+-----------------------------------------------------------+
Please specify a network name to administer (no spaces, please)
> privateNet
Sweet, you can set this via --network= privateNet next time!
INFO [03-20|15:25:21] Administering Ethereum network name= privateNet
WARN [03-20|15:25:21] No previous configurations found path=/ root/.puppeth/privateNet
What would you like to do? (default = stats)
1. Show network stats
2. Configure new genesis
3. Track new remote server
4. Deploy network components
> 2
Which consensus engine to use? (default = clique)
1. Ethash - proof-of-work
2. Clique - proof-of-authority
> 2
How many seconds should blocks take? (default = 15)
> 5
Which accounts are allowed to seal? (mandatory at least one)
> 0xd1be0a55270232c2dc03ee453871f43cc045e906
> 0x57cbfdb91c9f78fc3b0ec995c44325e63d6e0a74
> 0x
Which accounts should be pre-funded? (advisable at least one)
> 0xd1be0a55270232c2dc03ee453871f43cc045e906
> 0x57cbfdb91c9f78fc3b0ec995c44325e63d6e0a74
> 0x
Specify your chain/network ID if you want an explicit one (default = random)
> 105
Anything fun to embed into the genesis block? (max 32 bytes)
>
What would you like to do? (default = stats)
1. Show network stats
2. Manage existing genesis
3. Track new remote server
4. Deploy network components
> 2
1. Modify existing fork rules
2. Export genesis configuration
> 2
Which file to save the genesis into? (default = testnet.json)
> genesis.json
INFO [03-20|15:29:24] Exported existing genesis block
What would you like to do? (default = stats)
1. Show network stats
2. Manage existing genesis
3. Track new remote server
4. Deploy network components
> ^C
genesis.json部分字段说明
字段 | 说明 |
---|---|
chainId | 链id |
nonce | nonce就是一个64位随机数,用于挖矿,注意他和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件 |
mixhash | 与nonce配合用于挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件 |
difficulty | 设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度 |
alloc | 用来预置账号以及账号的以太币数量 |
coinbase | 矿工的账号 |
timestamp | 设置创世块的时间戳 |
parentHash | 上一个区块的hash值,因为是创世块,所以这个值是0 |
extraData | 附加信息,随便填,可以填你的个性信息 |
gasLimit | 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和 |
启动私链网络
1. 启动节点node0
-
创建创世块
geth --datadir node0 init genesis.json
- 这时候可以注意一下,此时node0目录下面会新增出两个文件夹geth和keystore。
- geth中保存的是区块链的相关数据
- keystore中保存的是该链条中的用户信息
- 这时候可以注意一下,此时node0目录下面会新增出两个文件夹geth和keystore。
-
启动节点
geth --datadir node0 --port 30000 --nodiscover --unlock '0' --password ./node0/password console
2. 启动节点node1
-
创建创世块
geth --datadir node1 init genesis.json
-
启动节点
geth --datadir node0 --port 30000 --nodiscover --unlock '0' --password ./node0/password console
3. 建立节点之间的连接
-
在各个节点的console输入admin.nodeInfo.enode,可以分别得到
节点node0 > admin.nodeInfo.enode "enode://d53bd9143a9600dafb94a8629c443a179e77cf11adc02e65f119875f922c19f65d325e0a414c0005fc6879b3e0243b5040611f5c8e63e7de1ba8e1ced15a8ae5@[::]:30000?discport=0" 节点node1 >admin.nodeInfo.enode "enode://7b3d3009760d8db18151260c316e44013d35f940fa9132412d40d1b2be2bd626bd85642ba404ccfd0b9ec10db21dc0352c21e4ab17c9e592190a0c18569f3bd0@[::]:30000?discport=0"
-
在节点0的console输入:
> admin.addPeer("enode://7b3d3009760d8db18151260c316e44013d35f940fa9132412d40d1b2be2bd626bd85642ba404ccfd0b9ec10db21dc0352c21e4ab17c9e592190a0c18569f3bd0@IP1:30000?discport=0")
我们在console中输入admin.peers来验证是否成功建立连接
> admin.peers [{ caps: ["eth/63"], id: "7b3d3009760d8db18151260c316e44013d35f940fa9132412d40d1b2be2bd626bd85642ba404ccfd0b9ec10db21dc0352c21e4ab17c9e592190a0c18569f3bd0", name: "Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.1", network: { localAddress: "IP0:42548", remoteAddress: "IP1:30000" }, protocols: { eth: { difficulty: 1, head: "0xe56dcbf501255547becd99651e7e28c46ba9fa05b4c34c5891051c6f35d11a69", version: 63 } } }]
可以看到,两个节点成功建立起了连接
4. 开启RPC服务
>admin.startRPC("IP0", 8545, "*", "eth,net,web3,admin,personal")
这样我们就可以用钱包(例如:metamask)连接私链了
也可以在启动节点的时候,命令添加相关参数直接打开服务
>geth --networkid 105 --rpc --rpccorsdomain "*" --rpcaddr IP0 --rpcapi "db,eth,net,personal,web3" --datadir node0 --port 30000 --nodiscover --unlock '0' --password ./node0/password console
参数说明
字段 | 说明 |
---|---|
--rpc | 开启rpc连接功能 |
--rpcport | 指定rpc端口,truffle部署时需要连接这个端口 |
--rpccorsdomain | 这个可以指示什么URL能连接到你的节点来执行RPC定制端任务 |
--datadir | 数据文件 |
--port | 节点服务端口 |
--rpcapi | 支持的rpcapi |
--networkid | 链id,对应创始区块的chainId |
console | 进入命令行模式 |
挖矿
在每个节点的console输入如下命令,启动挖矿:
> miner.start()
INFO [03-20|17:09:03] Transaction pool price threshold updated price=18000000000
INFO [03-20|17:09:03] Starting mining operation
信任节点的管理
我们私链使用的是POA共识算法,下面做简单介绍
1. POA的特点
- PoA是依靠预设好的授权节点(signers),负责产生block
- 可以由已授权的signer选举(投票超过50%)加入新的signer
- 即使存在恶意signer,他最多只能攻击连续块(数量是 (SIGNER_COUNT / 2) + 1) 中的1个,期间可以由其他signer投票踢出该恶意signer
- 可指定产生block的时间
2. POA的工作流程
- 在创世块中指定一组初始授权的signers, 所有地址 保存在创世块Extra字段中
- 启动挖矿后, 该组signers开始对生成的block进行 签名并广播.
- 签名结果 保存在区块头的Extra字段中
- Extra中更新当前高度已授权的 所有signers的地址 ,因为有新加入或踢出的signer
- 每一高度都有一个signer处于IN-TURN状态, 其他signer处于OUT-OF-TURN状态, IN-TURN的signer签名的block会 立即广播 , OUT-OF-TURN的signer签名的block会 延时 一点随机时间后再广播, 保证IN-TURN的签名block有更高的优先级上链
- 如果需要加入一个新的signer, signer通过API接口发起一个proposal, 该proposal通过复用区块头 Coinbase(新signer地址)和Nonce("0xffffffffffffffff") 字段广播给其他节点. 所有已授权的signers对该新的signer进行"加入"投票, 如果赞成票超过signers总数的50%, 表示同意加入
- 如果需要踢出一个旧的signer, 所有已授权的signers对该旧的signer进行"踢出"投票, 如果赞成票超过signers总数的50%, 表示同意踢出
3. 添加、删除信任节点
- 通过clique.propose(signer_address,true)添加信任节点
- 通过clique.discard(signer_address)删除信任节点