基于Go-Ethereum构建DPOS机制下的区块链

1.Go版本以太坊 2.为何选择DPOS机制 4 智能合约的实践 3 拓展共识改造实战 5 压⼒测试下暴露的问题
展开查看详情

1.基于Go-Ethereum构建DPOS机制下的区块链 恺英⽹网络—朱崇⽂文

2.⽬目录 1 Go版本以太坊 2 为何选择DPOS机制 3 拓拓展共识改造实战 4 智能合约的实践 5 压⼒力力测试下暴暴露露的问题

3.Go版本以太坊 以太坊的客户端 Geth • Go、官⽅方版本实现 Parity • Rust的实现,第⼆二⼤大客户端 以太坊技术协议 cpp-ethereum ethereumj

4.Go版本以太坊 以太坊的⼯工具组 Truffle Solidity 以太坊核⼼心组件 以太坊相关⼯工具组 Metamask Web3.js mist IFPS (Go) 以太坊外部存储 Swarm(Go)

5.Go版本以太坊 以太坊公链⽹网络拓拓扑

6.为何选择DPOS机制 共识机制对⽐比 POW • 消耗计算⼒力力 • 出块速度慢,确认慢 • TPS极低 10~20 • 确认 1分钟+ DPOS • 代理理⼈人模式 • 出块速度快,确认快 • TPS 700~1000 (实现) • 平均确认1~3秒

7.为何选择DPOS机制 DPOS机制的优势 系统可靠性 • 在商业场景下,⽹网络性能可控 • 对异常情况能快速处理理并恢复 • 对TPS/QPS,以及确认性能有⼀一定要求 区块链可信 • 以公有链为基础,可对外开放 • 任何⼈人都可以参与,设⽴立理理事会和⻅见证⼈人⻆角⾊色 • 理理事会管理理区块链⽹网络 • ⻅见证⼈人⽣生产并验证区块

8. 为何选择DPOS机制 理理事会成员 DPOS机制的理理念 委任⻅见证⼈人 ⻅见证⼈人 (矿⼯工) 选举理理事会 社区成员 A B C A ⽣生产/验证区块

9.拓拓展共识改造实战 共识框架引擎—改造共识层逻辑

10.拓拓展共识改造实战 共识框架引擎—官⽅方实现引擎: Ethash / Clique // Engine is an algorithm agnostic consensus engine.
 type Engine interface {
 
 Author(header *types.Header) (common.Address, error) 
 VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
 
 VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
 
 VerifyUncles(chain ChainReader, block *types.Block) error
 
 VerifySeal(chain ChainReader, header *types.Header) error
 
 Prepare(chain ChainReader, header *types.Header) error
 
 Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
 uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
 
 Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error)
 
 CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int
 
 APIs(chain ChainReader) []rpc.API
 }

11.拓拓展共识改造实战 共识框架引擎—Seal核⼼心⽅方法调⽤用 Import Block 其他节点产⽣生区块 
 // Seal generates a new block for the given input block with the local miner's
 // seal place on top.
 Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) go Agent.update work channel go Engine.Seal Result go Worker.wait result channel commit new work

12.拓拓展共识改造实战 借鉴Clique(POA)的实现 跳过步⻓长 n/2 Difficult=2 Clique • Go-Ethereum 实现的机制,
 A B C • In turn ⽤用以公共测试链 • 整个⽹网络由Signer节点出块 • Signer节点可以投票选择其他Signer节点 A • No turn • 节点之间可以相互竞争出块 • 存活节点数 > (n/2) + 1 • Signer 节点的选举记录在Extra Data中 Difficult=1

13.拓拓展共识改造实战 借鉴Clique(POA)的实现 • 满⾜足条件,竞争出块 // Sweet, the protocol permits us to sign the block, wait for our time
 delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
 • ⾮非⾃自⼰己的轮次,⼩小⼩小的延迟 if header.Difficulty.Cmp(diffNoTurn) == 0 {
 // It's not our turn explicitly to sign, delay it a bit
 wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
 delay += time.Duration(rand.Int63n(int64(wiggle)))
 
 select {
 case <-stop:
 return nil, nil
 case <-time.After(delay):
 } 竞争出块的节点有⼀一点延迟,避免过多分叉

14.拓拓展共识改造实战 扩展区块头结构—增加⻅见证⼈人列列表 区块头结构 // Header represents a block header in the Ethereum blockchain.
 type Header struct {
 • 框架程度较⾼高
 ParentHash common.Hash `json:"parentHash" gencodec:"required"`
 UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
 很容易易扩展字段 Coinbase common.Address `json:"miner" gencodec:"required"`
 Root common.Hash `json:"stateRoot" gencodec:"required"`
 • 地址按字典排序 ------------ ----------------- WitnessVotes WitnessVoteSlice `json:"witnessVotes" gencodec:"required"`
 }
 witnessVotes: [{ address: "0x36114a04a4f621da0f19b8453f414cf1f0c40757" }, { address: "0x45db4d996660a6cf312ccf2a6e42fb723bab1be5" 数据案例例 }, { address: "0x6e2059b7aabc5179a01a5b54523747b15620b4e3" }, { address: "0xa2477cadf6b3531072d11dab58e76729346baa69" }, { address: "0xe0263a75d6b7f5ebdcce6546bed9e7b6102a2d8d" }]

15.拓拓展共识改造实战 扩展区块头结构—⻅见证⼈人列列表⽣生成规则 扩展区块头结构 ⻅见证⼈人列列表 Block n+1 ⻅见证⼈人a (a,b) • 区块N的⻅见证⼈人列列表为区块(N-1)
 所达成共识的⻅见证⼈人 Block n ⻅见证⼈人 ⻅见证⼈人列列表 …………………. ⻅见证⼈人列列表
 Block-0 0x000…000 (初始)

16.拓拓展共识改造实战 轮流⽣生产者的实现 — 判断当前轮次是否需要产块 • 当前时间戳 • 当前区块的产块⼈人 is_step_proposer • Parent区块的⻅见证⼈人 • Parent区块的时间戳 • 产块周期

17.拓拓展共识改造实战 轮流⽣生产者的实现 — 场景分析 • 产块周期3秒 , 当前时间T+3 • 三个⻅见证⼈人节点判断该时间点是否应该出块 ⻅见证⼈人列列表
 Timestamp
 Parent Block n ⻅见证⼈人 a (a,b,c) T ⻅见证⼈人列列表 Timestamp
 节点A Block n+1 ⻅见证⼈人 a (a,b,c) T+3 ⻅见证⼈人列列表 Timestamp
 节点B Block n+1 ⻅见证⼈人 b (a,b,c) T+3 ⻅见证⼈人列列表 Timestamp
 节点C Block n+1 ⻅见证⼈人 c (a,b,c) T+3

18.拓拓展共识改造实战 轮流⽣生产者的实现 — 判断当前轮次—代码实现 // parent的signer存在于合法签发列列表中
 if parentOk {
 delta = int64(math.Abs(float64(currentIndex) - float64(parentIndex)))
 // 上⼀一个签发的是⾃自⼰己,则等待len(Signers)周期
 if delta == 0 {
 delta = int64(len(snap.Signers))
 } else {
 计算与Parent块
 // 如果 p 在 c 后⾯面,从P开始计算差
 之间的轮次差值 if (parentIndex > currentIndex) {
 delta = signsLength - delta
 }
 }
 } else {
 // parent不不在当前 singer list中, 则默认从当前的index + 1开始计算delta
 delta = int64(currentIndex + 1)
 } ------------ ----------------- ------------ 和当前出块时间⽐比较 ----------------- if (parentTimestamp + delta * period ) <= currentHeaderTime { return true }

19.拓拓展共识改造实战 轮流⽣生产者的实现 — 注册下⼀一次调⽤用 — 原因 Import Block 其他节点产⽣生区块 • Seal失败,则不不会触发下⼀一次Seal调⽤用 • 其他节点故障,不不会触发Seal调⽤用 go Agent.update work channel go Engine.Seal Result go Worker.wait result channel commit new work

20.拓拓展共识改造实战 轮流⽣生产者的实现 — 注册下⼀一次调⽤用 — 场景分析 • 注册下⼀一次delay时间,最⼤大值是3秒,防⽌止陷⼊入side fork • 最⼩小值 = (3-块打包消耗时间) ⻅见证⼈人列列表
 Timestamp
 Parent Block n ⻅见证⼈人 a (a,b,c) T ⻅见证⼈人列列表 Timestamp
 节点A Block n+1 ⻅见证⼈人a (a,b,c) T+3 Delay
 len(a,b,c)*3 - 3

21.拓拓展共识改造实战 轮流⽣生产者的实现 — 注册下⼀一次调⽤用 — 代码实现 • Seal⽅方法中,通过defer来注册下⼀一次调⽤用 // 注册返回函数,执⾏行行下⼀一次mine
 defer func() {
 var duration = time.Duration(c.config.Period) * time.Second
 var currentTimeStamp = time.Now().Unix()
 // 没有赋值,等待⼀一个默认周期
 计算delay时间 if nextTimeStamp == 0 {
 go c.registerNextDurationTimer(duration)
 } else {
 var interval int64
 // 如果进度滞后,duration = 0
 if currentTimeStamp >= nextTimeStamp {
 interval = 0
 } else {
 // duration时间不不超过⼀一个周期,防⽌止陷⼊入 side fork
 interval = min(nextTimeStamp - currentTimeStamp, int64(c.config.Period))
 }
 duration := time.Duration(interval) * time.Second
 go c.registerNextDurationTimer(duration)
 注册函数 }
 }()

22.拓拓展共识改造实战 轮流⽣生产者的实现 — ⾃自定义奖励规则 • 抽象奖励逻辑 • 实现多种奖励策略略

23. 拓拓展共识改造实战 轮流⽣生产者的实现 — ⾃自定义奖励规则 "config": { "board": { 创世块配置中设置奖励规则 "period": 3, "reward":"default_reward" } }, 定义奖励规则接⼝口 
 type Reward interface AccumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header) 
 var BoardBlockReward *big.Int = big.NewInt(5e+18) // 每⼀一个witness产块的奖励
 //默认产币规则,每⼀一个block奖励5个coin给予⻅见证者 func (c *DefaultReward) AccumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header) {
 reward := new(big.Int).Set(BoardBlockReward)
 实现默认规则 state.AddBalance(header.Coinbase, reward)
 }

24.智能合约的实践 合约语⾔言Solidity

25.智能合约的实践 实现投票合约 — 理理事会

26.智能合约的实践 实现投票合约 — ⻅见证⼈人

27.智能合约的实践 智能合约设计模式 • 合约控制器器模式设计 • 逻辑层和存储层分离 Business_1 Controller DataStorage Business_2

28.智能合约的实践 智能合约并不不智能,反⽽而有太多坑 1. 函数参数不不能超过7个 2. 函数参数不不能是任何变⻓长对象的数组,例例如string[],uint[][6] 3. 合约之间调⽤用不不能传递变⻓长数组,例例如uint[],只能传递定⻓长数组,例例如uint[6] 4. ⼆二维数组的定义⽅方式与⼤大多数语⾔言相反,例例如:uint[][6]表示⼀一个⻓长度为5的uint数组,每个元 素⼜又是⼀一个变⻓长数组 5. 只有internal和private的function才能使⽤用结构类型的参数 6. mapping不不可遍历,⼀一个可以遍历的版本https://github.com/ethereum/dapp-bin/blob/ master/library/iterable_mapping.sol 7. delete不不同对象有不不同的效果,主要为重置为初始值,⽽而⾮非真正删除该对象,详⻅见https:// baijiahao.baidu.com/s?id=1566265348199485&wfr=spider&for=pc 8. 不不要在view⽅方法中使⽤用event,这会导致失去view限制,消耗gas,并导致⽤用户调⽤用⽅方式的改 变 9. 合约创建的⼤大⼩小不不能过⼤大,24k

29.智能合约的实践 智能合约设计模式, 使⽤用单⼀一合约 Business_1 Controller Business_2 Single Business