区块链主流的三种共识算法(伪代码解释):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
}

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
什么是共识算法背景分布式系统集群设计中面临着一个不可回避的问题,一致性问题对于系统中的多个服务节点,给定一系列操作,如何试图使全局对局部处理结果达成某种程度的一致?这个一致性问题大致有如下的场景:节点之间通讯不可靠的,延迟和阻塞节点的处理可能是错误的,甚至节点自身随时可能宕机节点作恶举例说明,就比如有两家电影院同时售卖总量一定的电影票,在这样的场景下,要如何设计方式来保证两家电影院协调同步不出现超卖或者错卖的问题呢?共识算法,就是解决对某一提案(目标,投票等各种协作工作),大家达成一致意见的过程比如上述的买票问题,就可以有如下的设计:1.每次卖票打电话给其他电影院,确认当前票数2.协商售卖时间,比如一三五A卖,二四六B卖3.成立个第三方存票机构,它统一发票通过以上的设计,可以看出一个很重要的解决一致性算法的解决思路,即:将可能引发不一致的并行操作进行串行化,就是现在计算机系统里处理分布式一致性问题基础思路和唯一秘诀著名的共识设计理论FLP 不可能性原理  共识算法的理论下限提出该定理的论文是由 Fischer, Lynch 和 Patterson 三位作者于 1985 年发表,该论文后来获得了 Dijkstra(就是发明最短路径算法的那位)奖。FLP 原理认为对于允许节点失效情况下,纯粹异步系统无法确保一致性在有限时间内完成。三人三房间投票例子三个人在不同房间,进行投票(投票结果是 0 或者 1)。三个人彼此可以通过电话进行沟通,但经常会有人时不时地睡着。比如某个时候,A 投票 0,B 投票 1,C 收到了两人的投票,然后 C 睡着了。A 和 B 则永远无法在有限时间内获知最终的结果。如果可以重新投票,则类似情形每次在取得结果前发生带入到计算机领域就是说,即便在网络通信可靠情况下,一个可扩展的分布式系统的共识问题的下限是无解。即可靠性的下限是0%CAP  分布式系统领域的重要原理CAP 原理最早由 Eric Brewer 在 2000 年,ACM 组织的一个研讨会上提出猜想,后来 Lynch 等人进行了证明• C(一致性):所有的节点上的数据时刻保持同步,即数据一致• A(可用性):每个请求都能在一定时间内接受到一个响应,即低延迟• P(分区容错):当系统发生分区时仍然可以运行的定理:任何分布式系统只可同时满足二点,没法三者兼顾。即数据一致,响应及时,可分区执行不可能同时满足。举个例子:一个分布式网路上,某一个节点有一组依赖数据A,当网络无延迟,无阻塞时,依赖于X的操作可正常进行。但网络无延迟阻塞在现实世界中是没法100%保证的,那么当网络异常时,必然会产生分布式系统的分区和孤岛,那当一个执行操作在A分区之外时,如果要保证P,即当系统发生分区时仍可运行,就需要在分布式系统中多个节点有X的备份数据,以应对分区情况。则这时候就需要在C,A之间做出选择。假如选择C,即要保证数据在分布式网络中的一致性,那么就需要在X每次改动时,需要将全网节点的X数据同步刷新成最新的状态,那么在等待数据刷新完成之前,分布式系统是不可响应X的依赖操作的,即A的功能缺失假如选择A,即要突出低延迟的实时响应。那么在响应的时候,可能全节点的X数据并没有同步到最新的状态,则会导致C的缺失。上面看上去有些绕,那么你只要记住这句话,CAP原理在分布式网络系统的应用讨论,其实就是讨论在允许网络发生故障的系统中,该选择一致性还是可靠性?如果系统重视一致性,那么可以基于ACID原则做系统设计即 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)。ACID 原则描述了对分布式数据库的一致性需求,同时付出了可用性的代价。• Atomicity:每次操作是原子的,要么成功,要么不执行;• Consistency:数据库的状态是一致的,无中间状态;• Isolation:各种操作彼此互相不影响;• Durability:状态的改变是持久的,不会失效相应的有一个BASE原则,(Basic Availiability,Soft state,Eventually Consistency)则强调了可用性。经典的共识算法设计业内,针对节点异常的情况,会有两种分类1.故障的,不响应的节点,成为非拜占庭错误2.恶意响应的节点,称为非拜占庭错误Paxos 最早的共识算法  非拜占庭算法的代表Paxos有三种角色:• proposer:提出一个提案,等待大家批准为结案。客户端担任该角色;• acceptor:负责对提案进行投票。往往是服务端担任该角色;• learner:被告知结案结果,并与之统一,不参与投票过程。即普通节点系统运行由proposer驱动,当合法提案在一定时间内收到1/2以上投票后达成共识。 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值