├── README.md
├── dpos
├── README.md
└── main.go
├── pbft
├── README.md
├── client.go
├── cmd.go
├── images
│ ├── 关闭两个节点.png
│ ├── 启动.png
│ ├── 启动后.png
│ ├── 掉了一个节点后.png
│ └── 流程图.webp
├── main.go
├── pbft.go
├── rsa.go
└── tcp.go
├── pos
├── README.md
└── main.go
├── pow
├── README.md
└── main.go
└── raft
├── README.md
├── http.go
├── images
├── 开启端口.png
├── 打印消息.png
├── 残缺打印.png
├── 消息同步.png
├── 选举成功.png
├── 重启B.png
└── 领导者节点宕机.png
├── main.go
├── raft.go
└── rpc.go
/README.md:
--------------------------------------------------------------------------------
1 |
2 | >近期学习总结并代码实现的五大区块链共识算法,如果发现bug或者理解方面的错误请不吝指教 :)
3 |
4 |
5 | #### POW(工作量证明)
6 | https://github.com/corgi-kx/blockchain_consensus_algorithm/tree/master/pow
7 | #### POS(权益证明)
8 | https://github.com/corgi-kx/blockchain_consensus_algorithm/tree/master/pos
9 | #### DPOS(股份授权证明)
10 | https://github.com/corgi-kx/blockchain_consensus_algorithm/tree/master/dpos
11 | #### PBFT(实用拜占庭)
12 | https://github.com/corgi-kx/blockchain_consensus_algorithm/tree/master/pbft
13 | #### RAFT
14 | https://github.com/corgi-kx/blockchain_consensus_algorithm/tree/master/raft
15 |
16 |
--------------------------------------------------------------------------------
/dpos/README.md:
--------------------------------------------------------------------------------
1 |
2 | >股份授权证明机制是POS的一个变种,简单来说就是你手里有选票(币就相当于选票)。这时一些正在竞选超级节点的大节点们说把票都投给我把,等我当选了超级节点,我吃肉你喝汤,岂不美哉?然后你就信了,把票投给了竞选节点,这些节点竞选成功成为超级节点后会轮循的获得出块权。旷工费、通胀放出的代币也就都到了他们手里了。比较中心化的一种共识机制,但是TPS很高。
3 |
4 |
5 |
6 | 区块结构:
7 | ```go
8 | type block struct {
9 | //上一个块的hash
10 | prehash string
11 | //本块hash
12 | hash string
13 | //时间戳
14 | timestamp string
15 | //区块内容
16 | data string
17 | //区块高度
18 | height int
19 | //挖出本块的节点地址
20 | address string
21 | }
22 | ```
23 |
24 | ```go
25 | //用于存储区块链
26 | var blockchain []block
27 | //普通节点
28 | type node struct{
29 | //代币数量
30 | votes int
31 | //节点地址
32 | address string
33 | }
34 | //竞选节点
35 | type superNode struct {
36 | node
37 | }
38 | //投票节点池
39 | var voteNodesPool []node
40 | //竞选节点池
41 | var starNodesPool []superNode
42 | //存放可以挖矿的超级节点池
43 | var superStarNodesPool []superNode
44 | ```
45 |
46 | 初始化:\
47 | 生成投票节点池并随机赋予代币数量,同时为竞选节点池生成竞选节点
48 | ```go
49 | //初始化
50 | func init() {
51 | //初始化投票节点
52 | for i:=0;i<=voteNodeNum;i++ {
53 | rInt,err:=rand.Int(rand.Reader,big.NewInt(10000))
54 | if err != nil {
55 | log.Panic(err)
56 | }
57 | voteNodesPool = append(voteNodesPool,node{int(rInt.Int64()),"投票节点"+strconv.Itoa(i)})
58 | }
59 | //初始化超级节点
60 | for i:=0;i<=superNodeNum;i++ {
61 | starNodesPool = append(starNodesPool,superNode{node{0,"超级节点"+strconv.Itoa(i)}})
62 | }
63 | }
64 | ```
65 |
66 |
67 | 模拟普通节点投票(随机的对竞选节点投票)
68 | ```go
69 | //投票
70 | func voting() {
71 | for _, v := range voteNodesPool {
72 | rInt, err := rand.Int(rand.Reader, big.NewInt(superNodeNum+1))
73 | if err != nil {
74 | log.Panic(err)
75 | }
76 | starNodesPool[int(rInt.Int64())].votes += v.votes
77 | }
78 | }
79 | ```
80 |
81 | 对竞选节点根据得票数排序,前几名成为超级节点
82 | ```go
83 | //对挖矿节点进行排序
84 | func sortMineNodes() {
85 | sort.Slice(starNodesPool, func(i, j int) bool {
86 | return starNodesPool[i].votes > starNodesPool[j].votes
87 | })
88 | superStarNodesPool = starNodesPool[:mineSuperNodeNum]
89 | }
90 | ```
91 |
92 | 主函数
93 | ```go
94 | func main() {
95 | fmt.Println("初始化", voteNodeNum, "个投票节点...")
96 | fmt.Println(voteNodesPool)
97 | fmt.Println("当前存在的", superNodeNum, "个竞选节点")
98 | fmt.Println(starNodesPool)
99 | fmt.Println("投票节点们开始进行投票...")
100 | voting()
101 | fmt.Println("结束投票,查看竞选节点们获得票数...")
102 | fmt.Println(starNodesPool)
103 | fmt.Println("对竞选节点按获得票数排序,前", mineSuperNodeNum, "名,当选超级节点")
104 | sortMineNodes()
105 | fmt.Println(superStarNodesPool)
106 | fmt.Println("开始挖矿...")
107 | genesisBlock := block{"0000000000000000000000000000000000000000000000000000000000000000", "", time.Now().Format("2006-01-02 15:04:05"), "我是创世区块", 1, "000000000"}
108 | genesisBlock.getHash()
109 | blockchain = append(blockchain, genesisBlock)
110 | fmt.Println(blockchain[0])
111 | i, j := 0, 0
112 | for {
113 | time.Sleep(time.Second)
114 | newBlock := generateNewBlock(blockchain[i], "我是区块内容", superStarNodesPool[j].address)
115 | blockchain = append(blockchain, newBlock)
116 | fmt.Println(blockchain[i+1])
117 | i++
118 | j++
119 | j = j % len(superStarNodesPool)
120 | }
121 | }
122 | ```
123 |
124 | 运行结果:
125 | 
126 |
--------------------------------------------------------------------------------
/dpos/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/rand"
5 | "crypto/sha256"
6 | "encoding/hex"
7 | "fmt"
8 | "log"
9 | "math/big"
10 | "sort"
11 | "strconv"
12 | "time"
13 | )
14 |
15 | const (
16 | voteNodeNum = 100
17 | superNodeNum = 10
18 | mineSuperNodeNum = 3
19 | )
20 |
21 | type block struct {
22 | //上一个块的hash
23 | prehash string
24 | //本块hash
25 | hash string
26 | //时间戳
27 | timestamp string
28 | //区块内容
29 | data string
30 | //区块高度
31 | height int
32 | //挖出本块的节点地址
33 | address string
34 | }
35 |
36 | //用于存储区块链
37 | var blockchain []block
38 |
39 | //普通节点
40 | type node struct {
41 | //代币数量
42 | votes int
43 | //节点地址
44 | address string
45 | }
46 |
47 | //竞选节点
48 | type superNode struct {
49 | node
50 | }
51 |
52 | //投票节点池
53 | var voteNodesPool []node
54 |
55 | //竞选节点池
56 | var starNodesPool []superNode
57 |
58 | //存放可以挖矿的超级节点池
59 | var superStarNodesPool []superNode
60 |
61 | //生成新的区块
62 | func generateNewBlock(oldBlock block, data string, address string) block {
63 | newBlock := block{}
64 | newBlock.prehash = oldBlock.hash
65 | newBlock.data = data
66 | newBlock.timestamp = time.Now().Format("2006-01-02 15:04:05")
67 | newBlock.height = oldBlock.height + 1
68 | newBlock.address = address
69 | newBlock.getHash()
70 | return newBlock
71 | }
72 |
73 | //对自身进行散列
74 | func (b *block) getHash() {
75 | sumString := b.prehash + b.timestamp + b.data + b.address + strconv.Itoa(b.height)
76 | hash := sha256.Sum256([]byte(sumString))
77 | b.hash = hex.EncodeToString(hash[:])
78 | }
79 |
80 | //投票
81 | func voting() {
82 | for _, v := range voteNodesPool {
83 | rInt, err := rand.Int(rand.Reader, big.NewInt(superNodeNum+1))
84 | if err != nil {
85 | log.Panic(err)
86 | }
87 | starNodesPool[int(rInt.Int64())].votes += v.votes
88 | }
89 | }
90 |
91 | //对挖矿节点进行排序
92 | func sortMineNodes() {
93 | sort.Slice(starNodesPool, func(i, j int) bool {
94 | return starNodesPool[i].votes > starNodesPool[j].votes
95 | })
96 | superStarNodesPool = starNodesPool[:mineSuperNodeNum]
97 | }
98 |
99 | //初始化
100 | func init() {
101 | //初始化投票节点
102 | for i := 0; i <= voteNodeNum; i++ {
103 | rInt, err := rand.Int(rand.Reader, big.NewInt(10000))
104 | if err != nil {
105 | log.Panic(err)
106 | }
107 | voteNodesPool = append(voteNodesPool, node{int(rInt.Int64()), "投票节点" + strconv.Itoa(i)})
108 | }
109 | //初始化竞选节点
110 | for i := 0; i <= superNodeNum; i++ {
111 | starNodesPool = append(starNodesPool, superNode{node{0, "超级节点" + strconv.Itoa(i)}})
112 | }
113 | }
114 |
115 | func main() {
116 | fmt.Println("初始化", voteNodeNum, "个投票节点...")
117 | fmt.Println(voteNodesPool)
118 | fmt.Println("当前存在的", superNodeNum, "个竞选节点")
119 | fmt.Println(starNodesPool)
120 | fmt.Println("投票节点们开始进行投票...")
121 | voting()
122 | fmt.Println("结束投票,查看竞选节点们获得票数...")
123 | fmt.Println(starNodesPool)
124 | fmt.Println("对竞选节点按获得票数排序,前", mineSuperNodeNum, "名,当选超级节点")
125 | sortMineNodes()
126 | fmt.Println(superStarNodesPool)
127 | fmt.Println("开始挖矿...")
128 | genesisBlock := block{"0000000000000000000000000000000000000000000000000000000000000000", "", time.Now().Format("2006-01-02 15:04:05"), "我是创世区块", 1, "000000000"}
129 | genesisBlock.getHash()
130 | blockchain = append(blockchain, genesisBlock)
131 | fmt.Println(blockchain[0])
132 | i, j := 0, 0
133 | for {
134 | time.Sleep(time.Second)
135 | newBlock := generateNewBlock(blockchain[i], "我是区块内容", superStarNodesPool[j].address)
136 | blockchain = append(blockchain, newBlock)
137 | fmt.Println(blockchain[i+1])
138 | i++
139 | j++
140 | j = j % len(superStarNodesPool) //超级节点轮循获得出块权
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/pbft/README.md:
--------------------------------------------------------------------------------
1 | >参考资料:
2 | > - https://www.jianshu.com/p/fb5edf031afd
3 | > - https://www.cnblogs.com/gexin/p/10242161.html
4 |
5 |
6 |
7 |
8 | 本demo为pbft共识算法的代码实现,如果想了解pbft的详细信息请自行浏览参考资料\
9 | 本demo展示了pbft的部分功能(没有写主节点轮循机制),写的并不严谨,仅作为对pbft的了解用途
10 |
11 |
12 |
13 |
14 | 
15 | ## 实现功能:
16 | >pbft公式: n>=3f + 1 其中n为全网总节点数量,f为最多允许的作恶、故障节点
17 |
18 |
19 | 数据从客户端输入,到接收到节点们的回复共分为5步
20 |
21 | 1. 客户端向主节点发送请求信息
22 | 2. 主节点N0接收到客户端请求后将请求数据里的主要信息提出,并向其余节点进行preprepare发送
23 | 3. 从节点们接收到来自主节点的preprepare,首先利用主节点的公钥进行签名认证,其次将消息进行散列(消息摘要,以便缩小信息在网络中的传输大小)后,向其他节点广播prepare
24 | 4. 节点接收到2f个prepare信息(包含自己),并全部签名验证通过,则可以进行到commit步骤,向全网其他节点广播commit
25 | 5. 节点接收到2f+1个commit信息(包含自己),并全部签名验证通过,则可以把消息存入到本地,并向客户端返回reply消息
26 |
27 |
28 |
29 |
30 | ## 运行步骤:
31 |
32 |
33 | ##### 1.下载/编译
34 | ```shell
35 | git clone https://github.com/corgi-kx/blockchain_consensus_algorithm.git
36 | ```
37 | ```shell
38 | cd blockchain_consensus_algorithm/pbft
39 | ```
40 | ```go
41 | go build -o pbft.exe
42 | ```
43 |
44 | ##### 2.开启五个端口(一个客户端,四个节点)
45 | 客户端执行pbft.exe client
46 | 其他四个节点依次执行 pbft.exe N0 pbft.exe N1 pbft.exe N2 pbft.exe N3
47 | 
48 | ##### 3.输入一段信息,看看节点之间的同步过程
49 | 
50 | ##### 4.关闭一个节点(代表作恶、故障节点),再次输入信息,看看是否还会接收到reply
51 | 可以看到,客户端依然会接收到reply,因为根据公式 n >= 3f+1 ,就算宕机一个节点,系统依然能顺利运行
52 | 
53 | ##### 4.关闭两个节点(代表作恶、故障节点),再次输入信息,看看是否还会接收到reply
54 | 可以看到,关闭两个节点后,故障节点已经超出了pbft的允许数量,消息进行到Prepare阶段由于接收不到满足数量的信息,固系统不再进行commit确认,客户端也接收不到reply
55 | 
56 |
57 | >** 建了个QQ群:722124200 有问题可以加群互相讨论 :)** \
58 | >** 邮箱:mikesen1994@gmail.com vx:965952482**
59 |
--------------------------------------------------------------------------------
/pbft/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "crypto/rand"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 | "math/big"
10 | "os"
11 | "strings"
12 | "time"
13 | )
14 |
15 | func clientSendMessageAndListen() {
16 | //开启客户端的本地监听(主要用来接收节点的reply信息)
17 | go clientTcpListen()
18 | fmt.Printf("客户端开启监听,地址:%s\n", clientAddr)
19 |
20 | fmt.Println(" ---------------------------------------------------------------------------------")
21 | fmt.Println("| 已进入PBFT测试Demo客户端,请启动全部节点后再发送消息! :) |")
22 | fmt.Println(" ---------------------------------------------------------------------------------")
23 | fmt.Println("请在下方输入要存入节点的信息:")
24 | //首先通过命令行获取用户输入
25 | stdReader := bufio.NewReader(os.Stdin)
26 | for {
27 | data, err := stdReader.ReadString('\n')
28 | if err != nil {
29 | fmt.Println("Error reading from stdin")
30 | panic(err)
31 | }
32 | r := new(Request)
33 | r.Timestamp = time.Now().UnixNano()
34 | r.ClientAddr = clientAddr
35 | r.Message.ID = getRandom()
36 | //消息内容就是用户的输入
37 | r.Message.Content = strings.TrimSpace(data)
38 | br, err := json.Marshal(r)
39 | if err != nil {
40 | log.Panic(err)
41 | }
42 | fmt.Println(string(br))
43 | content := jointMessage(cRequest, br)
44 | //默认N0为主节点,直接把请求信息发送至N0
45 | tcpDial(content, nodeTable["N0"])
46 | }
47 | }
48 |
49 | //返回一个十位数的随机数,作为msgid
50 | func getRandom() int {
51 | x := big.NewInt(10000000000)
52 | for {
53 | result, err := rand.Int(rand.Reader, x)
54 | if err != nil {
55 | log.Panic(err)
56 | }
57 | if result.Int64() > 1000000000 {
58 | return int(result.Int64())
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/pbft/cmd.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/sha256"
5 | "encoding/hex"
6 | "encoding/json"
7 | "log"
8 | )
9 |
10 | //
11 | type Request struct {
12 | Message
13 | Timestamp int64
14 | //相当于clientID
15 | ClientAddr string
16 | }
17 |
18 | //<,m>
19 | type PrePrepare struct {
20 | RequestMessage Request
21 | Digest string
22 | SequenceID int
23 | Sign []byte
24 | }
25 |
26 | //
27 | type Prepare struct {
28 | Digest string
29 | SequenceID int
30 | NodeID string
31 | Sign []byte
32 | }
33 |
34 | //
35 | type Commit struct {
36 | Digest string
37 | SequenceID int
38 | NodeID string
39 | Sign []byte
40 | }
41 |
42 | //
43 | type Reply struct {
44 | MessageID int
45 | NodeID string
46 | Result bool
47 | }
48 |
49 | type Message struct {
50 | Content string
51 | ID int
52 | }
53 |
54 | const prefixCMDLength = 12
55 |
56 | type command string
57 |
58 | const (
59 | cRequest command = "request"
60 | cPrePrepare command = "preprepare"
61 | cPrepare command = "prepare"
62 | cCommit command = "commit"
63 | )
64 |
65 | //默认前十二位为命令名称
66 | func jointMessage(cmd command, content []byte) []byte {
67 | b := make([]byte, prefixCMDLength)
68 | for i, v := range []byte(cmd) {
69 | b[i] = v
70 | }
71 | joint := make([]byte, 0)
72 | joint = append(b, content...)
73 | return joint
74 | }
75 |
76 | //默认前十二位为命令名称
77 | func splitMessage(message []byte) (cmd string, content []byte) {
78 | cmdBytes := message[:prefixCMDLength]
79 | newCMDBytes := make([]byte, 0)
80 | for _, v := range cmdBytes {
81 | if v != byte(0) {
82 | newCMDBytes = append(newCMDBytes, v)
83 | }
84 | }
85 | cmd = string(newCMDBytes)
86 | content = message[prefixCMDLength:]
87 | return
88 | }
89 |
90 | //对消息详情进行摘要
91 | func getDigest(request Request) string {
92 | b, err := json.Marshal(request)
93 | if err != nil {
94 | log.Panic(err)
95 | }
96 | hash := sha256.Sum256(b)
97 | //进行十六进制字符串编码
98 | return hex.EncodeToString(hash[:])
99 | }
100 |
--------------------------------------------------------------------------------
/pbft/images/关闭两个节点.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/pbft/images/关闭两个节点.png
--------------------------------------------------------------------------------
/pbft/images/启动.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/pbft/images/启动.png
--------------------------------------------------------------------------------
/pbft/images/启动后.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/pbft/images/启动后.png
--------------------------------------------------------------------------------
/pbft/images/掉了一个节点后.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/pbft/images/掉了一个节点后.png
--------------------------------------------------------------------------------
/pbft/images/流程图.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/pbft/images/流程图.webp
--------------------------------------------------------------------------------
/pbft/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 | )
7 |
8 | const nodeCount = 4
9 |
10 | //客户端的监听地址
11 | var clientAddr = "127.0.0.1:8888"
12 |
13 | //节点池,主要用来存储监听地址
14 | var nodeTable map[string]string
15 |
16 | func main() {
17 | //为四个节点生成公私钥
18 | genRsaKeys()
19 | nodeTable = map[string]string{
20 | "N0": "127.0.0.1:8000",
21 | "N1": "127.0.0.1:8001",
22 | "N2": "127.0.0.1:8002",
23 | "N3": "127.0.0.1:8003",
24 | }
25 | if len(os.Args) != 2 {
26 | log.Panic("输入的参数有误!")
27 | }
28 | nodeID := os.Args[1]
29 | if nodeID == "client" {
30 | clientSendMessageAndListen() //启动客户端程序
31 | } else if addr, ok := nodeTable[nodeID]; ok {
32 | p := NewPBFT(nodeID, addr)
33 | go p.tcpListen() //启动节点
34 | } else {
35 | log.Fatal("无此节点编号!")
36 | }
37 | select {}
38 | }
39 |
--------------------------------------------------------------------------------
/pbft/pbft.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/hex"
5 | "encoding/json"
6 | "fmt"
7 | "io/ioutil"
8 | "log"
9 | "strconv"
10 | "sync"
11 | )
12 |
13 | //本地消息池(模拟持久化层),只有确认提交成功后才会存入此池
14 | var localMessagePool = []Message{}
15 |
16 | type node struct {
17 | //节点ID
18 | nodeID string
19 | //节点监听地址
20 | addr string
21 | //RSA私钥
22 | rsaPrivKey []byte
23 | //RSA公钥
24 | rsaPubKey []byte
25 | }
26 |
27 | type pbft struct {
28 | //节点信息
29 | node node
30 | //每笔请求自增序号
31 | sequenceID int
32 | //锁
33 | lock sync.Mutex
34 | //临时消息池,消息摘要对应消息本体
35 | messagePool map[string]Request
36 | //存放收到的prepare数量(至少需要收到并确认2f个),根据摘要来对应
37 | prePareConfirmCount map[string]map[string]bool
38 | //存放收到的commit数量(至少需要收到并确认2f+1个),根据摘要来对应
39 | commitConfirmCount map[string]map[string]bool
40 | //该笔消息是否已进行Commit广播
41 | isCommitBordcast map[string]bool
42 | //该笔消息是否已对客户端进行Reply
43 | isReply map[string]bool
44 | }
45 |
46 | func NewPBFT(nodeID, addr string) *pbft {
47 | p := new(pbft)
48 | p.node.nodeID = nodeID
49 | p.node.addr = addr
50 | p.node.rsaPrivKey = p.getPivKey(nodeID) //从生成的私钥文件处读取
51 | p.node.rsaPubKey = p.getPubKey(nodeID) //从生成的私钥文件处读取
52 | p.sequenceID = 0
53 | p.messagePool = make(map[string]Request)
54 | p.prePareConfirmCount = make(map[string]map[string]bool)
55 | p.commitConfirmCount = make(map[string]map[string]bool)
56 | p.isCommitBordcast = make(map[string]bool)
57 | p.isReply = make(map[string]bool)
58 | return p
59 | }
60 |
61 | func (p *pbft) handleRequest(data []byte) {
62 | //切割消息,根据消息命令调用不同的功能
63 | cmd, content := splitMessage(data)
64 | switch command(cmd) {
65 | case cRequest:
66 | p.handleClientRequest(content)
67 | case cPrePrepare:
68 | p.handlePrePrepare(content)
69 | case cPrepare:
70 | p.handlePrepare(content)
71 | case cCommit:
72 | p.handleCommit(content)
73 | }
74 | }
75 |
76 | //处理客户端发来的请求
77 | func (p *pbft) handleClientRequest(content []byte) {
78 | fmt.Println("主节点已接收到客户端发来的request ...")
79 | //使用json解析出Request结构体
80 | r := new(Request)
81 | err := json.Unmarshal(content, r)
82 | if err != nil {
83 | log.Panic(err)
84 | }
85 | //添加信息序号
86 | p.sequenceIDAdd()
87 | //获取消息摘要
88 | digest := getDigest(*r)
89 | fmt.Println("已将request存入临时消息池")
90 | //存入临时消息池
91 | p.messagePool[digest] = *r
92 | //主节点对消息摘要进行签名
93 | digestByte, _ := hex.DecodeString(digest)
94 | signInfo := p.RsaSignWithSha256(digestByte, p.node.rsaPrivKey)
95 | //拼接成PrePrepare,准备发往follower节点
96 | pp := PrePrepare{*r, digest, p.sequenceID, signInfo}
97 | b, err := json.Marshal(pp)
98 | if err != nil {
99 | log.Panic(err)
100 | }
101 | fmt.Println("正在向其他节点进行进行PrePrepare广播 ...")
102 | //进行PrePrepare广播
103 | p.broadcast(cPrePrepare, b)
104 | fmt.Println("PrePrepare广播完成")
105 | }
106 |
107 | //处理预准备消息
108 | func (p *pbft) handlePrePrepare(content []byte) {
109 | fmt.Println("本节点已接收到主节点发来的PrePrepare ...")
110 | // //使用json解析出PrePrepare结构体
111 | pp := new(PrePrepare)
112 | err := json.Unmarshal(content, pp)
113 | if err != nil {
114 | log.Panic(err)
115 | }
116 | //获取主节点的公钥,用于数字签名验证
117 | primaryNodePubKey := p.getPubKey("N0")
118 | digestByte, _ := hex.DecodeString(pp.Digest)
119 | if digest := getDigest(pp.RequestMessage); digest != pp.Digest {
120 | fmt.Println("信息摘要对不上,拒绝进行prepare广播")
121 | } else if p.sequenceID+1 != pp.SequenceID {
122 | fmt.Println("消息序号对不上,拒绝进行prepare广播")
123 | } else if !p.RsaVerySignWithSha256(digestByte, pp.Sign, primaryNodePubKey) {
124 | fmt.Println("主节点签名验证失败!,拒绝进行prepare广播")
125 | } else {
126 | //序号赋值
127 | p.sequenceID = pp.SequenceID
128 | //将信息存入临时消息池
129 | fmt.Println("已将消息存入临时节点池")
130 | p.messagePool[pp.Digest] = pp.RequestMessage
131 | //节点使用私钥对其签名
132 | sign := p.RsaSignWithSha256(digestByte, p.node.rsaPrivKey)
133 | //拼接成Prepare
134 | pre := Prepare{pp.Digest, pp.SequenceID, p.node.nodeID, sign}
135 | bPre, err := json.Marshal(pre)
136 | if err != nil {
137 | log.Panic(err)
138 | }
139 | //进行准备阶段的广播
140 | fmt.Println("正在进行Prepare广播 ...")
141 | p.broadcast(cPrepare, bPre)
142 | fmt.Println("Prepare广播完成")
143 | }
144 | }
145 |
146 | //处理准备消息
147 | func (p *pbft) handlePrepare(content []byte) {
148 | //使用json解析出Prepare结构体
149 | pre := new(Prepare)
150 | err := json.Unmarshal(content, pre)
151 | if err != nil {
152 | log.Panic(err)
153 | }
154 | fmt.Printf("本节点已接收到%s节点发来的Prepare ... \n", pre.NodeID)
155 | //获取消息源节点的公钥,用于数字签名验证
156 | MessageNodePubKey := p.getPubKey(pre.NodeID)
157 | digestByte, _ := hex.DecodeString(pre.Digest)
158 | if _, ok := p.messagePool[pre.Digest]; !ok {
159 | fmt.Println("当前临时消息池无此摘要,拒绝执行commit广播")
160 | } else if p.sequenceID != pre.SequenceID {
161 | fmt.Println("消息序号对不上,拒绝执行commit广播")
162 | } else if !p.RsaVerySignWithSha256(digestByte, pre.Sign, MessageNodePubKey) {
163 | fmt.Println("节点签名验证失败!,拒绝执行commit广播")
164 | } else {
165 | p.setPrePareConfirmMap(pre.Digest, pre.NodeID, true)
166 | count := 0
167 | for range p.prePareConfirmCount[pre.Digest] {
168 | count++
169 | }
170 | //因为主节点不会发送Prepare,所以不包含自己
171 | specifiedCount := 0
172 | if p.node.nodeID == "N0" {
173 | specifiedCount = nodeCount / 3 * 2
174 | } else {
175 | specifiedCount = (nodeCount / 3 * 2) - 1
176 | }
177 | //如果节点至少收到了2f个prepare的消息(包括自己),并且没有进行过commit广播,则进行commit广播
178 | p.lock.Lock()
179 | //获取消息源节点的公钥,用于数字签名验证
180 | if count >= specifiedCount && !p.isCommitBordcast[pre.Digest] {
181 | fmt.Println("本节点已收到至少2f个节点(包括本地节点)发来的Prepare信息 ...")
182 | //节点使用私钥对其签名
183 | sign := p.RsaSignWithSha256(digestByte, p.node.rsaPrivKey)
184 | c := Commit{pre.Digest, pre.SequenceID, p.node.nodeID, sign}
185 | bc, err := json.Marshal(c)
186 | if err != nil {
187 | log.Panic(err)
188 | }
189 | //进行提交信息的广播
190 | fmt.Println("正在进行commit广播")
191 | p.broadcast(cCommit, bc)
192 | p.isCommitBordcast[pre.Digest] = true
193 | fmt.Println("commit广播完成")
194 | }
195 | p.lock.Unlock()
196 | }
197 | }
198 |
199 | //处理提交确认消息
200 | func (p *pbft) handleCommit(content []byte) {
201 | //使用json解析出Commit结构体
202 | c := new(Commit)
203 | err := json.Unmarshal(content, c)
204 | if err != nil {
205 | log.Panic(err)
206 | }
207 | fmt.Printf("本节点已接收到%s节点发来的Commit ... \n", c.NodeID)
208 | //获取消息源节点的公钥,用于数字签名验证
209 | MessageNodePubKey := p.getPubKey(c.NodeID)
210 | digestByte, _ := hex.DecodeString(c.Digest)
211 | if _, ok := p.prePareConfirmCount[c.Digest]; !ok {
212 | fmt.Println("当前prepare池无此摘要,拒绝将信息持久化到本地消息池")
213 | } else if p.sequenceID != c.SequenceID {
214 | fmt.Println("消息序号对不上,拒绝将信息持久化到本地消息池")
215 | } else if !p.RsaVerySignWithSha256(digestByte, c.Sign, MessageNodePubKey) {
216 | fmt.Println("节点签名验证失败!,拒绝将信息持久化到本地消息池")
217 | } else {
218 | p.setCommitConfirmMap(c.Digest, c.NodeID, true)
219 | count := 0
220 | for range p.commitConfirmCount[c.Digest] {
221 | count++
222 | }
223 | //如果节点至少收到了2f+1个commit消息(包括自己),并且节点没有回复过,并且已进行过commit广播,则提交信息至本地消息池,并reply成功标志至客户端!
224 | p.lock.Lock()
225 | if count >= nodeCount/3*2 && !p.isReply[c.Digest] && p.isCommitBordcast[c.Digest] {
226 | fmt.Println("本节点已收到至少2f + 1 个节点(包括本地节点)发来的Commit信息 ...")
227 | //将消息信息,提交到本地消息池中!
228 | localMessagePool = append(localMessagePool, p.messagePool[c.Digest].Message)
229 | info := p.node.nodeID + "节点已将msgid:" + strconv.Itoa(p.messagePool[c.Digest].ID) + "存入本地消息池中,消息内容为:" + p.messagePool[c.Digest].Content
230 | fmt.Println(info)
231 | fmt.Println("正在reply客户端 ...")
232 | tcpDial([]byte(info), p.messagePool[c.Digest].ClientAddr)
233 | p.isReply[c.Digest] = true
234 | fmt.Println("reply完毕")
235 | }
236 | p.lock.Unlock()
237 | }
238 | }
239 |
240 | //序号累加
241 | func (p *pbft) sequenceIDAdd() {
242 | p.lock.Lock()
243 | p.sequenceID++
244 | p.lock.Unlock()
245 | }
246 |
247 | //向除自己外的其他节点进行广播
248 | func (p *pbft) broadcast(cmd command, content []byte) {
249 | for i := range nodeTable {
250 | if i == p.node.nodeID {
251 | continue
252 | }
253 | message := jointMessage(cmd, content)
254 | go tcpDial(message, nodeTable[i])
255 | }
256 | }
257 |
258 | //为多重映射开辟赋值
259 | func (p *pbft) setPrePareConfirmMap(val, val2 string, b bool) {
260 | if _, ok := p.prePareConfirmCount[val]; !ok {
261 | p.prePareConfirmCount[val] = make(map[string]bool)
262 | }
263 | p.prePareConfirmCount[val][val2] = b
264 | }
265 |
266 | //为多重映射开辟赋值
267 | func (p *pbft) setCommitConfirmMap(val, val2 string, b bool) {
268 | if _, ok := p.commitConfirmCount[val]; !ok {
269 | p.commitConfirmCount[val] = make(map[string]bool)
270 | }
271 | p.commitConfirmCount[val][val2] = b
272 | }
273 |
274 | //传入节点编号, 获取对应的公钥
275 | func (p *pbft) getPubKey(nodeID string) []byte {
276 | key, err := ioutil.ReadFile("Keys/" + nodeID + "/" + nodeID + "_RSA_PUB")
277 | if err != nil {
278 | log.Panic(err)
279 | }
280 | return key
281 | }
282 |
283 | //传入节点编号, 获取对应的私钥
284 | func (p *pbft) getPivKey(nodeID string) []byte {
285 | key, err := ioutil.ReadFile("Keys/" + nodeID + "/" + nodeID + "_RSA_PIV")
286 | if err != nil {
287 | log.Panic(err)
288 | }
289 | return key
290 | }
291 |
--------------------------------------------------------------------------------
/pbft/rsa.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto"
5 | "crypto/rand"
6 | "crypto/rsa"
7 | "crypto/sha256"
8 | "crypto/x509"
9 | "encoding/pem"
10 | "errors"
11 | "fmt"
12 | "log"
13 | "os"
14 | "strconv"
15 | )
16 |
17 | //如果当前目录下不存在目录Keys,则创建目录,并为各个节点生成rsa公私钥
18 | func genRsaKeys() {
19 | if !isExist("./Keys") {
20 | fmt.Println("检测到还未生成公私钥目录,正在生成公私钥 ...")
21 | err := os.Mkdir("Keys", 0644)
22 | if err != nil {
23 | log.Panic()
24 | }
25 | for i := 0; i <= 4; i++ {
26 | if !isExist("./Keys/N" + strconv.Itoa(i)) {
27 | err := os.Mkdir("./Keys/N"+strconv.Itoa(i), 0644)
28 | if err != nil {
29 | log.Panic()
30 | }
31 | }
32 | priv, pub := getKeyPair()
33 | privFileName := "Keys/N" + strconv.Itoa(i) + "/N" + strconv.Itoa(i) + "_RSA_PIV"
34 | file, err := os.OpenFile(privFileName, os.O_RDWR|os.O_CREATE, 0644)
35 | if err != nil {
36 | log.Panic(err)
37 | }
38 | defer file.Close()
39 | file.Write(priv)
40 |
41 | pubFileName := "Keys/N" + strconv.Itoa(i) + "/N" + strconv.Itoa(i) + "_RSA_PUB"
42 | file2, err := os.OpenFile(pubFileName, os.O_RDWR|os.O_CREATE, 0644)
43 | if err != nil {
44 | log.Panic(err)
45 | }
46 | defer file2.Close()
47 | file2.Write(pub)
48 | }
49 | fmt.Println("已为节点们生成RSA公私钥")
50 | }
51 | }
52 |
53 | //生成rsa公私钥
54 | func getKeyPair() (prvkey, pubkey []byte) {
55 | // 生成私钥文件
56 | privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
57 | if err != nil {
58 | panic(err)
59 | }
60 | derStream := x509.MarshalPKCS1PrivateKey(privateKey)
61 | block := &pem.Block{
62 | Type: "RSA PRIVATE KEY",
63 | Bytes: derStream,
64 | }
65 | prvkey = pem.EncodeToMemory(block)
66 | publicKey := &privateKey.PublicKey
67 | derPkix, err := x509.MarshalPKIXPublicKey(publicKey)
68 | if err != nil {
69 | panic(err)
70 | }
71 | block = &pem.Block{
72 | Type: "PUBLIC KEY",
73 | Bytes: derPkix,
74 | }
75 | pubkey = pem.EncodeToMemory(block)
76 | return
77 | }
78 |
79 | //判断文件或文件夹是否存在
80 | func isExist(path string) bool {
81 | _, err := os.Stat(path)
82 | if err != nil {
83 | if os.IsExist(err) {
84 | return true
85 | }
86 | if os.IsNotExist(err) {
87 | return false
88 | }
89 | fmt.Println(err)
90 | return false
91 | }
92 | return true
93 | }
94 |
95 | //数字签名
96 | func (p *pbft) RsaSignWithSha256(data []byte, keyBytes []byte) []byte {
97 | h := sha256.New()
98 | h.Write(data)
99 | hashed := h.Sum(nil)
100 | block, _ := pem.Decode(keyBytes)
101 | if block == nil {
102 | panic(errors.New("private key error"))
103 | }
104 | privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
105 | if err != nil {
106 | fmt.Println("ParsePKCS8PrivateKey err", err)
107 | panic(err)
108 | }
109 |
110 | signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
111 | if err != nil {
112 | fmt.Printf("Error from signing: %s\n", err)
113 | panic(err)
114 | }
115 |
116 | return signature
117 | }
118 |
119 | //签名验证
120 | func (p *pbft) RsaVerySignWithSha256(data, signData, keyBytes []byte) bool {
121 | block, _ := pem.Decode(keyBytes)
122 | if block == nil {
123 | panic(errors.New("public key error"))
124 | }
125 | pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
126 | if err != nil {
127 | panic(err)
128 | }
129 |
130 | hashed := sha256.Sum256(data)
131 | err = rsa.VerifyPKCS1v15(pubKey.(*rsa.PublicKey), crypto.SHA256, hashed[:], signData)
132 | if err != nil {
133 | panic(err)
134 | }
135 | return true
136 | }
137 |
--------------------------------------------------------------------------------
/pbft/tcp.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "log"
7 | "net"
8 | )
9 |
10 | //客户端使用的tcp监听
11 | func clientTcpListen() {
12 | listen, err := net.Listen("tcp", clientAddr)
13 | if err != nil {
14 | log.Panic(err)
15 | }
16 | defer listen.Close()
17 |
18 | for {
19 | conn, err := listen.Accept()
20 | if err != nil {
21 | log.Panic(err)
22 | }
23 | b, err := ioutil.ReadAll(conn)
24 | if err != nil {
25 | log.Panic(err)
26 | }
27 | fmt.Println(string(b))
28 | }
29 |
30 | }
31 |
32 | //节点使用的tcp监听
33 | func (p *pbft) tcpListen() {
34 | listen, err := net.Listen("tcp", p.node.addr)
35 | if err != nil {
36 | log.Panic(err)
37 | }
38 | fmt.Printf("节点开启监听,地址:%s\n", p.node.addr)
39 | defer listen.Close()
40 |
41 | for {
42 | conn, err := listen.Accept()
43 | if err != nil {
44 | log.Panic(err)
45 | }
46 | b, err := ioutil.ReadAll(conn)
47 | if err != nil {
48 | log.Panic(err)
49 | }
50 | p.handleRequest(b)
51 | }
52 |
53 | }
54 |
55 | //使用tcp发送消息
56 | func tcpDial(context []byte, addr string) {
57 | conn, err := net.Dial("tcp", addr)
58 | if err != nil {
59 | log.Println("connect error", err)
60 | return
61 | }
62 |
63 | _, err = conn.Write(context)
64 | if err != nil {
65 | log.Fatal(err)
66 | }
67 | conn.Close()
68 | }
69 |
--------------------------------------------------------------------------------
/pos/README.md:
--------------------------------------------------------------------------------
1 |
2 | >权益证明机制最开始是由点点币提出并应用(出块概率=代币数量 * 币龄) 简单来说谁的币多,谁就有更大的出块概率。但是深挖下去,这个出块概率谁来计算?碰到无成本利益关系问题怎么办?这个共识算法初看很简单,实际有很多问题需要解决,且看以太坊什么时候能完全转换到POS机制吧
3 |
4 |
5 |
6 | 区块结构
7 | ```go
8 | type block struct {
9 | //上一个块的hash
10 | prehash string
11 | //本块hash
12 | hash string
13 | //时间戳
14 | timestamp string
15 | //区块内容
16 | data string
17 | //区块高度
18 | height int
19 | //挖出本块的地址
20 | address string
21 | }
22 | ```
23 | 声明两个节点池\
24 | mineNodesPool 用来存放指定的挖矿节点\
25 | probabilityNodesPool 用于存入挖矿节点的代币数量*币龄获得的概率
26 | ```go
27 | //用于存储区块链
28 | var blockchain []block
29 | //代表挖矿节点
30 | type node struct{
31 | //代币数量
32 | tokens int
33 | //质押时间
34 | days int
35 | //节点地址
36 | address string
37 | }
38 | //挖矿节点
39 | var mineNodesPool []node
40 | //概率节点池
41 | var probabilityNodesPool []node
42 | ```
43 | 初始化节点池:
44 | ```go
45 | func init () {
46 | //手动添加两个节点
47 | mineNodesPool = append(mineNodesPool,node{1000,1,"AAAAAAAAAA"})
48 | mineNodesPool = append(mineNodesPool,node{100,3,"BBBBBBBBBB"})
49 | //初始化随机节点池(挖矿概率与代币数量和币龄有关)
50 | for _,v:=range mineNodesPool{
51 | for i:=0;i<=v.tokens * v.days; i ++ {
52 | randNodesPool = append(randNodesPool,v)
53 | }
54 | }
55 | }
56 | ```
57 | 每次挖矿都会从概率节点池中随机选出获得出块权的节点地址
58 | ```go
59 | //随机得出挖矿地址(挖矿概率跟代币数量与币龄有关)
60 | func getMineNodeAddress() string{
61 | bInt:=big.NewInt(int64(len(randNodesPool)))
62 | //得出一个随机数,最大不超过随机节点池的大小
63 | rInt,err:=rand.Int(rand.Reader,bInt)
64 | if err != nil {
65 | log.Panic(err)
66 | }
67 | return randNodesPool[int(rInt.Int64())].address
68 | }
69 | ```
70 |
71 |
72 | ```go
73 | func main() {
74 | //创建创世区块
75 | genesisBlock := block{"0000000000000000000000000000000000000000000000000000000000000000","",time.Now().Format("2006-01-02 15:04:05"),"我是创世区块",1,"0000000000"}
76 | genesisBlock.getHash()
77 | //把创世区块添加进区块链
78 | blockchain = append(blockchain,genesisBlock)
79 | fmt.Println(blockchain[0])
80 | i:=0
81 | for {
82 | time.Sleep(time.Second)
83 | newBlock:=generateNewBlock(blockchain[i],"我是区块内容","00000")
84 | blockchain = append(blockchain,newBlock)
85 | fmt.Println(blockchain[i + 1])
86 | i++
87 | }
88 | }
89 | ```
90 |
91 | 运行结果:
92 | 
93 |
--------------------------------------------------------------------------------
/pos/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/rand"
5 | "crypto/sha256"
6 | "encoding/hex"
7 | "fmt"
8 | "log"
9 | "math/big"
10 | "strconv"
11 | "time"
12 | )
13 |
14 | type block struct {
15 | //上一个块的hash
16 | prehash string
17 | //本块hash
18 | hash string
19 | //时间戳
20 | timestamp string
21 | //区块内容
22 | data string
23 | //区块高度
24 | height int
25 | //挖出本块的地址
26 | address string
27 | }
28 |
29 | //用于存储区块链
30 | var blockchain []block
31 |
32 | //代表挖矿节点
33 | type node struct {
34 | //代币数量
35 | tokens int
36 | //质押时间
37 | days int
38 | //节点地址
39 | address string
40 | }
41 |
42 | //挖矿节点
43 | var mineNodesPool []node
44 |
45 | //概率节点池
46 | var probabilityNodesPool []node
47 |
48 | //初始化
49 | func init() {
50 | //手动添加两个节点
51 | mineNodesPool = append(mineNodesPool, node{1000, 1, "AAAAAAAAAA"})
52 | mineNodesPool = append(mineNodesPool, node{100, 3, "BBBBBBBBBB"})
53 | //初始化随机节点池(挖矿概率与代币数量和币龄有关)
54 | for _, v := range mineNodesPool {
55 | for i := 0; i <= v.tokens*v.days; i++ {
56 | probabilityNodesPool = append(probabilityNodesPool, v)
57 | }
58 | }
59 | }
60 |
61 | //生成新的区块
62 | func generateNewBlock(oldBlock block, data string, address string) block {
63 | newBlock := block{}
64 | newBlock.prehash = oldBlock.hash
65 | newBlock.data = data
66 | newBlock.timestamp = time.Now().Format("2006-01-02 15:04:05")
67 | newBlock.height = oldBlock.height + 1
68 | newBlock.address = getMineNodeAddress()
69 | newBlock.getHash()
70 | return newBlock
71 | }
72 |
73 | //对自身进行散列
74 | func (b *block) getHash() {
75 | sumString := b.prehash + b.timestamp + b.data + b.address + strconv.Itoa(b.height)
76 | hash := sha256.Sum256([]byte(sumString))
77 | b.hash = hex.EncodeToString(hash[:])
78 | }
79 |
80 | //随机得出挖矿地址(挖矿概率跟代币数量与币龄有关)
81 | func getMineNodeAddress() string {
82 | bInt := big.NewInt(int64(len(probabilityNodesPool)))
83 | //得出一个随机数,最大不超过随机节点池的大小
84 | rInt, err := rand.Int(rand.Reader, bInt)
85 | if err != nil {
86 | log.Panic(err)
87 | }
88 | return probabilityNodesPool[int(rInt.Int64())].address
89 | }
90 |
91 | func main() {
92 | //创建创世区块
93 | genesisBlock := block{"0000000000000000000000000000000000000000000000000000000000000000", "", time.Now().Format("2006-01-02 15:04:05"), "我是创世区块", 1, "0000000000"}
94 | genesisBlock.getHash()
95 | //把创世区块添加进区块链
96 | blockchain = append(blockchain, genesisBlock)
97 | fmt.Println(blockchain[0])
98 | i := 0
99 | for {
100 | time.Sleep(time.Second)
101 | newBlock := generateNewBlock(blockchain[i], "我是区块内容", "00000")
102 | blockchain = append(blockchain, newBlock)
103 | fmt.Println(blockchain[i+1])
104 | i++
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/pow/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | >工作量证明机制的核心在于不断hash区块自身,将hash值与根据难度值计算出的一串大数对比,如果自身hash小于大数则说明挖矿成功,否则变化自身随机数重新计算。并且程序会随着出块间隔时间动态调节难度值(比如比特币)
6 |
7 |
8 |
9 | 区块结构
10 | ```go
11 | type block struct {
12 | //上一个区块的Hash
13 | Lasthash string
14 | //本区块Hash
15 | Hash string
16 | //区块存储的数据(比如比特币UTXO模型 则此处可用于存储交易)
17 | Data string
18 | //时间戳
19 | Timestamp string
20 | //区块高度
21 | Height int
22 | //难度值
23 | DiffNum uint
24 | //随机数
25 | Nonce int64
26 | }
27 | ```
28 | 挖矿函数:\
29 | 使用math/big包,根据全局变量的难度值diffNum计算出用于实际比较的一串大数newBigint ,并同时将区块hash转换为大数hashInt 两个大数进行数值比较,如果hashInt小于newBigint 则代表挖矿成功
30 |
31 | ```go
32 | //区块挖矿(通过自身递增nonce值计算hash)
33 | func mine(data string) block {
34 | if len(blockchain) < 1 {
35 | log.Panic("还未生成创世区块!")
36 | }
37 | lastBlock := blockchain[len(blockchain)-1]
38 | //制造一个新的区块
39 | newBlock := new(block)
40 | newBlock.Lasthash = lastBlock.Hash
41 | newBlock.Timestamp = time.Now().String()
42 | newBlock.Height = lastBlock.Height + 1
43 | newBlock.DiffNum = diffNum
44 | newBlock.Data = data
45 | var nonce int64 = 0
46 | //根据挖矿难度值计算的一个大数
47 | newBigint := big.NewInt(1)
48 | newBigint.Lsh(newBigint, 256-diffNum) //相当于左移 1<<256-diffNum
49 | for {
50 | newBlock.Nonce = nonce
51 | newBlock.getHash()
52 | hashInt := big.Int{}
53 | hashBytes, _ := hex.DecodeString(newBlock.Hash)
54 | hashInt.SetBytes(hashBytes) //把本区块hash值转换为一串数字
55 | //如果hash小于挖矿难度值计算的一个大数,则代表挖矿成功
56 | if hashInt.Cmp(newBigint) == -1 {
57 | break
58 | } else {
59 | nonce++ //不满足条件,则不断递增随机数,直到本区块的散列值小于指定的大数
60 | }
61 | }
62 | return *newBlock
63 | }
64 | ```
65 |
66 | ```go
67 | func main() {
68 | //制造一个创世区块
69 | genesisBlock := new(block)
70 | genesisBlock.Timestamp = time.Now().String()
71 | genesisBlock.Data = "我是创世区块!"
72 | genesisBlock.Lasthash = "0000000000000000000000000000000000000000000000000000000000000000"
73 | genesisBlock.Height = 1
74 | genesisBlock.Nonce = 0
75 | genesisBlock.DiffNum = 0
76 | genesisBlock.getHash()
77 | fmt.Println(*genesisBlock)
78 | //将创世区块添加进区块链
79 | blockchain = append(blockchain, *genesisBlock)
80 | for i := 0; i < 10; i++ {
81 | newBlock := mine("天气不错"+strconv.Itoa(i))
82 | blockchain = append(blockchain, newBlock)
83 | fmt.Println(newBlock)
84 | }
85 | ```
86 |
87 | 运行结果:
88 | 
89 |
90 |
--------------------------------------------------------------------------------
/pow/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/sha256"
5 | "encoding/hex"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 | "math/big"
10 | "strconv"
11 | "time"
12 | )
13 |
14 | //用于存放区块,以便连接成区块链
15 | var blockchain []block
16 |
17 | //挖矿难度值
18 | var diffNum uint = 17
19 |
20 | type block struct {
21 | //上一个区块的Hash
22 | Lasthash string
23 | //本区块Hash
24 | Hash string
25 | //区块存储的数据(比如比特币UTXO模型 则此处可用于存储交易)
26 | Data string
27 | //时间戳
28 | Timestamp string
29 | //区块高度
30 | Height int
31 | //难度值
32 | DiffNum uint
33 | //随机数
34 | Nonce int64
35 | }
36 |
37 | //区块挖矿(通过自身递增nonce值计算hash)
38 | func mine(data string) block {
39 | if len(blockchain) < 1 {
40 | log.Panic("还未生成创世区块!")
41 | }
42 | lastBlock := blockchain[len(blockchain)-1]
43 | //制造一个新的区块
44 | newBlock := new(block)
45 | newBlock.Lasthash = lastBlock.Hash
46 | newBlock.Timestamp = time.Now().String()
47 | newBlock.Height = lastBlock.Height + 1
48 | newBlock.DiffNum = diffNum
49 | newBlock.Data = data
50 | var nonce int64 = 0
51 | //根据挖矿难度值计算的一个大数
52 | newBigint := big.NewInt(1)
53 | newBigint.Lsh(newBigint, 256-diffNum) //相当于左移 1<<256-diffNum
54 | for {
55 | newBlock.Nonce = nonce
56 | newBlock.getHash()
57 | hashInt := big.Int{}
58 | hashBytes, _ := hex.DecodeString(newBlock.Hash)
59 | hashInt.SetBytes(hashBytes) //把本区块hash值转换为一串数字
60 | //如果hash小于挖矿难度值计算的一个大数,则代表挖矿成功
61 | if hashInt.Cmp(newBigint) == -1 {
62 | break
63 | } else {
64 | nonce++ //不满足条件,则不断递增随机数,直到本区块的散列值小于指定的大数
65 | }
66 | }
67 | return *newBlock
68 | }
69 |
70 | func (b *block) serialize() []byte {
71 | bytes, err := json.Marshal(b)
72 | if err != nil {
73 | log.Panic(err)
74 | }
75 | return bytes
76 | }
77 |
78 | func (b *block) getHash() {
79 | result := sha256.Sum256(b.serialize())
80 | b.Hash = hex.EncodeToString(result[:])
81 | }
82 |
83 | func main() {
84 | //制造一个创世区块
85 | genesisBlock := new(block)
86 | genesisBlock.Timestamp = time.Now().String()
87 | genesisBlock.Data = "我是创世区块!"
88 | genesisBlock.Lasthash = "0000000000000000000000000000000000000000000000000000000000000000"
89 | genesisBlock.Height = 1
90 | genesisBlock.Nonce = 0
91 | genesisBlock.DiffNum = 0
92 | genesisBlock.getHash()
93 | fmt.Println(*genesisBlock)
94 | //将创世区块添加进区块链
95 | blockchain = append(blockchain, *genesisBlock)
96 | for i := 0; i < 10; i++ {
97 | newBlock := mine("天气不错" + strconv.Itoa(i))
98 | blockchain = append(blockchain, newBlock)
99 | fmt.Println(newBlock)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/raft/README.md:
--------------------------------------------------------------------------------
1 | >** 邮箱:mikesen1994@gmail.com vx:965952482**
2 |
3 |
4 |
5 |
6 | 本demo为raft的代码实现,如果想了解raft的详细信息请自行浏览参考资料
7 | 本demo展示了raft的部分功能,写的并不严谨,仅作为对raft的了解用途
8 |
9 |
10 | ## 实现功能:
11 |
12 | - 节点状态分为Leader(领导者)、Follower(追随者)、Candidate(候选人)
13 | - 节点间随机成为candidate状态并选举出Leader,且同时仅存在一个Leader
14 | - Leader节点定时发送心跳检测至其他Follower节点
15 | - Follower节点们超过一定时间未收到心跳检测,则Follower节点们重新开启选举
16 | - 客户端通过http发送消息到节点A,如果A不是Leader则转发至Leader节点
17 | - Leader收到客户端的消息后向Follower节点进行广播
18 | - Follower节点收到消息,反馈给Leader,等待Leader确认
19 | - Leader收到全网超过二分之一的反馈后,本地进行打印,然后将确认收到反馈的信息提交到Follower节点
20 | - Follower节点收到确认提交信息后,打印消息
21 |
22 |
23 |
24 | ## 运行步骤:
25 |
26 |
27 | ##### 1.下载/编译
28 | ```shell
29 | git clone https://github.com/corgi-kx/blockchain_consensus_algorithm.git
30 | ```
31 | ```shell
32 | cd blockchain_consensus_algorithm/raft
33 | ```
34 | ```go
35 | go build -o raft.exe
36 | ```
37 |
38 | ##### 2.开启三个端口,并分别执行raft.exe A 、raft.exe B 、 raft.exe C,代表开启三个节点(初始状态为追随者)
39 | 
40 |
41 | ##### 3.三个节点会随机选举出领导者(其中A节点默认监听来自http的访问),成功的节点会发送心跳检测到其他两个节点
42 | 
43 | ##### 4.此时打开浏览器用http访问本地节点8080端口,带上节点需要同步打印的消息,比如:
44 | `http://localhost:8080/req?message=噢,我的上帝呀`
45 | 
46 | 可以看到三个节点同时打印了消息,本段数据同步步骤可以用下图进行理解(不过缺少了4.1步骤)
47 | 
48 | ##### 5.如果领导者节点宕机了怎么办呢,我们尝试关闭领导者节点B
49 | 
50 | 可以发现关闭领导者B后,节点间有个超时机制,如果超过一定时间没有收到心跳检测,则会自动开始重新进行选举,此时A当选了新的领导者
51 |
52 | ##### 6.再次打开浏览器用http访问本地节点8080端口,带上节点需要同步打印的消息,看看还能同步打印吗
53 | `http://localhost:8080/req?message=天气不错`
54 | 
55 | 结果发现可以打印的,因为新的领导者A、追随者C并没有宕机,A收到了C的回馈(2>3/2)超过了全网一半的节点,所以是可以进行打印数据的
56 |
57 | ##### 7.重新启动节点B,B自动变为追随者状态,并接收来自新的领导者A的心跳检测
58 | 
59 |
60 |
61 | >参考资料:
62 | > - http://thesecretlivesofdata.com/raft/
63 | > - https://www.cnblogs.com/mindwind/p/5231986.html
64 | > - https://blog.csdn.net/s15738841819/article/details/84286276
65 |
--------------------------------------------------------------------------------
/raft/http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/rand"
5 | "fmt"
6 | "log"
7 | "math/big"
8 | "net/http"
9 | "net/rpc"
10 | )
11 |
12 | //等待节点访问
13 | func (rf *Raft) getRequest(writer http.ResponseWriter, request *http.Request) {
14 | request.ParseForm()
15 | //http://localhost:8080/req?message=ohmygod
16 | if len(request.Form["message"]) > 0 && rf.currentLeader != "-1" {
17 | message := request.Form["message"][0]
18 | m := new(Message)
19 | m.MsgID = getRandom()
20 | m.Msg = message
21 | //接收到消息后,直接转发到领导者
22 | fmt.Println("http监听到了消息,准备发送给领导者,消息id:", m.MsgID)
23 | port := nodeTable[rf.currentLeader]
24 | rp, err := rpc.DialHTTP("tcp", "127.0.0.1"+port)
25 | if err != nil {
26 | log.Panic(err)
27 | }
28 | b := false
29 | err = rp.Call("Raft.LeaderReceiveMessage", m, &b)
30 | if err != nil {
31 | log.Panic(err)
32 | }
33 | fmt.Println("消息是否已发送到领导者:", b)
34 | writer.Write([]byte("ok!!!"))
35 | }
36 | }
37 |
38 | func (rf *Raft) httpListen() {
39 | //创建getRequest()回调方法
40 | http.HandleFunc("/req", rf.getRequest)
41 | fmt.Println("监听8080")
42 | if err := http.ListenAndServe(":8080", nil); err != nil {
43 | fmt.Println(err)
44 | return
45 | }
46 | }
47 |
48 | //返回一个十位数的随机数,作为消息idgit
49 | func getRandom() int {
50 | x := big.NewInt(10000000000)
51 | for {
52 | result, err := rand.Int(rand.Reader, x)
53 | if err != nil {
54 | log.Panic(err)
55 | }
56 | if result.Int64() > 1000000000 {
57 | return int(result.Int64())
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/raft/images/开启端口.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/raft/images/开启端口.png
--------------------------------------------------------------------------------
/raft/images/打印消息.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/raft/images/打印消息.png
--------------------------------------------------------------------------------
/raft/images/残缺打印.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/raft/images/残缺打印.png
--------------------------------------------------------------------------------
/raft/images/消息同步.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/raft/images/消息同步.png
--------------------------------------------------------------------------------
/raft/images/选举成功.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/raft/images/选举成功.png
--------------------------------------------------------------------------------
/raft/images/重启B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/raft/images/重启B.png
--------------------------------------------------------------------------------
/raft/images/领导者节点宕机.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corgi-kx/blockchain_consensus_algorithm/7a66af4f2b702a2f4de5efbd4e6ff73cf11725a3/raft/images/领导者节点宕机.png
--------------------------------------------------------------------------------
/raft/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "time"
8 | )
9 |
10 | //定义节点数量
11 | var raftCount = 3
12 |
13 | //节点池
14 | var nodeTable map[string]string
15 |
16 | //选举超时时间(单位:秒)
17 | var timeout = 3
18 |
19 | //心跳检测超时时间
20 | var heartBeatTimeout = 7
21 |
22 | //心跳检测频率(单位:秒)
23 | var heartBeatTimes = 3
24 |
25 | //用于存储消息
26 | var MessageStore = make(map[int]string)
27 |
28 | func main() {
29 | //定义三个节点 节点编号 - 监听端口号
30 | nodeTable = map[string]string{
31 | "A": ":9000",
32 | "B": ":9001",
33 | "C": ":9002",
34 | }
35 | //运行程序时候 指定节点编号
36 | if len(os.Args) < 1 {
37 | log.Fatal("程序参数不正确")
38 | }
39 |
40 | id := os.Args[1]
41 | //传入节点编号,端口号,创建raft实例
42 | raft := NewRaft(id, nodeTable[id])
43 | //启用RPC,注册raft
44 | go rpcRegister(raft)
45 | //开启心跳检测
46 | go raft.heartbeat()
47 | //开启一个Http监听
48 | if id == "A" {
49 | go raft.httpListen()
50 | }
51 |
52 | Circle:
53 | //开启选举
54 | go func() {
55 | for {
56 | //成为候选人节点
57 | if raft.becomeCandidate() {
58 | //成为后选人节点后 向其他节点要选票来进行选举
59 | if raft.election() {
60 | break
61 | } else {
62 | continue
63 | }
64 | } else {
65 | break
66 | }
67 | }
68 | }()
69 |
70 | //进行心跳检测
71 | for {
72 | //0.5秒检测一次
73 | time.Sleep(time.Millisecond * 5000)
74 | if raft.lastHeartBeartTime != 0 && (millisecond()-raft.lastHeartBeartTime) > int64(raft.timeout*1000) {
75 | fmt.Printf("心跳检测超时,已超过%d秒\n", raft.timeout)
76 | fmt.Println("即将重新开启选举")
77 | raft.reDefault()
78 | raft.setCurrentLeader("-1")
79 | raft.lastHeartBeartTime = 0
80 | goto Circle
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/raft/raft.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "sync"
7 | "time"
8 | )
9 |
10 | //声明raft节点类型
11 | type Raft struct {
12 | node *NodeInfo
13 | //本节点获得的投票数
14 | vote int
15 | //线程锁
16 | lock sync.Mutex
17 | //节点编号
18 | me string
19 | //当前任期
20 | currentTerm int
21 | //为哪个节点投票
22 | votedFor string
23 | //当前节点状态
24 | //0 follower 1 candidate 2 leader
25 | state int
26 | //发送最后一条消息的时间
27 | lastMessageTime int64
28 | //发送最后一条消息的时间
29 | lastHeartBeartTime int64
30 | //当前节点的领导
31 | currentLeader string
32 | //心跳超时时间(单位:秒)
33 | timeout int
34 | //接收投票成功通道
35 | voteCh chan bool
36 | //心跳信号
37 | heartBeat chan bool
38 | }
39 |
40 | type NodeInfo struct {
41 | ID string
42 | Port string
43 | }
44 |
45 | type Message struct {
46 | Msg string
47 | MsgID int
48 | }
49 |
50 | func NewRaft(id, port string) *Raft {
51 | node := new(NodeInfo)
52 | node.ID = id
53 | node.Port = port
54 |
55 | rf := new(Raft)
56 | //节点信息
57 | rf.node = node
58 | //当前节点获得票数
59 | rf.setVote(0)
60 | //编号
61 | rf.me = id
62 | //给0 1 2三个节点投票,给谁都不投
63 | rf.setVoteFor("-1")
64 | //0 follower
65 | rf.setStatus(0)
66 | //最后一次心跳检测时间
67 | rf.lastHeartBeartTime = 0
68 | rf.timeout = heartBeatTimeout
69 | //最初没有领导
70 | rf.setCurrentLeader("-1")
71 | //设置任期
72 | rf.setTerm(0)
73 | //投票通道
74 | rf.voteCh = make(chan bool)
75 | //心跳通道
76 | rf.heartBeat = make(chan bool)
77 | return rf
78 | }
79 |
80 | //修改节点为候选人状态
81 | func (rf *Raft) becomeCandidate() bool {
82 | r := randRange(1500, 5000)
83 | //休眠随机时间后,再开始成为候选人
84 | time.Sleep(time.Duration(r) * time.Millisecond)
85 | //如果发现本节点已经投过票,或者已经存在领导者,则不用变身候选人状态
86 | if rf.state == 0 && rf.currentLeader == "-1" && rf.votedFor == "-1" {
87 | //将节点状态变为1
88 | rf.setStatus(1)
89 | //设置为哪个节点投票
90 | rf.setVoteFor(rf.me)
91 | //节点任期加1
92 | rf.setTerm(rf.currentTerm + 1)
93 | //当前没有领导
94 | rf.setCurrentLeader("-1")
95 | //为自己投票
96 | rf.voteAdd()
97 | fmt.Println("本节点已变更为候选人状态")
98 | fmt.Printf("当前得票数:%d\n", rf.vote)
99 | //开启选举通道
100 | return true
101 | } else {
102 | return false
103 | }
104 | }
105 |
106 | //进行选举
107 | func (rf *Raft) election() bool {
108 | fmt.Println("开始进行领导者选举,向其他节点进行广播")
109 | go rf.broadcast("Raft.Vote", rf.node, func(ok bool) {
110 | rf.voteCh <- ok
111 | })
112 | for {
113 | select {
114 | case <-time.After(time.Second * time.Duration(timeout)):
115 | fmt.Println("领导者选举超时,节点变更为追随者状态\n")
116 | rf.reDefault()
117 | return false
118 | case ok := <-rf.voteCh:
119 | if ok {
120 | rf.voteAdd()
121 | fmt.Printf("获得来自其他节点的投票,当前得票数:%d\n", rf.vote)
122 | }
123 | if rf.vote > raftCount/2 && rf.currentLeader == "-1" {
124 | fmt.Println("获得超过网络节点二分之一的得票数,本节点被选举成为了leader")
125 | //节点状态变为2,代表leader
126 | rf.setStatus(2)
127 | //当前领导者为自己
128 | rf.setCurrentLeader(rf.me)
129 | fmt.Println("向其他节点进行广播...")
130 | go rf.broadcast("Raft.ConfirmationLeader", rf.node, func(ok bool) {
131 | fmt.Println(ok)
132 | })
133 | //开启心跳检测通道
134 | rf.heartBeat <- true
135 | return true
136 | }
137 | }
138 | }
139 | }
140 |
141 | //心跳检测方法
142 | func (rf *Raft) heartbeat() {
143 | //如果收到通道开启的信息,将会向其他节点进行固定频率的心跳检测
144 | if <-rf.heartBeat {
145 | for {
146 | fmt.Println("本节点开始发送心跳检测...")
147 | rf.broadcast("Raft.HeartbeatRe", rf.node, func(ok bool) {
148 | fmt.Println("收到回复:", ok)
149 | })
150 | rf.lastHeartBeartTime = millisecond()
151 | time.Sleep(time.Second * time.Duration(heartBeatTimes))
152 | }
153 | }
154 | }
155 |
156 | //产生随机值
157 | func randRange(min, max int64) int64 {
158 | //用于心跳信号的时间
159 | rand.Seed(time.Now().UnixNano())
160 | return rand.Int63n(max-min) + min
161 | }
162 |
163 | //获取当前时间的毫秒数
164 | func millisecond() int64 {
165 | return time.Now().UnixNano() / int64(time.Millisecond)
166 | }
167 |
168 | //设置任期
169 | func (rf *Raft) setTerm(term int) {
170 | rf.lock.Lock()
171 | rf.currentTerm = term
172 | rf.lock.Unlock()
173 | }
174 |
175 | //设置为谁投票
176 | func (rf *Raft) setVoteFor(id string) {
177 | rf.lock.Lock()
178 | rf.votedFor = id
179 | rf.lock.Unlock()
180 | }
181 |
182 | //设置当前领导者
183 | func (rf *Raft) setCurrentLeader(leader string) {
184 | rf.lock.Lock()
185 | rf.currentLeader = leader
186 | rf.lock.Unlock()
187 | }
188 |
189 | //设置当前领导者
190 | func (rf *Raft) setStatus(state int) {
191 | rf.lock.Lock()
192 | rf.state = state
193 | rf.lock.Unlock()
194 | }
195 |
196 | //投票累加
197 | func (rf *Raft) voteAdd() {
198 | rf.lock.Lock()
199 | rf.vote++
200 | rf.lock.Unlock()
201 | }
202 |
203 | //设置投票数量
204 | func (rf *Raft) setVote(num int) {
205 | rf.lock.Lock()
206 | rf.vote = num
207 | rf.lock.Unlock()
208 | }
209 |
210 | //恢复默认设置
211 | func (rf *Raft) reDefault() {
212 | rf.setVote(0)
213 | //rf.currentLeader = "-1"
214 | rf.setVoteFor("-1")
215 | rf.setStatus(0)
216 | }
217 |
--------------------------------------------------------------------------------
/raft/rpc.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "net/rpc"
8 | "time"
9 | )
10 |
11 | //rpc服务注册
12 | func rpcRegister(raft *Raft) {
13 | //注册一个服务器
14 | err := rpc.Register(raft)
15 | if err != nil {
16 | log.Panic(err)
17 | }
18 | port := raft.node.Port
19 | //把服务绑定到http协议上
20 | rpc.HandleHTTP()
21 | //监听端口
22 | err = http.ListenAndServe(port, nil)
23 | if err != nil {
24 | fmt.Println("注册rpc服务失败", err)
25 | }
26 | }
27 |
28 | func (rf *Raft) broadcast(method string, args interface{}, fun func(ok bool)) {
29 | //设置不要自己给自己广播
30 | for nodeID, port := range nodeTable {
31 | if nodeID == rf.node.ID {
32 | continue
33 | }
34 | //连接远程rpc
35 | rp, err := rpc.DialHTTP("tcp", "127.0.0.1"+port)
36 | if err != nil {
37 | fun(false)
38 | continue
39 | }
40 |
41 | var bo = false
42 | err = rp.Call(method, args, &bo)
43 | if err != nil {
44 | fun(false)
45 | continue
46 | }
47 | fun(bo)
48 | }
49 | }
50 |
51 | //投票
52 | func (rf *Raft) Vote(node NodeInfo, b *bool) error {
53 | if rf.votedFor != "-1" || rf.currentLeader != "-1" {
54 | *b = false
55 | } else {
56 | rf.setVoteFor(node.ID)
57 | fmt.Printf("投票成功,已投%s节点\n", node.ID)
58 | *b = true
59 | }
60 | return nil
61 | }
62 |
63 | //确认领导者
64 | func (rf *Raft) ConfirmationLeader(node NodeInfo, b *bool) error {
65 | rf.setCurrentLeader(node.ID)
66 | *b = true
67 | fmt.Println("已发现网络中的领导节点,", node.ID, "成为了领导者!\n")
68 | rf.reDefault()
69 | return nil
70 | }
71 |
72 | //心跳检测回复
73 | func (rf *Raft) HeartbeatRe(node NodeInfo, b *bool) error {
74 | rf.setCurrentLeader(node.ID)
75 | rf.lastHeartBeartTime = millisecond()
76 | fmt.Printf("接收到来自领导节点%s的心跳检测\n", node.ID)
77 | fmt.Printf("当前时间为:%d\n\n", millisecond())
78 | *b = true
79 | return nil
80 | }
81 |
82 | //追随者节点用来接收消息,然后存储到消息池中,待领导者确认后打印
83 | func (rf *Raft) ReceiveMessage(message Message, b *bool) error {
84 | fmt.Printf("接收到领导者节点发来的信息,id为:%d\n", message.MsgID)
85 | MessageStore[message.MsgID] = message.Msg
86 | *b = true
87 | fmt.Println("已回复接收到消息,待领导者确认后打印")
88 | return nil
89 | }
90 |
91 | //追随者节点的反馈得到领导者节点的确认,开始打印消息
92 | func (rf *Raft) ConfirmationMessage(message Message, b *bool) error {
93 | go func() {
94 | for {
95 | if _, ok := MessageStore[message.MsgID]; ok {
96 | fmt.Printf("raft验证通过,可以打印消息,id为:%d\n", message.MsgID)
97 | fmt.Println("消息为:", MessageStore[message.MsgID], "\n")
98 | rf.lastMessageTime = millisecond()
99 | break
100 | } else {
101 | //如果没有此消息,等一会看看!!!
102 | time.Sleep(time.Millisecond * 10)
103 | }
104 |
105 | }
106 | }()
107 | *b = true
108 | return nil
109 | }
110 |
111 | //领导者接收到,追随者节点转发过来的消息
112 | func (rf *Raft) LeaderReceiveMessage(message Message, b *bool) error {
113 | fmt.Printf("领导者节点接收到转发过来的消息,id为:%d\n", message.MsgID)
114 | MessageStore[message.MsgID] = message.Msg
115 | *b = true
116 | fmt.Println("准备将消息进行广播...")
117 | num := 0
118 | go rf.broadcast("Raft.ReceiveMessage", message, func(ok bool) {
119 | if ok {
120 | num++
121 | }
122 | })
123 |
124 | for {
125 | //自己默认收到了消息,所以减去一
126 | if num > raftCount/2-1 {
127 | fmt.Printf("全网已超过半数节点接收到消息id:%d\nraft验证通过,可以打印消息,id为:%d\n", message.MsgID, message.MsgID)
128 | fmt.Println("消息为:", MessageStore[message.MsgID], "\n")
129 | rf.lastMessageTime = millisecond()
130 | fmt.Println("准备将消息提交信息发送至客户端...")
131 | go rf.broadcast("Raft.ConfirmationMessage", message, func(ok bool) {
132 | })
133 | break
134 | } else {
135 | //休息会儿
136 | time.Sleep(time.Millisecond * 100)
137 | }
138 | }
139 | return nil
140 | }
141 |
--------------------------------------------------------------------------------