优点
缺点
优点
缺点
以下就是这三个共识算法的伪代码
数据结构:model.Block.go
package model
type PowBlock struct {
Index int64 // 区块号
Timestamp string // 时间戳字符串
Hash string // 区块的hash值
PrevHash string // 上一个区块的hash
NodeAddress string // 生成这个区块的节点地址
Data string // 区块数据
Nonce int // 随机值
}
type PosBlock struct {
Index int64 // 区块号
Timestamp string // 时间戳字符串
Hash string // 区块的hash值
PrevHash string // 上一个区块的hash
NodeAddress string // 生成这个区块的节点地址
Data string // 区块数据
Nonce int // 随机值
}
type DPosBlock struct {
Index int64 // 区块号
Timestamp string // 时间戳字符串
Hash string // 区块的hash值
PrevHash string // 上一个区块的hash
NodeAddress string // 生成这个区块的节点地址
Data string // 区块数据
Nonce int // 随机值
}
type WitnessNode struct {
Name string
NodeAddress string
Votes int // 投票数
}
全局变量:common/globleValue.go
package common
import (
"block_test/model"
)
// ************************* PoW **********************************
// GlobleBlocks CandidateBlocks 全局变量
var PowGlobleBlocks []model.PowBlock // 区块链
var PowCandidateBlocks []model.PowBlock // 候选区块数组
// ************************* PoS **********************************
// PosGlobleBlocks GlobleBlocks 全局变量
var PosGlobleBlocks []model.PosBlock // 区块链
var PosCandidateBlocks []model.PosBlock // 候选区块数组
// StackRecord 股权记录表
var StackRecord []string
// ************************* DPoS **********************************
// WitnessList 见证者列表
var WitnessList []model.WitnessNode
// BeforeTime 上一次更新时间
var BeforeTime int64
// DPosCycle 更新周期为一个小时
var DPosCycle = 60 * 60
// 见证者的数量限制
var WitnessNum = 100
PoW伪代码:server/pow.go
package server
import (
"block_test/common"
"block_test/model"
"encoding/hex"
"math/rand"
"strconv"
"strings"
"time"
)
// 判断是否要生成区块的函数
func IsBlockHashMatchDifficulty(block model.PowBlock, difficulty int) bool {
prefix := strings.Repeat("0", difficulty) // 根据难度值生成对应个数的前缀 0
hash := powCalculateHash(block)
return strings.HasPrefix(hash, prefix) // 进行前缀0个数的比较,包含则返回true
}
// 生成hash函数
func powCalculateHash(block model.PowBlock) string {
return "hash"
}
// 生成区块函数
func PowGenerateBolck(oldBlock model.PowBlock, data string, difficulty int) model.PowBlock {
var newBlock model.PowBlock
newBlock.Timestamp = strconv.FormatInt(time.Now().Unix(), 10)
newBlock.Index = oldBlock.Index + 1
newBlock.Data = data
newBlock.PrevHash = oldBlock.Hash
for i := 0; ; i++ {
newBlock.Nonce = hex.EncodedLen(rand.Intn(255)) // 给一个nonce值,先是给一个随机数吧,0-255
newBlock.Hash = powCalculateHash(newBlock)
if IsBlockHashMatchDifficulty(newBlock, difficulty) {
common.PowCandidateBlocks = append(common.PowCandidateBlocks, newBlock)
break
}
}
return newBlock
}
// 校验区块函数
func VerifyBlock(difficulty int) {
var resultBlock model.PowBlock
for i := 0; i < len(common.PowCandidateBlocks); i++ {
if IsBlockHashMatchDifficulty(common.PowCandidateBlocks[i], difficulty) {
resultBlock = common.PowCandidateBlocks[i]
break
}
continue
}
// 将候选的区块添加进区块链
common.PowGlobleBlocks = append(common.PowGlobleBlocks, resultBlock)
// 广播已经产生新区块的信息
}
PoS伪代码:server/pos.go
package server
import (
"block_test/common"
"block_test/model"
"math/rand"
"strconv"
"time"
)
// 生成hash函数
func posCalculateHash(block model.PosBlock) string {
return "hash"
}
//获取节点的balance
func getCoinBlance(nodeAddress string) int {
balance := 5 // 通过读取智能合约的上该节点的balance,这里先写死
return balance
}
// contain
func stackRecordContainNodeAddress(stackRecord []string, nodeAddress string) bool {
for i := 0; i < len(stackRecord); i++ {
if stackRecord[i] == nodeAddress {
return true
}
}
return false
}
// StakeDistribution 股权分配, 方式1
func StakeDistribution() {
var stackRecord []string
for i := 0; i < len(common.PosCandidateBlocks); i++ {
nodeAddress := common.PosCandidateBlocks[i].NodeAddress
coinNum := getCoinBlance(nodeAddress)
for j := 0; j < coinNum; j++ {
if stackRecordContainNodeAddress(stackRecord, nodeAddress) {
break
}
stackRecord = append(stackRecord, nodeAddress)
}
}
common.StackRecord = stackRecord
}
// StakeDistribution2 股权分配, 我个人认为这样更合理
func StakeDistribution2() {
var stackRecord []string
for i := 0; i < len(common.PosCandidateBlocks); i++ {
nodeAddress := common.PosCandidateBlocks[i].NodeAddress
if stackRecordContainNodeAddress(stackRecord, nodeAddress) {
continue
}
coinNum := getCoinBlance(nodeAddress)
for j := 0; j < coinNum; j++ {
stackRecord = append(stackRecord, nodeAddress)
}
}
common.StackRecord = stackRecord
}
// PosGenerateBolck 区块生成
func PosGenerateBolck(oldBlack model.PosBlock, data string) {
stackNum := len(common.StackRecord)
index := rand.Intn(stackNum - 1) // 0 - stackNum(股权数)
winner := common.StackRecord[index] // 胜出的 nodeAddress
var newBlock model.PosBlock
for i := 0; i < len(common.PosCandidateBlocks); i++ {
if winner == common.PosCandidateBlocks[i].NodeAddress {
newBlock.Timestamp = strconv.FormatInt(time.Now().Unix(), 10)
newBlock.Index = oldBlack.Index + 1
newBlock.Data = data
newBlock.PrevHash = oldBlack.Hash
newBlock.Hash = posCalculateHash(newBlock)
// other info
common.PosGlobleBlocks = append(common.PosGlobleBlocks, newBlock)
}
}
}
DPoS伪代码:server/dpos.go
package server
import (
"block_test/common"
"block_test/model"
"math/rand"
"sort"
"time"
)
// NeedRestVote DPos 是每过一个周期,进行重新投票排名
func NeedRestVote() bool {
now := time.Now().Unix()
if (now - common.BeforeTime) > int64(common.DPosCycle) {
common.BeforeTime = now
return true
}
return false
}
// SortByVotes 根据投票数来降序排序
func SortByVotes(witnessList []model.WitnessNode) {
sort.Slice(common.WitnessList, func(i, j int) bool {
return common.WitnessList[i].Votes > common.WitnessList[j].Votes
})
}
func SortWitnessList() {
// 判断是否重新投票
if NeedRestVote() {
for i := 0; i < len(common.WitnessList); i++ {
// 进行投票
common.WitnessList[i].Votes = rand.Intn(1000) // 假装进行投票
}
}
//按投票数进行降序排序
SortByVotes(common.WitnessList)
}
// 判断是否是无效的见证者
func isBadNode(node model.WitnessNode) bool {
// 判断规则
return false
}
// 检查见证者是否有效
func CheckBadNode(node model.WitnessNode) {
for i := 0; i < len(common.WitnessList); i++ {
if isBadNode(common.WitnessList[i]) {
common.WitnessList = append(common.WitnessList[:i], common.WitnessList[i+1:]...)
}
}
}
func getNewNode() *model.WitnessNode {
// 通过某种方式获取该新节点的信息
var node model.WitnessNode
return &node
}
// 检测是否有新的节点进来
/*
个人理解:
第一种情况:
假如当前 witnessList 还有空间,即当前见证者的列表长度小于 witnessNum,
且有新的节点被投票,那么就把这个节点放进 witnessList
第二种情况:
假如当前 witnessList 没有空间,即当前见证者的列表长度等于 witnessNum,
且有新的节点被投票,且新的节点的被投票数大于 witnessList 中最小被投票的节点的被投票数
*/
func isNewNodeComing() *model.WitnessNode {
// 判断是否有新节点
node := getNewNode()
if node != nil {
return node
} else {
return nil
}
}
// 把指针类型的节点转换成非指针类型
func getNode(pNode *model.WitnessNode) model.WitnessNode {
var node model.WitnessNode
node.NodeAddress = pNode.NodeAddress
node.Votes = pNode.Votes
node.Name = pNode.Name
return node
}
// CheckNewWitnessNode 这个需要不断的检测,这在调用的时候,需要开启一个协程
func CheckNewWitnessNode() {
for {
num := len(common.WitnessList)
if num < common.WitnessNum {
newWitness := isNewNodeComing()
if newWitness != nil {
node := getNode(newWitness)
common.WitnessList = append(common.WitnessList, node)
}
// 延迟0.1秒
time.Sleep(100 * time.Millisecond)
}
}
}
func getWitnessByIndex(witnessNode []model.WitnessNode) *model.WitnessNode {
for i := 0; i < len(witnessNode); i++ {
// 节点检验判断,如果没问题,直接返回该节点
if true {
return &witnessNode[i]
}
}
return nil
}
func generateBlock(node *model.WitnessNode) (model.DPosBlock, bool) {
var block model.DPosBlock
var timeOut bool
timeOut = false // 初始化 timeOut 变量为 false
// 区块产生逻辑,并需要对 timeOut 处理
return block, timeOut
}
func MakeBlock() model.DPosBlock {
SortWitnessList()
var resultBlock model.DPosBlock
for {
// 从上到下遍历,选择节点没问题的且票数最多的节点
witness := getWitnessByIndex(common.WitnessList)
if witness == nil {
break // 所有见证者出块都出了问题
}
// 出块
block, timeOut := generateBlock(witness)
resultBlock = block
if timeOut {
// 超时就轮到下一个
continue
}
// 广播block块出去,然后结束该轮,等待下一次开始
break
}
return resultBlock
}