[pow] Update pow to equihash, include fork related changes

This commit is contained in:
jc 2018-04-08 00:43:25 -04:00
parent cb9e6bbf58
commit ff894a8f09
9 changed files with 170 additions and 52 deletions

View File

@ -130,6 +130,7 @@ BITCOIN_CORE_H = \
core_io.h \
core_memusage.h \
cuckoocache.h \
fork.h \
fs.h \
httprpc.h \
httpserver.h \
@ -241,6 +242,7 @@ libbitcoin_server_a_SOURCES = \
chain.cpp \
checkpoints.cpp \
consensus/tx_verify.cpp \
fork.cpp \
httprpc.cpp \
httpserver.cpp \
index/txindex.cpp \

View File

@ -9,6 +9,10 @@
#include <stdlib.h>
#include <stdint.h>
/** The minimum allowed block version (network rule) */
static const int32_t MIN_BLOCK_VERSION = 4;
/** The minimum allowed transaction version (network rule) */
static const int32_t MIN_TX_VERSION = 1;
/** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */
static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 4000000;
/** The maximum allowed weight for a block, see BIP 141 (network rule) */

27
src/fork.h Normal file
View File

@ -0,0 +1,27 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2018 The Bitcoin Private developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BTCP_FORK_H
#define BTCP_FORK_H
static const uint256 forkExtraHashSentinel = uint256S("f0f0f0f0fafafafaffffffffffffffffffffffffffffffffafafafaf0f0f0f0f");
inline bool isForkBlock(int nHeight, int forkStartHeight, int forkHeightRange)
{
return (nHeight > forkStartHeight && nHeight <= forkStartHeight + forkHeightRange);
}
inline bool looksLikeForkBlockHeader(const CBlockHeader& header)
{
return header.hashReserved == forkExtraHashSentinel;
}
inline bool isForkEnabled(int nHeight, int forkStartHeight)
{
return nHeight > forkStartHeight;
}
#endif

View File

@ -37,7 +37,7 @@
uint64_t nLastBlockTx = 0;
uint64_t nLastBlockWeight = 0;
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
int64_t UpdateTime(CBlockHeader* pblock, const CChainParams& chainparams, const CBlockIndex* pindexPrev)
{
int64_t nOldTime = pblock->nTime;
int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
@ -46,8 +46,8 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
pblock->nTime = nNewTime;
// Updating time can change work required on testnet:
if (consensusParams.fPowAllowMinDifficultyBlocks)
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams);
return nNewTime - nOldTime;
}
@ -164,8 +164,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
UpdateTime(pblock, chainparams, pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams);
pblock->nNonce = uint256();
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);

View File

@ -6,6 +6,7 @@
#ifndef BITCOIN_MINER_H
#define BITCOIN_MINER_H
#include <chainparams.h>
#include <primitives/block.h>
#include <txmempool.h>
#include <validation.h>
@ -195,6 +196,6 @@ private:
/** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
int64_t UpdateTime(CBlockHeader* pblock, const CChainParams& consensusParams, const CBlockIndex* pindexPrev);
#endif // BITCOIN_MINER_H

View File

@ -7,63 +7,107 @@
#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
#include <crypto/equihash.h>
#include <fork.h>
#include <primitives/block.h>
#include <streams.h>
#include <uint256.h>
#include <util.h>
#include <validation.h>
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
#include <sodium.h>
#ifdef ENABLE_RUST
#include <librustzcash.h>
#endif // ENABLE_RUST
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const CChainParams& chainparams)
{
assert(pindexLast != nullptr);
unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();
// Only change once per difficulty adjustment interval
if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
const Consensus::Params& params = chainparams.GetConsensus();
int nHeight = pindexLast->nHeight + 1;
arith_uint256 proofOfWorkLimit;
if(!isForkEnabled(nHeight, chainparams.ForkStartHeight()))
proofOfWorkLimit = UintToArith256(params.prePowLimit);
else
proofOfWorkLimit = UintToArith256(params.powLimit);
unsigned int nProofOfWorkLimit = proofOfWorkLimit.GetCompact();
unsigned int nProofOfWorkBomb = UintToArith256(uint256S("000000000000000000000000000000000000000000000000000000000000ffff")).GetCompact();
bool is_fork = isForkBlock(nHeight, chainparams.ForkStartHeight(), chainparams.ForkHeightRange());
bool was_fork = isForkBlock(nHeight - params.nPowAveragingWindow, chainparams.ForkStartHeight(), chainparams.ForkHeightRange());
// Genesis block
if (pindexLast == NULL)
return nProofOfWorkLimit;
// right at fork
else if(is_fork && !was_fork)
return nProofOfWorkLimit;
// right post fork
else if(!is_fork && was_fork)
return nProofOfWorkLimit;
// difficulty bomb
else if(pindexLast->nHeight > params.nPowDifficultyBombHeight)
return nProofOfWorkBomb;
// testnet only
else if (params.fPowAllowMinDifficultyBlocks)
{
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;
// 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;
}
// 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);
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;
// Find the first block in the averaging interval
const CBlockIndex* pindexFirst = pindexLast;
arith_uint256 bnTot {0};
for (int i = 0; pindexFirst && i < params.nPowAveragingWindow; i++) {
arith_uint256 bnTmp;
bnTmp.SetCompact(pindexFirst->nBits);
bnTot += bnTmp;
pindexFirst = pindexFirst->pprev;
}
// Check we have enough blocks
if (pindexFirst == NULL)
return nProofOfWorkLimit;
arith_uint256 bnAvg {bnTot / params.nPowAveragingWindow};
bool isFork = isForkBlock(pindexLast->nHeight + 1, chainparams.ForkStartHeight(), chainparams.ForkHeightRange());
return CalculateNextWorkRequired(bnAvg, pindexLast->GetMedianTimePast(), pindexFirst->GetMedianTimePast(), params, proofOfWorkLimit, isFork);
}
unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
int64_t nLastBlockTime, int64_t nFirstBlockTime,
const Consensus::Params& params, const arith_uint256 bnPowLimit, bool isFork)
{
// 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;
// Use medians to prevent time-warp attacks
int64_t nActualTimespan = nLastBlockTime - nFirstBlockTime;
nActualTimespan = params.AveragingWindowTimespan(isFork) + (nActualTimespan - params.AveragingWindowTimespan(isFork))/4;
if (nActualTimespan < params.MinActualTimespan(isFork))
nActualTimespan = params.MinActualTimespan(isFork);
if (nActualTimespan > params.MaxActualTimespan(isFork))
nActualTimespan = params.MaxActualTimespan(isFork);
// Retarget
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
arith_uint256 bnNew;
bnNew.SetCompact(pindexLast->nBits);
arith_uint256 bnNew {bnAvg};
bnNew /= params.AveragingWindowTimespan(isFork);
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan;
if (bnNew > bnPowLimit)
bnNew = bnPowLimit;
@ -89,3 +133,38 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&
return true;
}
bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& params)
{
unsigned int n = params.EquihashN();
unsigned int k = params.EquihashK();
// Hash state
crypto_generichash_blake2b_state state;
EhInitialiseState(n, k, state);
// I = the block header minus nonce and solution.
CEquihashInput I{*pblock};
// I||V
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << I;
ss << pblock->nNonce;
// H(I||V||...
crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());
#ifdef ENABLE_RUST
// Ensure that our Rust interactions are working in production builds. This is
// temporary and should be removed.
{
assert(librustzcash_xor(0x0f0f0f0f0f0f0f0f, 0x1111111111111111) == 0x1e1e1e1e1e1e1e1e);
}
#endif // ENABLE_RUST
bool isValid;
EhIsValidSolution(n, k, state, pblock->nSolution, isValid);
if (!isValid)
return false;
return true;
}

View File

@ -6,18 +6,23 @@
#ifndef BITCOIN_POW_H
#define BITCOIN_POW_H
#include <consensus/params.h>
#include <chainparams.h>
#include <stdint.h>
class CBlockHeader;
class CBlockIndex;
class uint256;
class arith_uint256;
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params&);
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&);
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const CChainParams&);
unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
int64_t nLastBlockTime, int64_t nFirstBlockTime,
const Consensus::Params& params, const arith_uint256 bnPowLimit, bool isFork);
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& params);
#endif // BITCOIN_POW_H

View File

@ -569,7 +569,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
const Consensus::Params& consensusParams = Params().GetConsensus();
// Update nTime
UpdateTime(pblock, consensusParams, pindexPrev);
UpdateTime(pblock, Params(), pindexPrev);
pblock->nNonce = uint256();
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration

View File

@ -3248,7 +3248,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
// Check proof of work
const Consensus::Params& consensusParams = params.GetConsensus();
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, params))
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
// Check against checkpoints