原文发表日期: 2018-03-26
原文链接:https://medium.com/@mycoralhealth/code-your-own-proof-of-stake-blockchain-in-go-610cd99aa658转载请在文章开头注明作者和出处
作者: ChainGod(孙飞)
原文链接: http://chaingod.io/article/16
PoS简介
在上一篇文章中,我们讨论了工作量证明(Proof of Work),并向您展示了如何编写自己的工作量证明区块链。当前最流行的两个区块链平台,比特币和Ethereum都是基于工作量证明的。
但是工作证明的缺点是什么呢?其中一个主要的问题是电力能源的消耗。为了挖掘更多的比特币,就需要建立更多的挖矿硬件池,现在在世界各地,挖矿池都在不断建立中,而且呈现出规模越来越大的趋势。例如以下这张照片(仅仅是矿池的一角):
挖矿工作需要耗费大量的电力,仅比特币开采耗费的能源就超过了159个国家的电力能源消耗总和!!这种能源消耗是非常非常不合理的,而且,从技术的角度来看,工作量证明还有其他不足之处:随着越来越多的人参与到挖矿工作中,共识算法的难度就需要提高,难度的提高意味着需要更多、更长时间的挖矿,也意味着区块和交易需要更长的时间才能得到处理,因此能源的消耗就会越发的高。总之,工作量证明的方式就是一场竞赛,你需要更多的计算能力才能有更大的概率赢得比赛。
有很多区块链学者都试图找到工作量证明的替代品,到目前为止最有希望的就是PoS(权益证明或者股权证明,Proof of Stake)。目前在生产环境,已经有数个区块链平台使用了PoS,例如Nxt 和Neo。以太坊Ethereum在不远的未来也很可能会使用PoS——他们的Casper项目已经在测试网络上运行和测试了。
那么,到底什么才是股权证明PoS呢?
在PoW中,节点之间通过hash的计算力来竞赛以获取下一个区块的记账权,而在PoS中,块是已经铸造好的(这里没有“挖矿”的概念,所以我们不用这个词来证明股份),铸造的过程是基于每个节点(Node)愿意作为抵押的令牌(Token)数量。这些参与抵押的节点被称为验证者(Validator),注意在本文后续内容中,验证者和节点的概念是等同的!令牌的含义对于不同的区块链平台是不同的,例如,在以太坊中,每个验证者都将Ether作为抵押品。
如果验证者愿意提供更多的令牌作为抵押品,他们就有更大的机会记账下一个区块并获得奖励。你可以把奖励的区块看作是存款利息,你在银行存的钱越多,你每月的利息就会越高。
因此,这种共识机制被称为股权证明PoS。
PoS的缺陷是什么?
您可能已经猜到,一个拥有大量令牌的验证者会在创建新块时根据持有的令牌数量获得更高的概率。然而,这与我们在工作量证明中看到的并没有什么不同:比特币矿场变得越来越强大,普通人在自己的电脑上开采多年也未必能获得一个区块。因此,许多人认为,使用了PoS后,区块的分配将更加民主化,因为任何人都可以在自己的笔记本上参与,而不需要建立一个巨大的采矿平台,他们不需要昂贵的硬件,只需要一定的筹码,就算筹码不多,也有一定概率能获得区块的记账权,希望总是有的,你说呢?
从技术和经济的角度来看,还有其他不利因素。我们不会一一介绍,但这里有一个很好的介绍。在实际应用中,PoS和PoW都有自己的优点和缺点,因此以太坊的Casper具有两者混合的特征。
像往常一样,了解PoS的方法是编写自己的代码,那么,我们开始吧!
编写PoS代码
我们建议在继续之前看一下200行Go代码编写区块链Part2,因为在接下来的文章中,一些基础知识不再会介绍,因此这篇文章能帮助你回顾一下。
注意
我们将实现PoS的核心概念,然后因为文章长度有限,因此一些不必要的代码奖省去!
- P2P网络的实现。文中的网络是模拟的,区块链状态只在其中一个中心化节点持有,而不是每个节点,同时状态通过该持有节点广播到其它节点
- 钱包和余额变动。本文没有实现一个钱包,持有的令牌数量是通过stdin(标准输入)输入的,你可以输入你想要的任何数量。一个完整的实现会为每个节点分配一个hash地址,并在节点中跟踪余额的变动
架构图
- 我们将有一个中心化的TCP服务节点,其他节点可以连接该服务器
- 最新的区块链状态将定期广播到每个节点
- 每个节点都能提议建立新的区块
- 基于每个节点的令牌数量,其中一个节点将随机地(以令牌数作为加权值)作为获胜者,并且将该区块添加到区块链中
设置和导入
在开始写代码之前,我们需要一个环境变量来设置TCP服务器的端口,首先在工作文件夹中创建.env文件,写入一行配置:
ADDR=9000
我们的Go程序将读取该文件,并且暴露出9000端口。同时在工作目录下,再创建一个main.go文件。
package main
import (
"bufio"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net"
"os"
"strconv"
"sync"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/joho/godotenv"
)
spew
可以把我们的区块链用漂亮的格式打印到终端terminal中godotenv
允许我们从之前创建的.evn文件读取配置
快速脉搏检查
如果你读过我们的其他教程,就会知道我们是一家医疗保健公司,目前要去收集人体脉搏信息,同时添加到我们的区块上。把两个手指放在你的手腕上,数一下你一分钟能感觉到多少次脉搏,这将是您的BPM整数,我们将在接下来的文章中使用。
全局变量
现在,让我们声明我们需要的所有全局变量(main.go中)。
// Block represents each 'item' in the blockchain
type Block struct {
Index