diff --git a/src/Makefile.am b/src/Makefile.am index 74e78a276..8ab695d44 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 650635a76..6d6b250ac 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -9,6 +9,10 @@ #include #include +/** 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) */ diff --git a/src/fork.h b/src/fork.h new file mode 100644 index 000000000..5a07d758c --- /dev/null +++ b/src/fork.h @@ -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 diff --git a/src/miner.cpp b/src/miner.cpp index d7db49440..406b7ae08 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -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 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]); diff --git a/src/miner.h b/src/miner.h index ed1b4434f..c81bbb60e 100644 --- a/src/miner.h +++ b/src/miner.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_MINER_H #define BITCOIN_MINER_H +#include #include #include #include @@ -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 diff --git a/src/pow.cpp b/src/pow.cpp index 929ea26a0..53aac8c31 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -7,63 +7,107 @@ #include #include +#include +#include +#include #include +#include #include +#include +#include -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) +#include + +#ifdef ENABLE_RUST +#include +#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; +} diff --git a/src/pow.h b/src/pow.h index b8cc34926..8d7f5a447 100644 --- a/src/pow.h +++ b/src/pow.h @@ -6,18 +6,23 @@ #ifndef BITCOIN_POW_H #define BITCOIN_POW_H -#include +#include #include 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 diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index a024ffef6..bad995840 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -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 diff --git a/src/validation.cpp b/src/validation.cpp index 9791d6e2d..e992a38e7 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -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