这可能是全网最简单的POS共识机制算法

网友投稿 315 2022-11-29

这可能是全网最简单的POS共识机制算法

共识算法是区块链非常重要的一种算法,简单来说,共识算法就是为了使网络中的节点得到一致的认可。就比如说合作双方达成一致,共同遵守某个合约。

传统的分布式系统中,由于有着中心服务器进行统一管理,各个子服务器只需要跟着中心服务器进行操作即可。而区块链作为一种去中心化分布式系统,并没有传统意义上的中心服务器,因此传统的分布一致性算法并不适用于区块链网络。每个服务器都可以是中心服务器,那么这样就需要一种新的算法来维护系统中的一致性了。现在区块链中,主流的共识算法一般为三种:POW,POS和DPOS。

工作量证明(Proof Of Work)

工作量证明算法之前已经提到过,在实现微型区块链时用的就是pow算法,这是最传统最经典的共识算法,应用在比特币中。它的原理就是寻找一个以若干个0为开头的哈希串。举个例子,给定字符串“blockchain”,我们给出的工作量要求是,可以在这个字符串后面连接一个称为nonce的整数值串,对连接后的字符串进行sha256哈希运算,如果得到的哈希结果(以十六进制的形式表示)是以若干个0开头的,则验证通过。为了达到这个工作量证明的目标,我们需要不停地递增nonce值,对得到的新字符串进行sha256哈希运算。

原理非常的简单,也可以说是只需要算就行了。

这样也存在一个问题,那就是它只要求算力,不要求理解区块链的本身,也就造成了现如今比特币的大量矿机无脑挖矿。就像演唱会的黄牛,买票只为了获取它的利益,并不在意演唱会本身的内容。为了避免这种靠纯运算的共识机制,相继提出来很多新的共识算法。

权益证明(Proof Of Stake)

今天重点讲讲POS机制。

权益证明,它提出来币龄的概念,币龄 = 持有的货币数 * 持有时间,比如说我有100币,持有了30天,那么我的币龄就是3000。币龄越大的节点呢获取记账权(也就是生成区块)的概率越大。每次记完账之后,该节点的币龄就清空。当然,刚获取的币不能直接参与币龄计算,一般是30天之后开始计算币龄。

那么这样会产生一个问题:囤币来获取绝对记账权。为了防止这种情况发生,会设置一个最大概率,一般设置90天时获得最大记账概率,之后不再增加。

但是POS本质上还是类似POW的暴力计算,只不过币多的人更有可能挖到矿,降低了POW的全网一致难度,谁算力强谁记账的局限性。

具体来看代码怎么实现它的吧:

导入所需包:

package mainimport ( "bytes" "crypto/sha256" "encoding/binary" "fmt" "math" "math/big" "math/rand" "time")

定义常量设置最大最小概率,这里由于不可能等30天之后再计算币龄,我设置10秒之后开始计算,并且防止数据过大,按分钟计时:

const ( dif = 2 INT64_MAX = math.MaxInt64 MaxProbably = 255 MinProbably = 235 MaxCoinAge = 10 Minute = 60)

定义币的数据结构和一个币池,这里币中提出了地址Address概念,一般理解为钱包地址,一般一个钱包属于一个用户,表明这个币的所有权是这个地址对应的用户:

type Coin struct { Time int64 Num int Address string}var CoinPool []Coin

定义区块和链:

type Block struct { PrevHash []byte Hash []byte Data string Height int64 Timestamp int64 Coin Coin Nonce int Dif int64}type BlockChain struct { Blocks []Block}

init函数,设置随机数种子和币池初始化,会在main函数开始前自动执行:

func init() { rand.Seed(time.Now().UnixNano()) CoinPool = make([]Coin, 0)}

生成创世块,传入的参数是区块上的数据data和挖矿地址addr,这里每个区块的币随机给出1到5,币池增加创世块的币:

func GenesisBlock(data string, addr string) *BlockChain { var bc BlockChain bc.Blocks = make([]Block, 1) newCoin := Coin{ Time: time.Now().Unix(), Num: 1 + rand.Intn(5), Address: addr, } bc.Blocks[0] = Block{ PrevHash: []byte(""), Data: data, Height: 1, Timestamp: time.Now().Unix(), Coin: newCoin, Nonce: 0, } bc.Blocks[0].Hash, bc.Blocks[0].Nonce, bc.Blocks[0].Dif = ProofOfStake(dif, addr, bc.Blocks[0]) CoinPool = append(CoinPool, newCoin) return &bc}

生成新区块函数,还是一样,prevHash对应上一个节点的Hash,随机给出1-5币奖励,记录该币的所有者地址addr,币池增加新区块的币:

func GenerateBlock(bc *BlockChain, data string, addr string) { prevBlock := bc.Blocks[len(bc.Blocks)-1] newCoin := Coin{ Time: time.Now().Unix(), Num: 1 + rand.Intn(5), Address: addr, } b := Block{ PrevHash: prevBlock.Hash, Data: data, Height: prevBlock.Height + 1, Timestamp: time.Now().Unix(), } b.Hash, b.Nonce, b.Dif = ProofOfStake(dif, addr, b) b.Coin = newCoin bc.Blocks = append(bc.Blocks, b) CoinPool = append(CoinPool, newCoin)}

权益证明算法:

POS机制的核心算法了,设置的是10s之后开始计算币龄,根据币龄coinAge调整当前难度Dif以获取真实难度realDif。挖矿还是类似POW寻找一个nonce加在尾部,寻找到第一个满足真实难度realDif的sha256哈希串:

func IntToHex(num int64) []byte { buff := new(bytes.Buffer) err := binary.Write(buff, binary.BigEndian, num) if err != nil { panic(err) } return buff.Bytes()}func ProofOfStake(dif int, addr string, b Block) ([]byte, int, int64) { var coinAge int64 var realDif int64 realDif = int64(MinProbably) curTime := time.Now().Unix() for k, i := range CoinPool { if i.Address == addr && i.Time+MaxCoinAge < curTime { //币龄增加, 并设置上限 var curCoinAge int64 if curTime-i.Time < 3*MaxCoinAge { curCoinAge = curTime - i.Time } else { curCoinAge = 3 * MaxCoinAge } coinAge += int64(i.Num) * curCoinAge //参与挖矿的币龄置为0 CoinPool[k].Time = curTime } } if realDif+int64(dif)*coinAge/Minute > int64(MaxProbably) { realDif = MaxProbably } else { realDif += int64(dif) * coinAge / Minute } target := big.NewInt(1) target.Lsh(target, uint(realDif)) nonce := 0 for ; nonce < INT64_MAX; nonce++ { check := bytes.Join( [][]byte{ b.PrevHash, []byte(b.Data), IntToHex(b.Height), IntToHex(b.Timestamp), IntToHex(int64(nonce)), }, []byte{}) hash := sha256.Sum256(check) var hashInt big.Int hashInt.SetBytes(hash[:]) if hashInt.Cmp(target) == -1 { return hash[:], nonce, 255 - realDif } } return []byte(""), -1, 255 - realDif}

再写个打印区块和打印币池函数:

func Print(bc *BlockChain) { for _, i := range bc.Blocks { fmt.Printf("PrevHash: %x\n", i.PrevHash) fmt.Printf("Hash: %x\n", i.Hash) fmt.Println("Block's Data: ", i.Data) fmt.Println("Current Height: ", i.Height) fmt.Println("Timestamp: ", i.Timestamp) fmt.Println("Nonce: ", i.Nonce) fmt.Println("Dif: ", i.Dif) }}func PrintCoinPool() { for _, i := range CoinPool { fmt.Println("Coin's Num: ", i.Num) fmt.Println("Coin's Time: ", i.Time) fmt.Println("Coin's Owner: ", i.Address) }}

写个main函数测试看看,虚拟两个地址,当然啊真实区块链的地址不可能这么简单,需要使用公钥生成地址算法。

给addr1先记账,获取一定币,等待可以计算币龄时再次记账,再给新地址addr2记账,看看难度对比:

func main() { addr1 := "192.168.1.1" addr2 := "192.168.1.2" bc := GenesisBlock("reigns", addr1) GenerateBlock(bc, "send 1$ to alice", addr1) GenerateBlock(bc, "send 1$ to bob", addr1) GenerateBlock(bc, "send 2$ to alice", addr1) time.Sleep(11 * time.Second) GenerateBlock(bc, "send 3$ to alice", addr1) GenerateBlock(bc, "send 4$ to alice", addr2) Print(bc) PrintCoinPool()}

看看打印结果吧:

可以看到啊,addr1记前四笔账的时候难度都是20,第五笔的时候,由于之前延迟了11秒,开始计算币龄,难度减小到了15,第六笔账由addr2记账,难度又回到了20,同时看币池,参与币龄计算的币的Time统一到第5笔记账时间,也就是刷新了币龄。

当然啊,真实环境中肯定不是顺序记账,每个节点都会竞争记账权,最先计算出满足难度的hash串的节点优先记账,并向全网广播,开始下一次记账权争夺。

好了,今天到这里了~下次讲讲更有效率的共识算法,也是三大主流算法的最后一个:DPOS。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:LeetCode-204. Count Primes
下一篇:java实用型
相关文章

 发表评论

暂时没有评论,来抢沙发吧~