本文转载自微信公众号《区块链研究实验室》,作者连三丰。转载本文请联系区块链研究实验室公众号。随着区块链彻底改变世界市场,在做出预测之前了解基础知识至关重要。在本文中,我们将探讨股权证明的基础知识,这是一种区块链协议,类似于在区块链中锻造新区块的彩票方法。本文的主要目标如下:了解区块链空间的当前性能趋势。通过GoLang中的工作示例学习权益证明。提升您的计算机科学和围棋编程技能。这将是一个有趣的过程,让我们开始编码吧。了解权益证明权益证明(PoS)的基础实际上非常简单。当然,这个系统的核心组成部分是区块链本身。简而言之,区块链是一个不可变的分类账,其中每个单独的块都是根据以前的块以加密方式构建的。您永远无法更改区块链的任何部分,因为连接到网络的每个人都可以轻松看到更改并反驳您的区块链版本。创建新块的过程由您的区块链协议定义,比特币建立在工作量证明(PoW)协议之上,其中需要越来越多的计算能力通过数学过程验证以前的交易,因此每次您验证列表区块中包含的交易,您将获得比特币奖励。所以交易历史的证明就在于你做了多少工作,而做这些工作的人被称为“矿工”。PoW的一个日益严重的问题是随着时间的推移解决这些数学难题需要巨大的计算能力。股权证明从根本上不同,因此您可以在区块链网络上“占用”一定数量的代币(不一定是加密货币),而不是在区块链上拥有计算能力来计算和扩展。通常这可以通过创建您自己的“节点”来实现,以促进您参与区块链生态系统。如果节点采取勤奋的工作态度,你将有更大的机会在区块链中创建新的区块并获得原始回报的奖励。被选中打造下一个区块的可能性也与您在网络上抵押的代币数量成比例增加;如果你懒惰地工作,你的股份可能会受到惩罚甚至被完全撤回。这种奖励和惩罚方法旨在促进区块链中的诚实工作,而不会出现与工作量证明相关的计算可扩展性瓶颈。现在我们对PoS与PoW有了一个概念性的想法,让我们继续用Go编写一个工作的PoS示例。Go导入“区块链”中的权益证明首先,除了定义自定义对象类型外,我们还需要在我们的项目中包含一些Go包。以下是您需要的包——我们将使用math/rand、crypto/sha256和encoding/hex作为加密区块链方法。当然,错误是Go!的标准错误!packagemainimport("crypto/sha256""encoding/hex""errors""fmt""log"math"math/rand""time")后跟我们的自定义数据类型。使用Go使这个超级简单的结构。这里我们有3种自定义类型,第一种是PoSNetwork,我们有一个Blockchain字段,它是对Block结构实例的引用数组。我们将通过BlockchainHead字段跟踪最近添加的块,并且还将有一系列对用作验证器的节点结构的引用。typePoSNetworkstruct{Blockchain[]*BlockBlockchainHead*BlockValidators[]*Node}typeNodestruct{StakeintAddressstring}typeBlockstruct{TimestampstringPrevHashstringHashstringValidatorAddrstring}Node结构将有一个Stake字段,表示它添加到网络中的代币数量。该地址将是一个随机生成的字符串,以便我们可以跟踪哪个节点成功验证了下一个块。最后,Block结构将包含跟踪区块链所需的信息。我们将有一个用于创建块时的时间戳、前一个块的前一个哈希、表示自身的哈希以及Node验证此块的地址-所有类型都是字符串。BuildingBlockchainBricksBrick这是我们构建区块链的第一种方法。首先,在函数签名中,将此方法附加到PoSNetwork结构并将该结构引用为n。然后,我们的节点将对a的引用作为唯一参数。我们将返回一个新的Block引用数组来表示新的Blockchain,对Block的引用将是新的BlockchainHead,并且可能是一个错误。您可以看到在我们尝试添加任何内容之前立即调用了ValidateBlockchain()方法。我们稍后会进行验证,但只要知道我们发现更改了哪个区块链,我们就知道有逻辑来惩罚节点。func(nPo??SNetwork)GenerateNewBlock(Validator*Node)([]*Block,*Block,error){iferr:=n.ValidateBlockchain();err!=nil{Validator.Stake-=10returnn.Blockchain,n.BlockchainHead,err}currentTime:=time.Now().String()newBlock:=&Block{Timestamp:currentTime,PrevHash:n.BlockchainHead.Hash,Hash:NewBlockHash(n.BlockchainHead),ValidatorAddr:Validator.Address,}iferr:=n.ValidateBlockCandidate(newBlock);err!=nil{Validator.Stake-=10returnn.Blockchain,n.BlockchainHead,err}else{n.Blockchain=append(n.Blockchain,newBlock)}returnn.Blockchain,newBlock,nil}in检查区块链完好无损后,我们得到系统的当前时间,在实例化new时将其存储为TimestampTimeBlock。我们也附上了前面的Hash值,你可以很方便的访问到BlockchainHead。然后,我们将在NewBlockHash()上调用BlockchainHead方法,并将作为节点输入的地址分配给我们的验证器地址。填充新块的字段后,我们在新块上调用ValidateBlockCandidate()以查看是否有任何错误。如果是这样,我们返回一个层。如果没有,我们会将新块附加到区块链。但是,NewBlockHash()是如何工作的?我们有两个函数来完成为每个块创建唯一哈希的任务。函数NewBlockHash()只是获取了Block的所有信息,并将其拼接成一个字符串传递给newHash()。funcNewBlockHash(block*Block)string{blockInfo:=block.Timestamp+block.PrevHash+block.Hash+block.ValidatorAddrreturnnewHash(blockInfo)}funcnewHash(sstring)string{h:=sha256.New()h.Write([]byte(s))hashed:=h.Sum(nil)returnhex.EncodeToString(hashed)}接下来,newHash()将利用crypto/sha256包创建一个存储为h的新SHA256对象。然后我们将输入字符串s转换为字节数组并将其写入h。最后,我们使用h.Sum()将h转换为我们可以调用hex.EncodeToString()的格式,这样我们就有了一个用于最终输出的字符串。验证我们的区块链Blockchain如果你不能验证它是没有用的。在这里,我们将ValidateBlockchain()方法附加到PoSNetwork结构并返回可能的错误。如果区块链是空的或者只有一个区块,我们不能确定它是正确的所以我们只返回nil。接下来,我们评估区块链中每对区块之间的三个独立条件。第一个检查是前一个块的哈希值是否等于当前块为其前一个哈希值存储的值。func(nPo??SNetwork)ValidateBlockchain()错误{iflen(n.Blockchain)<=1{returnnil}currBlockIdx:=len(n.Blockchain)-1prevBlockIdx:=len(n.Blockchain)-2forprevBlockIdx>=0{currBlock:=n.Blockchain[currBlockIdx]prevBlock:=n.Blockchain[prevBlockIdx]ifcurrBlock.PrevHash!=prevBlock.Hash{returnerrors.New("blockchainhasinconsistenthashes")}ifcurrBlock.Timestamp<=prevBlock.Timestamp{returnerrors.New("blockchainhasinconsistenthashes")}ifNewBlockHash(prevBlock)!=currBlock.Hash{returnerrors.New("blockchainhasinconsistenthashgeneration")}currBlockIdx--prevBlockIdx--}returnnil}我们还检查是否在任何时候一个块的时间戳比当前块更新。如果当前的Block是在2020年制作的,而之前的Blot是在2021年制作的,那么你就知道出了问题。最后,我们要直接计算Hash之前的block,还是会检索到Hash的当前Block。如果满足这些条件中的任何一个,我们将返回错误,因为我们的区块链处于被篡改的状态。现在我们已经验证了整个区块链,我们需要确保下一个要添加的Block也是有效的。遵循与上述相同的条件检查,仅针对添加的单个新块。func(nPo??SNetwork)ValidateBlockCandidate(newBlock*Block)error{ifn.BlockchainHead.Hash!=newBlock.PrevHash{returnerrors.New("blockchainHEADhashisnoteequaltonewblockprevioushash")}ifn.BlockchainHead.Timestamp>=newBlock.Timestamp{returnerrorstoblockoreat"HEADblockchaingr)}ifNewBlockHash(n.BlockchainHead)!=newBlock.Hash{returnerrors.New("newblockhashofblockchainHEADdoesnotequalnewblockhash")}returnnil}现在我们已经完成了向区块链添加新块并验证其正确性的步骤。那么,我们如何决定何时添加一个新区块?这就是我们的验证器发挥作用的地方,对于每个在网络中有权益的节点,我们将通过抽签的方式随机选择一个节点来锻造下一个区块并获得奖励。首先,我们首先需要一个节点。为了向我们的PoSNetwork添加一个新节点,我们调用NewNode()接受初始权益节点并返回一个新的节点引用数组。这里没什么特别的,我们只是附加到n.Validators数组并调用randAddress()到G为新节点生成一个唯一地址。func(nPo??SNetwork)NewNode(stakeint)[]*Node{newNode:=&Node{Stake:stake,Address:randAddress(),}n.Validators=append(n.Validators,newNode)returnn.Validators}funcrandAddress()string{b:=make([]byte,16)_,_=math.Read(b)returnfmt.Sprintf("%x",b)}那么,我们实际上如何选择获胜者呢?一点点概率和统计!在SelectWinner()方法中,我们首先按覆盖范围找到网络中持有的所有权益n.Validators。我们还将所有权益大于零的节点添加到数组winnerPool中以供可能的选择。如果发现winnerPool为空,则返回错误。.然后我们使用Intn()方法选择一个中奖号码,该方法将选择一个介于0和我们的总赌注之间的随机数。func(nPo??SNetwork)SelectWinner()(*Node,error){varwinnerPool[]*NodetotalStake:=0for_,node:=rangen.Validators{ifnode.Stake>0{winnerPool=append(winnerPool,node)totalStake+=node.Stake}}ifwinnerPool==nil{returnnil,errors.New("therarerenonodeswithstakeinthenetwork")}winnerNumber:=math.Intn(totalStake)tmp:=0for_,node:=rangen.Validators{tmp+=node.StakeifwinnerNumber
