共识算法之POW
简介
POW是proof-of-work的缩写,中译为:工作量证明,是比特币中采用的共识机制,也被许多公有区块链系统所采用(比如以太坊)。工作量证明机制基础是哈希运算,因此要理解pow首先要明白哈希函数(比特币大量采用了sha256,以及rimped160)。
本篇文章重点介绍pow共识算法的原理,不具体介绍比特币的相关细节。
什么是哈希函数?
哈希运算是密码学的重要组成部分,一般用来保护数据的完整性。给定一段消息,通过哈希函数可以将消息映射为固定长度的哈希值(比如sha256,将任意长度的消息映射为256位的哈希值)。哈希函数具有两个重要的特点:“无碰撞“和”不可逆“。
1. 所谓不可逆,就是当你知道x的HASH值,无法求出x;因此哈希函数在数学上是一种单向函数。
2. 所谓无冲突,就是当你知道x,无法求出一个y, 使x与y的HASH值相同。简单来说就是,无法找到一个不同的消息y,使得y的哈希值与x的哈希值相同。(当然,因为消息的比特空间 << 所求哈希值的比特空间,因此其实这样的y是存在的,只是这样的y很难找,只是一个概率上的不可能)
哈希函数的应用
从哈希函数的特性我们可以看出,当x被改变时,x所对应的哈希值也会发生改变,因此可以通过一段消息的哈希值是否改变来间接反应消息是否被修改,也就是我们说的数据完整性验证。(当然这个一般还要也公钥密码体制相结合来使用,这里不解释公钥密码体制,自行google)。
### 在比特币中的使用
比特币主要使用了sha256哈希函数,有三个用途:
1. 比特币地址是公钥的两次sha256运算结果
2. 保证每笔交易的完整性
3. pow工作量证明机制的基础
比特币共识算法
pow工作量证明机制,我们从“工作量”和“证明”两个概念上来理解pow。
1. 什么是工作量?
比特币一直被人诟病做了大量无意义的运算,耗费了巨量的电能。这里的运算就是比特币共识机制中的工作量,而这个运算就是哈希运算。具体是下面的运算:
SHA256(SHA256(Block_Header))
这里的Block_Header可以简单理解为:一堆比特币交易txs + 一个随机数数nonce。在生成比特币区块的时候,我们可以认为txs是固定的,因此pow可以简化为:选取随机数nonce,然后求SHA256(SHA256(txs || nonce)),我们下面把这个计算叫做POWHash。
这个不断重复选取随机数nonce,计算POWHash的过程什么时候结束呢?在比特币中POWHash的前n位为0的时候,我们认为找到了一个有效的nonce,然后就可以生成比特币区块并广播给其他的矿工。这里的n是一个可调节的数,比特币中没生成2016个块,会重新调整n,n越大有效的POWHash越难计算,相反越容易。
2. 怎么证明?
pow中的证明值得是验证nonce是否有效,同样是计算SHA256(SHA256(txs || nonce)),不过这里的txs和nonce都是已知的,我们把运算结果记为POWHash‘,我们只需要验证POWHash’是否满足前n为0,就可以证明这个区块是不是一个有效的比特币区块。
通过分析我们发现,如果要伪造交易(修改txs),需要重新找到一个nonce,因此对于非法修改区块,需要重新进行大量哈希运算来找到一个有效的nonce。由于每个区块都包含前一个区块的hash值,因此后面所有的区块都需要重新进行调整。因此修改比特币中的交易信息是计算上不可能的。
--------------------------------Talk is cheap, show me your code-------------------------------
下面是简单的pow实现,仅供参考
var ( target, _ = new(big.Int).SetString("ffffffffffffffff", 16) //64bits ) func pow(merkelRootHash, previousHash []byte, difficulty int, h hash.Hash) { target = target.Rsh(target, uint(difficulty)) //variable to computed. 1. from the beginning 0, 2. random a nounce. Here we choose 1. nounce := new(big.Int).SetInt64(0) for { //time stamp now := fmt.Sprintf("%v", time.Now()) var header []byte header = append(header, []byte(now)...) header = append(header, previousHash...) header = append(header, merkelRootHash...) header = append(header, nounce.Bytes()...) h.Write(header) res := new(big.Int).SetBytes(h.Sum(nil)[:8]) //substract first 8*8 bits from sha256 if res.Cmp(target) < 0 { fmt.Printf("%b\n", nounce) //found a valid nonce, print it. break } nounce.Add(nounce, new(big.Int).SetUint64(1)) } }
----------------------------------End Line-------------------------------------------------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?