区块链共识笔记 --- POW(bitcoin)

接触区块链,大都是从比特币开始,从比特币的特性到源码实现学习,POW是重点中的重点,而且POW也是以太坊的共识算法,可以说POW是真正的随机选取节点的共识算法,下面就以比特币为例,学习下POW共识算法

POW:proof of work, 翻译过来是工作量证明,即用一定量的工作量来换取记账权利,再比特币中,就是计算一个数学难题,谁先算出来并广播出来,谁就有权利记账,具体的:

区块构成:

区块由BlockHeader + Transaction list构成

nVersion: 版本号

hashPrevBlock: 父区块哈希,即SHA256(SHA256(父区块头))

hashMerkleRoot:  Transhaction list的Merkle树根哈希,可以快速验证某个交易是否存在某个区块中

nTime: 时间戳,区块产生时间

nBits: 区块的难度目标,32bit,其中高8bit是指数参数 c,底24bit是底数a, 由此计算出难度值就是a * 2^(8*(c -3))

nNonce: 就是工作量证明里寻找的随机值,

这样POW算法就是计算SHA256(SHA256(区块头)) ,寻找1个nonce,是的最后的哈希值小于上面计算的难度值

bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
    bool fNegative;
    bool fOverflow;
    arith_uint256 bnTarget;

    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    // Check range
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
        return false;

    // Check proof of work matches claimed amount
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}

难度值是动态调整的,进而可以控制找到nonce的时间,也就控制了出块时间,难度值的调整算法是根据前面2016个区块的实际时间跟理论时间(理论上1个区块是10分钟,则2016块就是20160分钟)的比值来调剂,实际时间短,即出块速度快,就增大难度,使得后面出块速度慢下来,反之加快出块速度,具体看算法

unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
    assert(pindexLast != nullptr);
    unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();

    // Only change once per difficulty adjustment interval
    // 判断是否是新的2016个区块 不等于0 即还在1个2016区块调节步长内,继续用父区块的nBits
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
    {
        if (params.fPowAllowMinDifficultyBlocks)
        {
            // Special difficulty rule for testnet:
            // If the new block's timestamp is more than 2* 10 minutes
            // then allow mining of a min-difficulty block.
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                // Return the last non-special-min-difficulty-rules-block
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        return pindexLast->nBits;
    }

    //等于0,即开始进入新的1个2016步长
    // Go back by what we want to be 14 days worth of blocks
    int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
    assert(nHeightFirst >= 0);
    const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
    assert(pindexFirst);
    
    //计算新的nBits
    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}

unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
    if (params.fPowNoRetargeting)
        return pindexLast->nBits;

    // Limit adjustment step
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
    arith_uint256 bnNew;
    //根据父区块的nBits计算难度值
    bnNew.SetCompact(pindexLast->nBits);
    // 进行调节
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;

    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;
    //转成nBits形式
    return bnNew.GetCompact();
}

 这样由区块的构成元素,挖矿的计算过程,难度的调节算法可以总结比特币挖矿的的整个过程:

1. 构建区块,包括将交易池中的交易打包进区块的交易列表中,区块头填充各个参数(父区块hash,merkle树根hash,时间戳,版本号)

2. 判断目标值是否调整,确定nBits

3. 挖矿,寻找nonce,计算区块头的hash值,指导找到一个nonce,使得hash值,小于难度值

调用python的hashlib库模拟了个简单的pow算法

import hashlib
import time

class Block:

	def __init__(self, index, timestamp, tx, parentHash=""):
		self.index = index
		self.parentHash = parentHash
		self.tx = tx
		self.timestamp = timestamp
		self.nonce = 1
		self.hash = self.calculateHash()

	def calculateHash(self):
		sha256 = hashlib.sha256()
		string = str(self.index) + str(self.parentHash) + str(self.tx) + str(self.timestamp) + str(self.nonce)
		sha256.update(string.encode('utf-8'))
		return sha256.hexdigest()

	def makeNewBlock(self, difficulty):
		while(self.hash[0:difficulty] != str(0).zfill(difficulty)):
			self.nonce += 1
			self.hash = self.calculateHash()

class BlockChain:

	def __init__(self):
		self.chain = [Block(0, "12/22/2018", "genesis block")]
		self.difficulty = 5;

	def addBlock(self, newBlock):
		newBlock.parentHash = self.chain[len(self.chain) - 1].hash
		newBlock.makeNewBlock(self.difficulty)
		self.chain.append(newBlock)


testBlockChain = BlockChain()

firstBlockStartTime = time.time()
print("the first block time is: " + str(firstBlockStartTime))

testBlockChain.addBlock(Block(1, "12/23/2018", "{tx:10}"))

firstBlockEndTime = time.time()
print("time spend for first block is: " + str(firstBlockEndTime -  firstBlockStartTime))

secondBlockStartTime = time.time()
print("the second block time is: " + str(secondBlockStartTime))

testBlockChain.addBlock(Block(1, "12/24/2018", "{tx:20}"))

secondBlockEndTime = time.time()
print("time spend for second block is: " + str(secondBlockEndTime -  secondBlockStartTime))

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值