共识算法
POW 工作量证明
- 暴力破解的方式
实现原理
- 关键字:
- 难度系数 Difficulty
- 随机值 Nonce
- 将新生成的区块信息(包含上一个hash,nonce,difficulty)等信息通过Sha256计算hash值,再对hash值进行判断,判断前缀是否符合难度系数,如难度系数为5,代表生成的hash值前面有5个0,则代表这个区块是一个合法区块,如果区块不合校验规则,则Nonce++ 的方式不停的重试
区块校验
- 区块结构语法有效
- 验证pow,区块头的hash满足难度系数
- 区块时间戳在2小时内
- 验证区块大小
- 验证区块内交易的合法性
package main
import (
"strconv"
"time"
"crypto/sha256"
"strings"
"encoding/hex"
"fmt"
)
//pow 挖矿算法
//定义难度系数
const difiiculty = 4
type Block struct {
Index int // 区块高度
TimeStamp int64
Data string //交易记录
Hash string
Prehash string
Nonce int
Difficulty int //难度系数
}
//创建区块链
var BlockChain []Block
//创世区块
func GenesisBlock() *Block {
var geneBlock = Block{0, time.Now().Unix(), "", "", "", 0, difiiculty}
geneBlock.Hash = hex.EncodeToString(BlockHash(geneBlock))
return &geneBlock
}
func BlockHash(block Block) []byte {
re := strconv.Itoa(block.Index) + strconv.Itoa(int(block.TimeStamp)) + block.Data + block.Prehash +
strconv.Itoa(block.Nonce) + strconv.Itoa(block.Difficulty)
h := sha256.New()
h.Write([]byte(re))
hashed := h.Sum(nil)
return hashed
}
func isBlockValid(block Block) bool {
prefix := strings.Repeat("0", block.Difficulty)
return strings.HasPrefix(block.Hash, prefix)
}
//创建新区块 pow挖矿
func CreateNewBlock(lastBlock *Block, data string) *Block {
var newBlock Block
newBlock.Index = lastBlock.Index + 1
newBlock.TimeStamp = time.Now().Unix()
newBlock.Data = data
newBlock.Prehash = lastBlock.Hash
newBlock.Difficulty = difiiculty
newBlock.Nonce = 0
//开挖-当前区块的hash值的前面的0的个数与难度系数值相同
for {
//计算hash
cuhash := hex.EncodeToString(BlockHash(newBlock))
fmt.Println("挖矿中",cuhash)
newBlock.Hash = cuhash
if isBlockValid(newBlock) {
//校验区块
if VerflyBlock(newBlock, *lastBlock) {
fmt.Println("挖矿成功")
return &newBlock
}
}
newBlock.Nonce ++
}
}
//校验新的区块是否合法
func VerflyBlock(newblock Block, lastBlock Block) bool {
if lastBlock.Index +1 !=newblock.Index {
return false
}
if newblock.Prehash !=lastBlock.Hash {
return false
}
return true
}
func main() {
var genBlock = GenesisBlock()
newBlock := CreateNewBlock(genBlock,"新区块")
fmt.Println(newBlock)
}
优点:
- 去中心化
- 算法简单
缺点
- 浪费资源(算力)
- 散列算法固定(Sha256),后续会遭到破解
POS 权益证明
- 核心是通过币龄,铸造区块来获取币同时也会减小币龄
持币有利息
- 累积币龄最大的为主链
实现原理
-
关键字:
- 币龄: 每个币每天产生1币龄, 拥有m个币,持有n天,则CoinAge为 m*n ,币龄越大出块的概率越高
-
不同的旷工拥有的币龄不同,币龄越大的人,在整个区块链网络中所占的百分比也就越大,因此随机筛选一位作为旷工的概率也就越大
-
当某个旷工挖取到区块之后,会得到奖励 n个币 ,同时会减少币龄,既 获取币,减少了币龄
package main
import (
"time"
"strconv"
"crypto/sha256"
"math/rand"
"fmt"
"encoding/hex"
)
//实现pos挖矿的原理
type Block struct {
Index int
Data string //
PreHash string
Hash string
Timestamp string
//记录挖矿节点
Validator *Node
}
func genesisBlock() Block {
var genesBlock = Block{0, "Genesis block","","",time.Now().String(),&Node{0, 0, "dd"}}
genesBlock.Hash = hex.EncodeToString(BlockHash(&genesBlock))
return genesBlock
}
func BlockHash(block *Block) []byte {
record := strconv.Itoa(block.Index) + block.Data + block.PreHash + block.Timestamp + block.Validator.Address
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hashed
}
//创建全节点类型
type Node struct {
Tokens int //持币数量
Days int //持币时间
Address string //地址
}
//创建5个节点
//算法的实现要满足 持币越多的节点越容易出块
var nodes = make([]Node, 5)
//存放节点的地址
var addr = make([]*Node, 15)
func InitNodes() {
nodes[0] = Node{1, 1, "0x12341"}
nodes[1] = Node{2, 1, "0x12342"}
nodes[2] = Node{3, 1, "0x12343"}
nodes[3] = Node{4, 1, "0x12344"}
nodes[4] = Node{5, 1, "0x12345"}
cnt :=0
for i:=0;i<5;i++ {
for j:=0;j<nodes[i].Tokens * nodes[i].Days;j++{
addr[cnt] = &nodes[i]
cnt++
}
}
}
//采用Pos共识算法进行挖矿
func CreateNewBlock(lastBlock *Block, data string) Block{
var newBlock Block
newBlock.Index = lastBlock.Index + 1
newBlock.Timestamp = time.Now().String()
newBlock.PreHash = lastBlock.Hash
newBlock.Data = data
//通过pos计算由那个村民挖矿
//设置随机种子
rand.Seed(time.Now().Unix())
//[0,15)产生0-15的随机值
// FIXME 随机的方式不正确,正确的应该是范围命中的方式
var rd =rand.Intn(15)
//选出挖矿的旷工
node := addr[rd]
//设置当前区块挖矿地址为旷工
newBlock.Validator = node
//简单模拟 挖矿所得奖励
node.Tokens += 1
newBlock.Hash = hex.EncodeToString(BlockHash(&newBlock))
return newBlock
}
func main() {
InitNodes()
//创建创世区块
var genesisBlock = genesisBlock()
//创建新区快
var newBlock = CreateNewBlock(&genesisBlock, "new block")
//打印新区快信息
fmt.Println(newBlock)
fmt.Println(newBlock.Validator.Address)
fmt.Println(newBlock.Validator.Tokens)
}
优点
- 相比pow 共识缩短
- 资源不浪费
缺点
DPOS Delegated Proof of Stake 委托权益证明
- 让每个持有币的人进行委托选举,选举投票最多的n个人将会成为代表(矿池),生成区块,
类似于人大代表
,若未生产出区块,则会被除名,重新投票选举新的超级节点
原理
- 每个持币节点具有选择权,对网络中的节点进行投票选举,前n个节点会作为超级节点行使
POS
的权利,生成区块 - Q: 超级节点具有什么功能:
- A: 超级节点可以配置网络中的利息,出块时间等
- Q: 超级节点什么时候剔除
- A: 当超级节点无法履行其职责的,如无法生成区块时,会被剔除,重新选举
package main
import (
"fmt"
"math/rand"
"time"
"strconv"
"crypto/sha256"
"encoding/hex"
)
type Block struct {
Index int
Timestamp string
Prehash string
Hash string
Data []byte
delegate *Node// 代理 区块由哪个节点挖出
}
func GenesisBlock() Block {
gene := Block{0, time.Now().String(),"", "", []byte("genesis block"), nil}
gene.Hash = string(blockHash(gene))
return Block{}
}
func blockHash(block Block) []byte {
record := strconv.Itoa(block.Index) + block.Timestamp + block.Prehash + hex.EncodeToString(block.Data)
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hashed
}
//节点类型
type Node struct {
Name string //节点名称
Votes int // 被选举的票数
}
func (node *Node)GenerateNewBlock(lastBlock Block, data []byte) Block {
var newBlock = Block{lastBlock.Index+1, time.Now().String(), lastBlock.Hash, "", data, nil}
newBlock.Hash = hex.EncodeToString(blockHash(newBlock))
newBlock.delegate = node
return newBlock
}
//创建节点
var NodeArr = make([]Node,10)
func CreateNode() {
for i := 0; i < 10; i++ {
name := fmt.Sprintf("NODE %d num", i+1)
NodeArr[i] = Node{name, 0}
}
}
//简单模拟投票
func Vote() {
for i := 0; i < 10; i++ {
rand.Seed(time.Now().UnixNano())
vote := rand.Intn(10) + 1
NodeArr[i].Votes = vote
}
}
//选出票数最多的前3位
func SortNodes() []Node {
n:= NodeArr
for i := 0; i<len(n) ;i++ {
for j := 0; j < len(n)-1 ;j++ {
if n[j].Votes < n[j+1].Votes {
n[j],n[j+1] = n[j+1],n[j]
}
}
}
return n[:3]
}
func main() {
CreateNode()
fmt.Println(NodeArr)
Vote()
nodes := SortNodes()
fmt.Println(nodes)
//创建创世区块
gene := GenesisBlock()
lastBlock := gene
for i:= 0; i< len(nodes) ;i++ {
lastBlock = nodes[i].GenerateNewBlock(lastBlock,[]byte(fmt.Sprintf("new block %d",i)))
}
}
优点
- 提升共识效率,处理效率
缺点
- 权利集中在超级节点,不合公链的理念,并且某些节点可以一直成为超级节点
BFT
- 既拜占庭容错,用于解决分布式对等网络节点间通信容错问题
- Q: 什么是拜占庭容错:
- A: 由于硬件错误、网络拥塞或断开以及遭到恶意攻击,计算机和网络可能出现不可预料的行为 ;可以认为是叛徒节点伪造请求
PBFT
- 用于解决BFT性能低下问题而出现
Raft
-
与PBFT的区别在于,RAFT不支持
作恶节点,只支持故障节点
,并且可支持的故障节点数为 (N-1)/2 N为集群节点总量,既可用节点需要为半数以上 -
而PBFT不仅支持故障节点,也支持作恶节点,最大容错节点数为 (N-1)/3
-
白话讲RAFT和PBFT的区别为:
-
RAFT 遵循领导的话,领导的话维诺不从,当领导任期到了,重新选一个新的领导作为发言人
-
PBFT 认为一切都不可信,领导的命令会征询旗下的其他节点,当超过2f+1个节点认可,则会认为该命令可信 f指的是故障节点,故障节点数<= (N-1)/3
-