Commit 3fd0fd25 authored by 黄刚's avatar 黄刚

更新文档

parent 3048bb00
# 共识模 # 共识模
# 共识模块 # 共识模块
<!-- TOC --> <!-- TOC -->
- [共识模块](#共识模块) - [共识模块](#共识模块)
- [1.模块介绍](#1-模块介绍) - [1.模块介绍](#1模块介绍)
- [2.逻辑架构及上下文](#2-逻辑架构及上下文) - [2.逻辑架构及上下文](#2逻辑架构及上下文)
- [3.处理逻辑](#3-处理逻辑) - [3.处理逻辑](#3处理逻辑)
- [3.1加载共识模块](#3.1-加载共识模块) - [3.1 模块接口](#31-模块接口)
- [接口定义](#3.1.1-接口定义) - [接口定义](#接口定义)
- [通用处理逻辑](#3.1.2-通用处理逻辑) - [3.2 Minner接口](#32-minner接口)
- [3.2关闭共识模块](#3.2-关闭共识模块) - [接口定义](#接口定义-1)
- [接口定义](#3.2.1-接口定义) - [4. 二次开发](#4-二次开发)
- [通用处理逻辑](#3.2.2-通用处理逻辑) - [4.1 整体介绍](#41-整体介绍)
- [3.3创建创世区块交易](#3.3-创建创世区块交易) - [4.2 结构定义](#42-结构定义)
- [接口定义](#3.3.1-接口定义) - [4.3 配置](#43-配置)
- [通用处理逻辑](#3.3.2-通用处理逻辑) - [4.4 实现Module接口](#44-实现module接口)
- [3.4获取创世区块时间](#3.4-获取创世区块时间) - [4.5 实现Miner接口](#45-实现miner接口)
- [接口定义](#3.4.1-接口定义)
- [通用处理逻辑](#3.4.2-通用处理逻辑)
- [3.5创建区块](#3.5-创建区块)
- [接口定义](#3.5.1-接口定义)
- [通用处理逻辑](#3.5.2-通用处理逻辑)
- [3.6检查区块](#3.6-检查区块)
- [接口定义](#3.6.1-接口定义)
- [通用处理逻辑](#3.6.2-通用处理逻辑)
- [3.7处理自定义事件](#3.7-处理自定义事件)
- [接口定义](#3.7.1-接口定义)
- [通用处理逻辑](#3.7.2-通用处理逻辑)
<!-- /TOC --> <!-- /TOC -->
<h2 id="1-模块介绍"> 1.模块介绍</h2> ## 1.模块介绍
共识模块是实现区块链共识机制的模块,共识机制是区块链技术的重要组件。区块链共识机制的目标是使所有的诚实节点保存一致的区块链视图,同时满足两个性质: 共识模块是实现区块链共识机制的模块,共识机制是区块链技术的重要组件。区块链共识机制的目标是使所有的诚实节点保存一致的区块链视图,同时满足两个性质:
- **一致性**:所有诚实节点保存的区块链的前缀部分完全相同。 - **一致性**:所有诚实节点保存的区块链的前缀部分完全相同。
- **有效性**:由某诚实节点发布的信息终将被其他所有诚实节点记录在自己的区块链中。 - **有效性**:由某诚实节点发布的信息终将被其他所有诚实节点记录在自己的区块链中。
...@@ -34,7 +25,7 @@ ...@@ -34,7 +25,7 @@
通俗来说,共识机制在区块链网络内起到决定谁负责生成新区块以及维护区块链统一的作用。 通俗来说,共识机制在区块链网络内起到决定谁负责生成新区块以及维护区块链统一的作用。
目前区块链的共识机制大致可以分为PoW(Proof of Work 工作量证明)、PoS(Proof of Stack 权益证明)、DPoS(Delegated Proof of Stake 委托权益证明)以及分布式一致性算法几类。 目前区块链的共识机制大致可以分为PoW(Proof of Work 工作量证明)、PoS(Proof of Stack 权益证明)、DPoS(Delegated Proof of Stake 委托权益证明)以及分布式一致性算法几类。
<h2 id="2-逻辑架构及上下文">2.逻辑架构及上下文</h2> ## 2.逻辑架构及上下文
*共识模块上下文* *共识模块上下文*
目前chain33系统中的共识模块是可插拔的,支持平行链共识,PBFT,Raft,Tendermint,Ticket几种共识算法。共识模块在整个chain33系统中所处的地位如下图: 目前chain33系统中的共识模块是可插拔的,支持平行链共识,PBFT,Raft,Tendermint,Ticket几种共识算法。共识模块在整个chain33系统中所处的地位如下图:
...@@ -57,194 +48,206 @@ ...@@ -57,194 +48,206 @@
> 共识模块内部逻辑 > 共识模块内部逻辑
![共识模块内部图](./reources/Consensus_logic.png) ![共识模块内部图](./resources/Consensus_logic.png)
如上图所示,共识模块主要通过Miner和Module两个接口实现主要逻辑功能。 如上图所示,共识模块主要通过Miner和Module两个接口实现主要逻辑功能。
<h2 id="3-处理逻辑">3.处理逻辑</h2> ## 3.处理逻辑
> 下面分别介绍每个接口的具体功能以及实现逻辑 > 下面分别介绍这两个接口的具体功能。
<h3 id="3.1-加载共识模块">3.1 加载共识模块</h3> ### 3.1 模块接口
<h4 id="3.1.1-接口定义">接口定义</h4> #### 接口定义
```go ```go
/// Module be used for module interface /// Module be used for module interface
type Module interface { type Module interface {
//加载共识模块
SetQueueClient(client Client) SetQueueClient(client Client)
//关闭共识模块,完成清理工作
Close()
} }
``` ```
此接口的参数Client是实现了消息队列client接口。 > 其中SetQueueClient接口的参数Client是实现了消息队列client接口。实现的主要功能如下:
> **实现功能:**
> 1. 设置消息队列Client > 1. 设置消息队列Client
> 2. 初始化本地区块 > 2. 初始化本地区块
> 3. 订阅消息并开始消息循环 > 3. 订阅消息并开始消息循环
> 4. 开启共识过程 > 4. 开启共识过程
<h4 id="3.1.2-通用处理逻辑">通用处理逻辑</h4> ### 3.2 Minner接口
#### 接口定义
```go ```go
func (bc *BaseClient) SetQueueClient(c queue.Client) { type Miner interface {
bc.InitClient(c, func() { //创建创世区块中包含的交易
//call init block CreateGenesisTx() []*types.Transaction
bc.InitBlock() //获取创世区块时间
}) GetGenesisBlockTime() int64
go bc.EventLoop() //获取交易,打包生成新区块
go bc.child.CreateBlock() CreateBlock()
//通过比较父区块和当前区块,检查当前区块的有效性
CheckBlock(parent *types.Block, current *types.BlockDetail) error
//处理自定义事件:处理除BaseClient处理的事件以外的自定义事件
//*注:如果不需要处理自定义事件直接返回false,对关心的事件处理成功后返回true。*
ProcEvent(msg queue.Message) bool
} }
``` ```
在BaseClient基类中,实现了上述4点功能,其中CreateBlock()实现了最简单的共识过程,获取交易,然后打包。 ## 4. 二次开发
为介绍共识模块的二次开发过程。假定有一个节点数不变的区块链网络采用如下共识规则:
<h3 id="3.2-关闭共识模块">3.2 关闭共识模块</h3> 1. 每个节点的配置项中包含NodeId配置,用于配置节点唯一编号,从0开始递增,每次加1。
<h4 id="3.2.1-接口定义">接口定义</h4> 2. 每个节点的配置项中包含NodeCount配置,用于配置该网络包含的总节点数,所有节点该值必须一致。
3. 区块高度与节点数取模后的值与哪个节点编号一致,该节点将作为生成新区块的节点。
> 将该共识规则命名为NumberDecide。
### 4.1 整体介绍
共识模块的二次开发的目录主要集中在/chain33/plugin/consensus目录下。在该目录下创建numberdecide文件夹,在该文件夹下创建numberdecide.go和numberdecide_test.go文件。
### 4.2 结构定义
```go ```go
/// Module be used for module interface type subConfig struct {
type Module interface { //创世地址
Close() Genesis string `json:"genesis"`
//创世区块时间
GenesisBlockTime int64 `json:"genesisBlockTime"`
//节点编号
NodeId int64 `json:"nodeId"`
//节点信息(IP,端口号)
Nodes []string `json:"nodes"`
} }
``` ```
> **实现功能:** 关闭共识模块,进行必要的清理工作。 > 定义共识模块子配置结构体,用于从配置文件中获取配置信息。创世地址和创始区块时间会在Miner接口的CreateGenesisTx()和GetGenesisBlockTime()中用到,可以使用consensus配置项中的配置,也可以在子配置中添加配置使用,推荐在子配置中添加。
<h4 id="3.2.2-通用处理逻辑">通用处理逻辑</h4>
```go ```go
func (bc *BaseClient) Close() { type Client struct {
atomic.StoreInt32(&bc.minerStart, 0) //父类对象指针
bc.client.Close() *drivers.BaseClient
log.Info("consensus base closed") //子配置
subcfg *subConfig
} }
``` ```
关闭挖矿,关闭client消息接收 > 定义实现Module接口的Client结构体。
### 4.3 配置
<h3 id="3.3-创建创世区块交易">3.3 创建创世区块交易</h3> 1. 修改配置文件chain33.toml,修改[consensus]项中的name键为"numberdecide"。
<h4 id="3.3.1-接口定义">接口定义</h4> 2. 新增[consensus.sub.numberdecide]配置项,增加genesis,genesisBlockTime,nodeId,nodes项。
3. 在numberdecide.go中增加init()函数,并在init文件夹下的init.go中增加numberdecide目录。
> [consensus]
> name="numberdecide"
> minerstart=true
> genesisBlockTime=1514533394
> genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
>
> [consensus.sub.numberdecide]
> genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
> genesisBlockTime=1514534444
> nodeId=0
> nodes=["10.0.0.2:20181","10.0.0.3:20180","10.0.0.4:20180","10.0.0.5:20180"]
numberdecide.go
```go ```go
type Miner interface { func init() {
CreateGenesisTx() []*types.Transaction drivers.Reg("numberdecide", New)
drivers.QueryData.Register("numberdecide", &Client{})
} }
```
> **实现功能:** 创建创世区块中包含的交易。
<h4 id="3.3.2-通用处理逻辑">通用处理逻辑</h4> func New(cfg *types.Consensus, sub []byte) queue.Module {
```go c := drivers.NewBaseClient(cfg)
func (client *Client) CreateGenesisTx() (ret []*types.Transaction) { var subcfg subConfig
var tx types.Transaction if sub != nil {
//设置本次交易的执行器名 types.MustDecode(sub, &subcfg)
tx.Execer = []byte("coins") }
//设置本次交易的目的地址
tx.To = client.subcfg.Genesis client := &Client{c, &subcfg}
//gen payload c.SetChild(client)
g := &cty.CoinsAction_Genesis{} return client
g.Genesis = &types.AssetsGenesis{}
//转账数额
g.Genesis.Amount = 1e8 * types.Coin
tx.Payload = types.Encode(&cty.CoinsAction{Value: g, Ty: cty.CoinsActionGenesis})
ret = append(ret, &tx)
return
} }
``` ```
init.go
<h3 id="3.4-获取创世区块时间">3.4 获取创世区块时间</h3>
<h4 id="3.4.1-接口定义">接口定义</h4>
```go ```go
type Miner interface { import (
GetGenesisBlockTime() int64 _ "gitlab.33.cn/chain33/chain33/plugin/consensus/para"
} _ "gitlab.33.cn/chain33/chain33/plugin/consensus/pbft"
_ "gitlab.33.cn/chain33/chain33/plugin/consensus/raft"
_ "gitlab.33.cn/chain33/chain33/plugin/consensus/tendermint"
_ "gitlab.33.cn/chain33/chain33/plugin/consensus/ticket"
//新增numberdecide目录
_ "gitlab.33.cn/chain33/chain33/plugin/consensus/numberdecide"
)
``` ```
> **实现功能:** 获取创世区块时间 ### 4.4 实现Module接口
```go
func (bc *Client) SetQueueClient(c queue.Client) {
bc.InitClient(c, func() {
//call init block
bc.InitBlock()
})
go bc.EventLoop()
//启动共识
go bc.startConsensus()
}
<h4 id="3.4.2-通用处理逻辑">通用处理逻辑</h4> func (client *Client) Close() {
//关闭监听服务,清除相关存储
slog.Info("consensus numberdecide closed")
}
```go func (client *Client) startConsensus() {
func (client *Client) GetGenesisBlockTime() int64 { client.connectNodes()
return client.subcfg.GenesisBlockTime client.CreateBlock()
} }
```
<h3 id="3.5-创建区块">3.5 创建区块</h3> func (client *Client) connectNodes() {
<h4 id="3.5.1-接口定义">接口定义</h4> //创建tcp服务监听其他节点连接
```go go client.listenRoutine()
type Miner interface { for {
CreateBlock() //连接配置文件中的节点
for i:=0; i<len(client.subcfg.Nodes); i++ {
//如果node ip已连接,则跳过
//if ... {
// continue
//} else {
// 主动连接该节点
//}
}
//如果所有节点都已连接,则返回
//if ... {
// return
//}
time.Sleep(time.Second)
}
} }
```
> **实现功能:** 获取交易,打包生成区块
<h4 id="3.5.2-通用处理逻辑">通用处理逻辑</h4> func (client *Client) listenRoutine() {
//开始监听并处理连接,进而获取nodeid及node
}
```
### 4.5 实现Miner接口
由于本例的共识机制与solo有相似之处,所以Miner接口中GetGenesisBlockTime(),CreateGenesisTx(),ProcEvent(),CheckBlock()的实现与solo是一样的在此不做赘述,仅就CreateBlock()稍作修改。
```go ```go
func (client *Client) CreateBlock() { func (client *Client) CreateBlock() {
issleep := true issleep := true
for { for {
//判断是否正在挖矿,是否已完成区块同步
if !client.IsMining() || !client.IsCaughtUp() { if !client.IsMining() || !client.IsCaughtUp() {
time.Sleep(client.sleepTime) time.Sleep(time.Second)
continue continue
} }
if issleep { if issleep {
time.Sleep(client.sleepTime) time.Sleep(time.Second)
} }
//获取前一高度的区块 ...
lastBlock := client.GetCurrentBlock() //判断高度与节点个数的mod值是否与本节点的id相等,相等则打包否则不打包等待广播高度更新
//从Mempool中获取可用交易 if (lastBlock.Height + 1) % int64(len(client.subcfg.Nodes)) == client.subcfg.NodeId {
txs := client.RequestTx(int(types.GetP(lastBlock.Height+1).MaxTxNumber), nil) err := client.WriteBlock(lastBlock.StateHash, &newblock)
if len(txs) == 0 { //判断有没有交易是被删除的,这类交易要从mempool 中删除
issleep = true if err != nil {
continue issleep = true
} continue
issleep = false }
//检查去重 } else {
txs = client.CheckTxDup(txs) //检查高度是否已经更新
var newblock types.Block //client.checkHeightUpdate()
newblock.ParentHash = lastBlock.Hash()
newblock.Height = lastBlock.Height + 1
//将交易加入区块
client.AddTxsToBlock(&newblock, txs)
//solo 挖矿固定难度
newblock.Difficulty = types.GetP(0).PowLimitBits
newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
newblock.BlockTime = types.Now().Unix()
if lastBlock.BlockTime >= newblock.BlockTime {
newblock.BlockTime = lastBlock.BlockTime + 1
}
//写区块:写入本地并广播给其他节点
err := client.WriteBlock(lastBlock.StateHash, &newblock)
//判断有没有交易是被删除的,这类交易要从mempool 中删除
if err != nil {
issleep = true
continue
} }
} }
} }
``` ```
<h3 id="3.6-检查区块">3.6 检查区块</h3>
<h4 id="3.6.1-接口定义">接口定义</h4>
```go
type Miner interface {
CheckBlock(parent *types.Block, current *types.BlockDetail) error
}
```
> **实现功能:** 通过比较父区块和当前区块,检查当前区块的有效性
<h4 id="3.6.2-通用处理逻辑">通用处理逻辑</h4>
```go
func (client *Client) CheckBlock(parent *types.Block, current *types.BlockDetail) error {
return nil
}
```
此接口用于检查用户自定义的一些记录再区块内的逻辑信息,区块内交易信息校验在具体的执行器内实现。
<h3 id="3.7-处理自定义事件块">3.7 处理自定义事件</h3>
<h4 id="3.7.1-接口定义">接口定义</h4>
```go
type Miner interface {
ProcEvent(msg queue.Message) bool
}
```
> **实现功能:** 处理除BaseClient处理的事件以外的自定义事件
<h4 id="3.7.2-通用处理逻辑">通用处理逻辑</h4>
```go
func (client *Client) ProcEvent(msg queue.Message) bool {
return false
}
```
*注:如果不需要处理自定义事件直接返回false,对关心的事件处理成功后返回true。*
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment