作者:黎跃春
孔壹学院创始人兼CEO
黎跃春:孔壹学院创始人兼CEO,国内区块链布道先行者,通信和信息技术培养工程区块链高级授课专家。如果您有任何关于区块链的问题,可以加入区块链技术交流QQ群729666975(进群无需添加验证信息,直接点击下一步,等待管理员通过即可),我们会为您一一解答。
从零到壹学习共识算法为一个系列,一共18讲,包括通过一个App的演示深入理解区块链运行原理、go 实现简单的区块链、拜占庭、PoW、PoS、DPoS、Paxos、Raft等,为大家详尽的介绍共识算法的学习过程。今天我们将为大家介绍从零到壹学习共识算法第十讲:使用 go 实现 Proof of Stake 共识机制。话不多说,马上开启我们的共识算法学习之旅。
课程学习,添加莉莉微信(kongyixueyuan)。
孔壹学院
什么是 Proof of Stake
在PoW中,节点之间通过hash的计算力来竞赛以获取下一个区块的记账权,而在PoS中,块是已经铸造好的,铸造的过程是基于每个节点(Node)愿意作为抵押的令牌(Token)数量。如果验证者愿意提供更多的令牌作为抵押品,他们就有更大的机会记账下一个区块并获得奖励。
实现 Proof of Stake 主要功能点
我们将有一个中心化的TCP服务节点,其他节点可以连接该服务器
最新的区块链状态将定期广播到每个节点
每个节点都能提议建立新的区块
基于每个节点的令牌数量,其中一个节点将随机地(以令牌数作为加权值)作为获胜者,并且将该区块添加到区块链中
实现 Proof of Stake
设置 TCP 服务器的端口
新建 ,添加如下内容
安装依赖软件
在控制台中格式化输出相应的结果。
可以从我们项目的根目录的 文件中读取数据。
引入相应的包
新建 ,引入相应的包
全局变量
是每个区块的内容
是我们的官方区块链,它只是一串经过验证的区块集合。每个区块中的 与前面块的 相比较,以确保我们的链是正确的。 是临时存储单元,在区块被选出来并添加到 之前,临时存储在这里
是 的通道,任何一个节点在提出一个新块时都将它发送到这个通道
也是一个通道,我们的主Go TCP服务器将向所有节点广播最新的区块链
是一个标准变量,允许我们控制读/写和防止数据竞争
是节点的存储map,同时也会保存每个节点持有的令牌数
生成区块
是用来创建新块的。 存储的是上一个区块的 是通过 生成的 Hash 。 存储的是获取记账权的节点地址
函数会接受一个 ,并且返回一个 。
是对一个 进行 ,将一个 的所有字段连接到一起后,再调用 将字符串转为 。
验证区块
我们通过检查 来确保它们按预期递增。我们也检查以确保我们 的确与 前一个区块相同。最后,我们希望通过在当前块上 再次运行该函数来检查当前块的散列。
验证者
当一个验证者连接到我们的TCP服务,我们需要提供一些函数达到以下目标:
输入令牌的余额(之前提到过,我们不做钱包等逻辑)
接收区块链的最新广播
接收验证者赢得区块的广播信息
将自身节点添加到全局的验证者列表中(validators)
输入Block的BPM数据- BPM是每个验证者的人体脉搏值
提议创建一个新的区块
允许验证者输入他持有的令牌数量,然后,该验证者被分配一个 地址,随后该验证者地址和验证者的令牌数被添加到验证者列表 中。
接着我们输入BPM,验证者的脉搏值,并创建一个单独的Go协程来处理这块儿逻辑
如果验证者试图提议一个被污染(例如伪造)的 ,例如包含一个不是整数的BPM,那么程序会抛出一个错误,我们会立即从我们的验证器列表 中删除该验证者,他们将不再有资格参与到新块的铸造过程同时丢失相应的抵押令牌。
正是因为这种抵押令牌的机制,使得PoS协议是一种更加可靠的机制。如果一个人试图伪造和破坏,那么他将被抓住,并且失去所有抵押和未来的权益,因此对于恶意者来说,是非常大的威慑。
接着,我们用 函数创建一个新的 ,然后将其发送到 通道进行进一步处理。将 发送到通道使用的语法:
最后会循环打印出最新的区块链,这样每个验证者都能获知最新的状态。
选择获取记账权的节点
下面是PoS的主要逻辑。我们需要编写代码以实现获胜验证者的选择;他们所持有的令牌数量越高,他们就越有可能被选为胜利者。
每隔30秒,我们选出一个胜利者,这样对于每个验证者来说,都有时间提议新的区块,参与到竞争中来。接着创建一个,它会持有所有验证者的地址,这些验证者都有机会成为一个胜利者。然后,对于提议块的暂存区域,我们会通过来判断是否已经有了被提议的区块。
在循环中,要检查暂存区域是否和 中存在同样的验证者,如果存在,则跳过。
在以 开始的代码块中,我们确保了从中取出来的验证者都是合法的,即这些验证者在验证者列表已存在。若合法,则把该验证者加入到中。
那么我们怎么根据这些验证者持有的令牌数来给予他们合适的随机权重呢?
首先,用验证者的令牌填充数组,例如一个验证者有100个令牌,那么在中就将有100个元素填充;如果有1个令牌,那么将仅填充1个元素。
然后,从中随机选择一个元素,元素所属的验证者即是胜利者,把胜利验证者的地址赋值给lotteryWinner。这里能够看出来,如果验证者持有的令牌越多,那么他在数组中的元素也越多,他获胜的概率就越大;同时,持有令牌很少的验证者,也是有概率获胜的。
接着我们把获胜者的区块添加到整条区块链上,然后通知所有节点关于胜利者的消息:。
最后,清空tempBlocks,以便下次提议的进行。
主函数
会解析 文件并将相应的Key/Value对都放到环境变量中,通过 获取
然后创建一个创世区块genesisBlock,形成了区块链。
接着启动了Tcp服务,等待所有验证者的连接。
启动了一个Go协程从 通道中获取提议的区块,然后填充到临时缓冲区 中,最后启动了另外一个Go协程来完成 函数。
最后的for循环,用来接收验证者节点的连接。
运行
启动您的Go程序和TCP服务器,并会打印出初始区块的信息。
打开新的终端,运行 ,输入 , 然后输入
可以打开多个终端,输入不同的 ,来检验 PoS 算法
领取专属 10元无门槛券
私享最新 技术干货