工作量证明(proof-of-work)
哈希计算
获得指定数据的一个哈希值的过程,就叫做哈希计算。一个哈希,就是对所计算数据的一个唯一表示。对于一个哈希函数,输入任意大小的数据,它会输出一个固定大小的哈希值。下面是哈希的几个关键特性:
- 无法从一个哈希值恢复原始数据。也就是说,哈希并不是加密。
- 对于特定的数据,只能有一个哈希,并且这个哈希是唯一的。
- 即使是仅仅改变输入数据中的一个字节,也会导致输出一个完全不同的哈希。
Hashcash
比特币使用 Hashcash ,它可以被分解为以下步骤:
- 取一些公开的数据(在比特币中,它是区块头)
- 给这个公开数据添加一个计数器。计数器默认从 0 开始
- 将 data(数据) 和 counter(计数器) 组合到一起,获得一个哈希
- 检查哈希是否符合一定的条件:
- 如果符合条件(前多少位是0),结束
- 如果不符合,增加计数器,重复步骤 3-4
实现
//挖矿难度,前24位都是0,用16进制的话则是前6位都是0
const targetBits = 24
我们并不会像比特币那样动态调整难度,就是说targetBits是常量
//构造一个pow的结构体去实现功能
//我们使用bitInt,在对比hash值的时候,转化成数字比大小
type ProofOfWork struct {
block *Block
target *big.Int
}
func NewProofOfWork(b *Block) *ProofOfWork {
target := big.NewInt(1)
//0000010000000000000000000...
//左移256-targetBits位
target.Lsh(target, uint(256-targetBits))
pow := &ProofOfWork{b, target}
return pow
}
//准备数据
//data = PrevBlockHash + blockData(交易的hash值) + Timestamp + targetBits + nonce
func (pow *ProofOfWork) prepareData(nonce int) []byte {
data := bytes.Join(
[][]byte{
pow.block.PrevBlockHash,
pow.block.Data,
IntToHex(pow.block.Timestamp),
IntToHex(int64(targetBits)),
IntToHex(int64(nonce)),
},
[]byte{},
)
return data
}
//pow运算
//1. hash = hash(PrevBlockHash + blockData + Timestamp + targetBits + nonce)
//2. 如果hash < target 则成功
//3. 否则 nonce++ 重复运算
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0
fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data)
for nonce < maxNonce {
//计算hash
data := pow.prepareData(nonce)
hash = sha256.Sum256(data)
hashInt.SetBytes(hash[:])
//这里对比hash值和目标值的大小,如果小于,则pow成功
if hashInt.Cmp(pow.target) == -1 {
fmt.Printf("\r%x", hash)
break
} else {
nonce++
}
}
fmt.Print("\n\n")
return nonce, hash[:]
}
//pow验证
func (pow *ProofOfWork) Validate() bool {
var hashInt big.Int
data := pow.prepareData(pow.block.Nonce)
hash := sha256.Sum256(data)
hashInt.SetBytes(hash[:])
isValid := hashInt.Cmp(pow.target) == -1
return isValid
}
func main() {
...
for _, block := range bc.blocks {
...
pow := NewProofOfWork(block)
fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate()))
fmt.Println()
}
}