原理
工作量证明POW
工作量证明(Proof-of-Work,PoW)是一种对应服务与资源滥用、或是阻断服务攻击的经济对策。一般是要求用户进行一些耗时适当的复杂运算,并且答案能被服务方快速验算,以此耗用的时间、设备与能源做为担保成本,以确保服务与资源是被真正的需求所使用。此一概念最早由Cynthia Dwork和Moni Naor于1993年的学术论文提出[1],而工作量证明一词则是在1999年由Markus Jakobsson与Ari Juels.[2]所发表。现时此一技术成为了加密货币的主流共识机制之一,如比特币所采用的技术。(百度百科)
工作方式
结合前一个区块哈希值,当前nonce,挖矿难度等数值,不断增加nonce的值算取新的哈希值,直到新的哈希值满足要求(前n位皆为0)即为“挖矿”成功。挖矿难度(target)可以修改。
原理理解
最简单的挖矿过程:
func main() {
//定时器,用于记录开始挖矿时间
start := time.Now()
//设置一个非常大的数字,确保挖矿成功率很高
for i := 0; i < 10000000000000000; i++ {
//将当前循环次数i作为sha256函数输入
data := sha256.Sum256([]byte(strconv.Itoa(i)))
fmt.Printf("%10d, %x\n", i, data)
fmt.Printf("%s\n", string(data[len(data)-3:]))
//如果满足要求:后3位为"000"即为挖矿成功,输出挖矿总时长并退出
if string(data[len(data)-3:]) == "000" {
//计算挖矿总时长
usedtime := time.Since(start)
fmt.Printf("挖矿成功!%d Ms", usedtime)
break
}
}
}
后两位为"00"时挖矿成功用时
后三位为"000"时挖矿成功用时大约 116175552300Ms 挖矿难度和时间呈现指数级增长趋势
原理运用
准备工作
定义最大整数maxNonce用于循环改变nonce值,定义对比位数targetBits用于工作量证明。
var (
maxNonce = math.MaxInt64 //最大的64位整数
)
const targetBits = 24 //对比位数,因为hash值为16进制,每一个数字代表4位,所以最终hash的前24位,即前6个数字都为0
//工作量证明结构体
type ProofOfWork struct {
block *Block //区块
target *big.Int //存储计算哈希对比的特定整数
}
每创建一个新的区块就创建一个工作量证明对象。
//创建一个工作量证明的挖矿对象
func NewProofOfWork(block *Block) *ProofOfWork {
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) //数据转换
pow := &ProofOfWork{block, target} //创建对象
return pow
}
准备数据进行挖矿计算,主要是用于设置新区块的各个参数,使用16进制存储。
//准备数据进行挖矿计算
func (pow *ProofOfWork) prepareData(nonce int) []byte {
data := bytes.Join(
[][]byte{
pow.block.PrevBlockHash, //上一块哈希
pow.block.Data, //当前数据
IntToHex(pow.block.Timestamp), //时间16进制
IntToHex(int64(targetBits)), //位数16进制
IntToHex(int64((nonce))), //保存工作量的nonce
}, []byte{},
)
return data
}
挖矿的执行过程,将准备好的参数进行sha256加密后的值与目标值进行对比,如果满足条件则退出,否则增加nonce的值。计算出满足结果的nonce即为工作量。成功后返回工作量和哈希值。
//挖矿执行
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0
fmt.Printf("当前挖矿计算的数据%s", pow.block.Data)
for nonce < maxNonce {
data := pow.prepareData(nonce) //准备数据
hash = sha256.Sum256(data) //计算出哈希
//fmt.Printf("\r%x", hash) //打印显示哈希
hashInt.SetBytes(hash[:]) //获取要对比的数据
if hashInt.Cmp(pow.target) == -1 { //挖矿的校验:如果hashInt小于pow.target返回-1,即满足前若干位数值全为0
break
} else {
nonce++
}
}
fmt.Println("\n\n")
return nonce, hash[:] //nonce相当于答案,hash代表当前哈希
}
验证挖矿是否成功
//校验挖矿是否真的成功
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) //挖矿的校验:如果hashInt小于pow.target返回-1,即满足前若干位数值全为0
return isValid
}