原理

工作量证明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
}

挖矿成功,区块生成

运行过程