区块链主流的三种共识算法(伪代码解释):PoW,PoS,DPoS

  • PoW(Proof of Work:工作量证明),以BTC为代表,区块链1.0
  • PoP(Proof of Stake:股权证明),以ETH为代表,区块链2.0
  • DPoS(Decentralized Proof of Stake:去中心化的股权证明):以EOS为代表,区块链3.0

PoW

优点

  • 难度系数(difficulty)可自动调整
  • 越早进场,越占优势,有促进作用
  • 奖励都是分给个人,相对公平,相对去中心化

缺点

  • 对资源消耗大
  • 比的是算力,谁算力大,谁占优势,以去中心化有所违背
  • 安全性还差点意思,“51%算力攻击”

PoS

优点

  • 缩短共识达成时间,出块时间速度快
  • 资源消耗小

缺点

  • 攻击成本低,节点有token可以发起脏数据的区块攻击

以下就是这三个共识算法的伪代码
数据结构: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
}

你可能感兴趣的:(共识算法)