From f2b719962a75a1e5cf8b29fe236a01309b711ef6 Mon Sep 17 00:00:00 2001 From: timemarkovqtum Date: Mon, 29 Jul 2024 15:45:27 +0200 Subject: [PATCH] Port block assembler --- src/node/blockstorage.cpp | 6 +- src/node/chainstate.cpp | 65 +++++ src/node/interfaces.cpp | 46 +++- src/node/miner.cpp | 500 +++++++++++++++++++++++++++++++++++--- src/node/miner.h | 102 +++++++- 5 files changed, 670 insertions(+), 49 deletions(-) diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index fa59faf8f1..8f0745776a 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -168,8 +169,8 @@ bool BlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, s pindexNew->prevoutStake = diskindex.prevoutStake; pindexNew->vchBlockSigDlgt = diskindex.vchBlockSigDlgt; // qtum - if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) { - return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString()); + if (!CheckIndexProof(*pindexNew, consensusParams)) { + return error("%s: CheckIndexProof failed: %s", __func__, pindexNew->ToString()); } // NovaCoin: build setStakeSeen @@ -648,6 +649,7 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, CBlockInde } pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime); pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); + pindexNew->nStakeModifier = ComputeStakeModifier(pindexNew->pprev, block.IsProofOfWork() ? hash : block.prevoutStake.hash.ToUint256()); pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (best_header == nullptr || best_header->nChainWork < pindexNew->nChainWork) { best_header = pindexNew; diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index bf1fc06b0b..9a8131eab9 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -41,6 +43,9 @@ static ChainstateLoadResult CompleteChainstateInitialization( // new BlockTreeDB tries to delete the existing file, which // fails if it's still open from the previous loop. Close it first: pblocktree.reset(); + pstorageresult.reset(); + globalState.reset(); + globalSealEngine.reset(); pblocktree = std::make_unique(DBParams{ .path = chainman.m_options.datadir / "blocks" / "index", .cache_bytes = static_cast(cache_sizes.block_tree_db), @@ -142,6 +147,62 @@ static ChainstateLoadResult CompleteChainstateInitialization( } } + /////////////////////////////////////////////////////////// qtum + fGettingValuesDGP = options.getting_values_dgp; + + dev::eth::NoProof::init(); + fs::path qtumStateDir = gArgs.GetDataDirNet() / "stateQtum"; + bool fStatus = fs::exists(qtumStateDir); + const std::string dirQtum = PathToString(qtumStateDir); + const dev::h256 hashDB(dev::sha3(dev::rlp(""))); + dev::eth::BaseState existsQtumstate = fStatus ? dev::eth::BaseState::PreExisting : dev::eth::BaseState::Empty; + globalState = std::unique_ptr(new QtumState(dev::u256(0), QtumState::openDB(dirQtum, hashDB, dev::WithExisting::Trust), dirQtum, existsQtumstate)); + const CChainParams& chainparams = Params(); + dev::eth::ChainParams cp(chainparams.EVMGenesisInfo()); + globalSealEngine = std::unique_ptr(cp.createSealEngine()); + + pstorageresult.reset(new StorageResults(PathToString(qtumStateDir))); + if (options.reindex) { + pstorageresult->wipeResults(); + } + + { + LOCK(cs_main); + CChain& active_chain = chainman.ActiveChain(); + if(active_chain.Tip() != nullptr){ + globalState->setRoot(uintToh256(active_chain.Tip()->hashStateRoot)); + globalState->setRootUTXO(uintToh256(active_chain.Tip()->hashUTXORoot)); + } else { + globalState->setRoot(dev::sha3(dev::rlp(""))); + globalState->setRootUTXO(uintToh256(chainparams.GenesisBlock().hashUTXORoot)); + globalState->populateFrom(cp.genesisState); + } + globalState->db().commit(); + globalState->dbUtxo().commit(); + } + + fRecordLogOpcodes = options.record_log_opcodes; + fIsVMlogFile = fs::exists(gArgs.GetDataDirNet() / "vmExecLogs.json"); + /////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// // qtum + if (fAddressIndex != options.addrindex) { + return {ChainstateLoadStatus::FAILURE, _("You need to rebuild the database using -reindex to change -addrindex")}; + } + /////////////////////////////////////////////////////////////// + // Check for changed -logevents state + if (fLogEvents != options.logevents && !fLogEvents) { + return {ChainstateLoadStatus::FAILURE, _("You need to rebuild the database using -reindex to enable -logevents")}; + } + + if (!options.logevents) + { + pstorageresult->wipeResults(); + pblocktree->WipeHeightIndex(); + fLogEvents = false; + pblocktree->WriteFlag("logevents", fLogEvents); + } + if (!options.reindex) { auto chainstates{chainman.GetAll()}; if (std::any_of(chainstates.begin(), chainstates.end(), @@ -252,6 +313,10 @@ ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const C LOCK(cs_main); + CChain& active_chain = chainman.ActiveChain(); + QtumDGP qtumDGP(globalState.get(), chainman.ActiveChainstate(), fGettingValuesDGP); + globalSealEngine->setQtumSchedule(qtumDGP.getGasSchedule(active_chain.Height() + (active_chain.Height()+1 >= chainman.GetConsensus().QIP7Height ? 0 : 1) )); + for (Chainstate* chainstate : chainman.GetAll()) { if (!is_coinsview_empty(chainstate)) { const CBlockIndex* tip = chainstate->m_chain.Tip(); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 7700125ff1..1090daa46f 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -52,6 +52,8 @@ #include #include #include +#include +#include #if defined(HAVE_CONFIG_H) #include @@ -64,6 +66,13 @@ #include +#ifdef ENABLE_WALLET +#include +#include +#include +#include +#endif + using interfaces::BlockTip; using interfaces::Chain; using interfaces::FoundBlock; @@ -362,6 +371,13 @@ class NodeImpl : public Node } void getGasInfo(uint64_t& blockGasLimit, uint64_t& minGasPrice, uint64_t& nGasPrice) override { + LOCK(::cs_main); + + QtumDGP qtumDGP(globalState.get(), chainman().ActiveChainstate(), fGettingValuesDGP); + int numBlocks = chainman().ActiveChain().Height(); + blockGasLimit = qtumDGP.getBlockGasLimit(numBlocks); + minGasPrice = CAmount(qtumDGP.getMinGasPrice(numBlocks)); + nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE; } void getSyncInfo(int& numBlocks, bool& isSyncing) override { @@ -395,11 +411,13 @@ class NodeImpl : public Node } uint64_t getNetworkStakeWeight() override { - return {}; + LOCK(::cs_main); + return GetPoSKernelPS(chainman()); } double getEstimatedAnnualROI() override { - return {}; + LOCK(::cs_main); + return GetEstimatedAnnualROI(chainman()); } int64_t getMoneySupply() override { @@ -408,7 +426,7 @@ class NodeImpl : public Node } double getPoSKernelPS() override { - return {}; + return GetPoSKernelPS(chainman()); } std::unique_ptr handleInitMessage(InitMessageFn fn) override { @@ -613,7 +631,8 @@ class ChainImpl : public Chain } std::map getImmatureStakes() override { - return {}; + LOCK(cs_main); + return GetImmatureStakes(chainman()); } std::optional findLocatorFork(const CBlockLocator& locator) override { @@ -917,38 +936,45 @@ class ChainImpl : public Chain } CAmount getTxGasFee(const CMutableTransaction& tx) override { - return {}; + return GetTxGasFee(tx, mempool(), chainman().ActiveChainstate()); } #ifdef ENABLE_WALLET void startStake(wallet::CWallet& wallet) override { + if (node::CanStake()) + { + StartStake(wallet); + } } void stopStake(wallet::CWallet& wallet) override { + StopStake(wallet); } uint64_t getStakeWeight(const wallet::CWallet& wallet, uint64_t* pStakerWeight, uint64_t* pDelegateWeight) override { - return {}; + return GetStakeWeight(wallet, pStakerWeight, pDelegateWeight); } void refreshDelegates(wallet::CWallet *pwallet, bool myDelegates, bool stakerDelegates) override { + RefreshDelegates(pwallet, myDelegates, stakerDelegates); } Span getContractRPCCommands() override { - return {}; + return wallet::GetContractRPCCommands(); } Span getMiningRPCCommands() override { - return {}; + return wallet::GetMiningRPCCommands(); } #endif bool getDelegation(const uint160& address, Delegation& delegation) override { - return {}; + QtumDelegation qtumDelegation; + return qtumDelegation.ExistDelegationContract() ? qtumDelegation.GetDelegation(address, delegation, chainman().ActiveChainstate()) : false; } bool verifyDelegation(const uint160& address, const Delegation& delegation) override { - return {}; + return QtumDelegation::VerifyDelegation(address, delegation); } NodeContext& m_node; }; diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 4c0ee039c2..692be45628 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -19,10 +19,15 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include +#include #ifdef ENABLE_WALLET #include #include @@ -33,6 +38,37 @@ #include namespace node { +unsigned int nMaxStakeLookahead = MAX_STAKE_LOOKAHEAD; +unsigned int nBytecodeTimeBuffer = BYTECODE_TIME_BUFFER; +unsigned int nStakeTimeBuffer = STAKE_TIME_BUFFER; +unsigned int nMinerSleep = STAKER_POLLING_PERIOD; +unsigned int nMinerWaitWalidBlock = STAKER_WAIT_FOR_WALID_BLOCK; +unsigned int nMinerWaitBestBlockHeader = STAKER_WAIT_FOR_BEST_BLOCK_HEADER; + +void updateMinerParams(int nHeight, const Consensus::Params& consensusParams, bool minDifficulty) +{ + static unsigned int timeDownscale = 1; + static unsigned int timeDefault = 1; + unsigned int timeDownscaleTmp = consensusParams.TimestampDownscaleFactor(nHeight); + if(timeDownscale != timeDownscaleTmp) + { + timeDownscale = timeDownscaleTmp; + unsigned int targetSpacing = consensusParams.TargetSpacing(nHeight); + nMaxStakeLookahead = std::max(MAX_STAKE_LOOKAHEAD / timeDownscale, timeDefault); + nMaxStakeLookahead = std::min(nMaxStakeLookahead, targetSpacing); + nBytecodeTimeBuffer = std::max(BYTECODE_TIME_BUFFER / timeDownscale, timeDefault); + nStakeTimeBuffer = std::max(STAKE_TIME_BUFFER / timeDownscale, timeDefault); + nMinerSleep = std::max(STAKER_POLLING_PERIOD / timeDownscale, timeDefault); + nMinerWaitWalidBlock = std::max(STAKER_WAIT_FOR_WALID_BLOCK / timeDownscale, timeDefault); + } + + // Sleep for 20 seconds when mining with minimum difficulty to avoid creating blocks every 4 seconds + if(minDifficulty && nMinerSleep != STAKER_POLLING_PERIOD_MIN_DIFFICULTY) + { + nMinerSleep = STAKER_POLLING_PERIOD_MIN_DIFFICULTY; + } +} + int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { int64_t nOldTime = pblock->nTime; @@ -44,7 +80,7 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam // Updating time can change work required on testnet: if (consensusParams.fPowAllowMinDifficultyBlocks) { - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams, pblock->IsProofOfStake()); } return nNewTime - nOldTime; @@ -64,8 +100,8 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman) static BlockAssembler::Options ClampOptions(BlockAssembler::Options options) { - // Limit weight to between 4K and DEFAULT_BLOCK_MAX_WEIGHT for sanity: - options.nBlockMaxWeight = std::clamp(options.nBlockMaxWeight, 4000, DEFAULT_BLOCK_MAX_WEIGHT); + // Limit weight to between 4K and dgpMaxBlockWeight-4K for sanity: + options.nBlockMaxWeight = std::clamp(options.nBlockMaxWeight, 4000, dgpMaxBlockWeight - 4000); return options; } @@ -95,6 +131,14 @@ static BlockAssembler::Options ConfiguredOptions() BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool) : BlockAssembler(chainstate, mempool, ConfiguredOptions()) {} +#ifdef ENABLE_WALLET +BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, wallet::CWallet *_pwallet) + : BlockAssembler(chainstate, mempool) +{ + pwallet = _pwallet; +} +#endif + void BlockAssembler::resetBlock() { inBlock.clear(); @@ -108,7 +152,25 @@ void BlockAssembler::resetBlock() nFees = 0; } -std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) +void BlockAssembler::RebuildRefundTransaction(CBlock* pblock){ + int refundtx=0; //0 for coinbase in PoW + if(pblock->IsProofOfStake()){ + refundtx=1; //1 for coinstake in PoS + } + CMutableTransaction contrTx(originalRewardTx); + contrTx.vout[refundtx].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + contrTx.vout[refundtx].nValue -= bceResult.refundSender; + //note, this will need changed for MPoS + int i=contrTx.vout.size(); + contrTx.vout.resize(contrTx.vout.size()+bceResult.refundOutputs.size()); + for(CTxOut& vout : bceResult.refundOutputs){ + contrTx.vout[i]=vout; + i++; + } + pblock->vtx[refundtx] = MakeTransactionRef(std::move(contrTx)); +} + +std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, bool fProofOfStake, int64_t* pTotalFees, int32_t txProofTime, int32_t nTimeLimit) { const auto time_start{SteadyClock::now()}; @@ -121,11 +183,20 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc } CBlock* const pblock = &pblocktemplate->block; // pointer for convenience + this->nTimeLimit = nTimeLimit; + // Add dummy coinbase tx as first transaction pblock->vtx.emplace_back(); + // Add dummy coinstake tx as second transaction + if(fProofOfStake) + pblock->vtx.emplace_back(); pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end +#ifdef ENABLE_WALLET + if(pwallet && pwallet->IsStakeClosing()) + return nullptr; +#endif LOCK(::cs_main); CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip(); assert(pindexPrev != nullptr); @@ -138,15 +209,17 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion); } - pblock->nTime = TicksSinceEpoch(NodeClock::now()); - m_lock_time_cutoff = pindexPrev->GetMedianTimePast(); - - int nPackagesSelected = 0; - int nDescendantsUpdated = 0; - if (m_mempool) { - LOCK(m_mempool->cs); - addPackageTxs(*m_mempool, nPackagesSelected, nDescendantsUpdated); + if(txProofTime == 0) { + txProofTime = GetAdjustedTimeSeconds(); } + if(fProofOfStake) + txProofTime &= ~chainparams.GetConsensus().StakeTimestampMask(nHeight); + pblock->nTime = txProofTime; + if (!fProofOfStake) + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus(),fProofOfStake); + + m_lock_time_cutoff = pindexPrev->GetMedianTimePast(); const auto time_1{SteadyClock::now()}; @@ -158,24 +231,91 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc coinbaseTx.vin.resize(1); coinbaseTx.vin[0].prevout.SetNull(); coinbaseTx.vout.resize(1); - coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; - coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + if (fProofOfStake) + { + // Make the coinbase tx empty in case of proof of stake + coinbaseTx.vout[0].SetEmpty(); + } + else + { + coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; + coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + } coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + originalRewardTx = coinbaseTx; pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); - pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev); + + // Create coinstake transaction. + if(fProofOfStake) + { + CMutableTransaction coinstakeTx; + coinstakeTx.vout.resize(2); + coinstakeTx.vout[0].SetEmpty(); + coinstakeTx.vout[1].scriptPubKey = scriptPubKeyIn; + originalRewardTx = coinstakeTx; + pblock->vtx[1] = MakeTransactionRef(std::move(coinstakeTx)); + + //this just makes CBlock::IsProofOfStake to return true + //real prevoutstake info is filled in later in SignBlock + pblock->prevoutStake.n=0; + + } + + //////////////////////////////////////////////////////// qtum + QtumDGP qtumDGP(globalState.get(), m_chainstate, fGettingValuesDGP); + globalSealEngine->setQtumSchedule(qtumDGP.getGasSchedule(nHeight)); + uint32_t blockSizeDGP = qtumDGP.getBlockSize(nHeight); + minGasPrice = qtumDGP.getMinGasPrice(nHeight); + if(gArgs.IsArgSet("-staker-min-tx-gas-price")) { + std::optional stakerMinGasPrice = ParseMoney(gArgs.GetArg("-staker-min-tx-gas-price", "")); + minGasPrice = std::max(minGasPrice, (uint64_t)(stakerMinGasPrice.value_or(0))); + } + hardBlockGasLimit = qtumDGP.getBlockGasLimit(nHeight); + softBlockGasLimit = gArgs.GetIntArg("-staker-soft-block-gas-limit", hardBlockGasLimit); + softBlockGasLimit = std::min(softBlockGasLimit, hardBlockGasLimit); + txGasLimit = gArgs.GetIntArg("-staker-max-tx-gas-limit", softBlockGasLimit); + + m_options.nBlockMaxWeight = blockSizeDGP ? blockSizeDGP * WITNESS_SCALE_FACTOR : m_options.nBlockMaxWeight; + + dev::h256 oldHashStateRoot(globalState->rootHash()); + dev::h256 oldHashUTXORoot(globalState->rootHashUTXO()); + ////////////////////////////////////////////////// deploy offline staking contract + if(nHeight == chainparams.GetConsensus().nOfflineStakeHeight){ + globalState->deployDelegationsContract(); + } + ///////////////////////////////////////////////// + int nPackagesSelected = 0; + int nDescendantsUpdated = 0; + if (m_mempool) { + LOCK(m_mempool->cs); + addPackageTxs(*m_mempool, nPackagesSelected, nDescendantsUpdated, minGasPrice, pblock); + } + pblock->hashStateRoot = uint256(h256Touint(dev::h256(globalState->rootHash()))); + pblock->hashUTXORoot = uint256(h256Touint(dev::h256(globalState->rootHashUTXO()))); + globalState->setRoot(oldHashStateRoot); + globalState->setRootUTXO(oldHashUTXORoot); + + //this should already be populated by AddBlock in case of contracts, but if no contracts + //then it won't get populated + RebuildRefundTransaction(pblock); + //////////////////////////////////////////////////////// + + pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev, fProofOfStake); pblocktemplate->vTxFees[0] = -nFees; LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); + // The total fee is the Fees minus the Refund + if (pTotalFees) + *pTotalFees = nFees - bceResult.refundSender; + // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); pblock->nNonce = 0; pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); BlockValidationState state; - if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, + if (!fProofOfStake && m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString())); } @@ -189,6 +329,114 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc return std::move(pblocktemplate); } +std::unique_ptr BlockAssembler::CreateEmptyBlock(const CScript& scriptPubKeyIn, bool fProofOfStake, int64_t* pTotalFees, int32_t nTime) +{ + resetBlock(); + + pblocktemplate.reset(new CBlockTemplate()); + + if(!pblocktemplate.get()) + return nullptr; + CBlock* const pblock = &pblocktemplate->block; // pointer for convenience + + // Add dummy coinbase tx as first transaction + pblock->vtx.emplace_back(); + // Add dummy coinstake tx as second transaction + if(fProofOfStake) + pblock->vtx.emplace_back(); + pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end + +#ifdef ENABLE_WALLET + if(pwallet && pwallet->IsStakeClosing()) + return nullptr; +#endif + LOCK(cs_main); + CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip(); + assert(pindexPrev != nullptr); + nHeight = pindexPrev->nHeight + 1; + + pblock->nVersion = m_chainstate.m_chainman.m_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (chainparams.MineBlocksOnDemand()) + pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion); + + uint32_t txProofTime = nTime == 0 ? GetAdjustedTimeSeconds() : nTime; + if(fProofOfStake) + txProofTime &= ~chainparams.GetConsensus().StakeTimestampMask(nHeight); + pblock->nTime = txProofTime; + + m_lock_time_cutoff = pindexPrev->GetMedianTimePast(); + + m_last_block_num_txs = nBlockTx; + m_last_block_weight = nBlockWeight; + + // Create coinbase transaction. + CMutableTransaction coinbaseTx; + coinbaseTx.vin.resize(1); + coinbaseTx.vin[0].prevout.SetNull(); + coinbaseTx.vout.resize(1); + if (fProofOfStake) + { + // Make the coinbase tx empty in case of proof of stake + coinbaseTx.vout[0].SetEmpty(); + } + else + { + coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; + coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + } + coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + originalRewardTx = coinbaseTx; + pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); + + // Create coinstake transaction. + if(fProofOfStake) + { + CMutableTransaction coinstakeTx; + coinstakeTx.vout.resize(2); + coinstakeTx.vout[0].SetEmpty(); + coinstakeTx.vout[1].scriptPubKey = scriptPubKeyIn; + originalRewardTx = coinstakeTx; + pblock->vtx[1] = MakeTransactionRef(std::move(coinstakeTx)); + + //this just makes CBlock::IsProofOfStake to return true + //real prevoutstake info is filled in later in SignBlock + pblock->prevoutStake.n=0; + } + + //////////////////////////////////////////////////////// qtum + //state shouldn't change here for an empty block, but if it's not valid it'll fail in CheckBlock later + pblock->hashStateRoot = uint256(h256Touint(dev::h256(globalState->rootHash()))); + pblock->hashUTXORoot = uint256(h256Touint(dev::h256(globalState->rootHashUTXO()))); + + RebuildRefundTransaction(pblock); + //////////////////////////////////////////////////////// + + pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev, fProofOfStake); + pblocktemplate->vTxFees[0] = -nFees; + + // The total fee is the Fees minus the Refund + if (pTotalFees) + *pTotalFees = nFees - bceResult.refundSender; + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + if (!fProofOfStake) + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus(),fProofOfStake); + pblock->nNonce = 0; + pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); + + BlockValidationState state; + if (!fProofOfStake && m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) { + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString())); + } + + return std::move(pblocktemplate); +} + void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet) { for (CTxMemPool::setEntries::iterator iit = testSet.begin(); iit != testSet.end(); ) { @@ -207,7 +455,7 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost if (nBlockWeight + WITNESS_SCALE_FACTOR * packageSize >= m_options.nBlockMaxWeight) { return false; } - if (nBlockSigOpsCost + packageSigOpsCost >= MAX_BLOCK_SIGOPS_COST) { + if (nBlockSigOpsCost + packageSigOpsCost >= (uint64_t)dgpMaxBlockSigOps) { return false; } return true; @@ -225,6 +473,153 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa return true; } +bool BlockAssembler::AttemptToAddContractToBlock(CTxMemPool::txiter iter, uint64_t minGasPrice, CBlock* pblock) { + if (nTimeLimit != 0 && GetAdjustedTimeSeconds() >= nTimeLimit - nBytecodeTimeBuffer) { + return false; + } + if (gArgs.GetBoolArg("-disablecontractstaking", false)) + { + // Contract staking is disabled for the staker + return false; + } + + dev::h256 oldHashStateRoot(globalState->rootHash()); + dev::h256 oldHashUTXORoot(globalState->rootHashUTXO()); + // operate on local vars first, then later apply to `this` + uint64_t nBlockWeight = this->nBlockWeight; + uint64_t nBlockSigOpsCost = this->nBlockSigOpsCost; + + unsigned int contractflags = GetContractScriptFlags(nHeight, chainparams.GetConsensus()); + QtumTxConverter convert(iter->GetTx(), m_chainstate, m_mempool, NULL, &pblock->vtx, contractflags); + + ExtractQtumTX resultConverter; + if(!convert.extractionQtumTransactions(resultConverter)){ + //this check already happens when accepting txs into mempool + //therefore, this can only be triggered by using raw transactions on the staker itself + LogPrintf("AttemptToAddContractToBlock(): Fail to extract contacts from tx %s\n", iter->GetTx().GetHash().ToString()); + return false; + } + std::vector qtumTransactions = resultConverter.first; + dev::u256 txGas = 0; + for(QtumTransaction qtumTransaction : qtumTransactions){ + txGas += qtumTransaction.gas(); + if(txGas > txGasLimit) { + // Limit the tx gas limit by the soft limit if such a limit has been specified. + LogPrintf("AttemptToAddContractToBlock(): The gas needed is bigger than -staker-max-tx-gas-limit for the contract tx %s\n", iter->GetTx().GetHash().ToString()); + return false; + } + + if(bceResult.usedGas + qtumTransaction.gas() > softBlockGasLimit){ + // If this transaction's gasLimit could cause block gas limit to be exceeded, then don't add it + // Log if the contract is the only contract tx + if(bceResult.usedGas == 0) + LogPrintf("AttemptToAddContractToBlock(): The gas needed is bigger than -staker-soft-block-gas-limit for the contract tx %s\n", iter->GetTx().GetHash().ToString()); + return false; + } + if(qtumTransaction.gasPrice() < minGasPrice){ + //if this transaction's gasPrice is less than the current DGP minGasPrice don't add it + LogPrintf("AttemptToAddContractToBlock(): The gas price is less than -staker-min-tx-gas-price for the contract tx %s\n", iter->GetTx().GetHash().ToString()); + return false; + } + } + // We need to pass the DGP's block gas limit (not the soft limit) since it is consensus critical. + ByteCodeExec exec(*pblock, qtumTransactions, hardBlockGasLimit, m_chainstate.m_chain.Tip(), m_chainstate.m_chain); + if(!exec.performByteCode()){ + //error, don't add contract + globalState->setRoot(oldHashStateRoot); + globalState->setRootUTXO(oldHashUTXORoot); + LogPrintf("AttemptToAddContractToBlock(): Perform byte code fails for the contract tx %s\n", iter->GetTx().GetHash().ToString()); + return false; + } + + ByteCodeExecResult testExecResult; + if(!exec.processingResults(testExecResult)){ + globalState->setRoot(oldHashStateRoot); + globalState->setRootUTXO(oldHashUTXORoot); + LogPrintf("AttemptToAddContractToBlock(): Processing results fails for the contract tx %s\n", iter->GetTx().GetHash().ToString()); + return false; + } + + if(bceResult.usedGas + testExecResult.usedGas > softBlockGasLimit){ + // If this transaction could cause block gas limit to be exceeded, then don't add it + globalState->setRoot(oldHashStateRoot); + globalState->setRootUTXO(oldHashUTXORoot); + // Log if the contract is the only contract tx + if(bceResult.usedGas == 0) + LogPrintf("AttemptToAddContractToBlock(): The gas used is bigger than -staker-soft-block-gas-limit for the contract tx %s\n", iter->GetTx().GetHash().ToString()); + return false; + } + + //apply contractTx costs to local state + nBlockWeight += iter->GetTxWeight(); + nBlockSigOpsCost += iter->GetSigOpCost(); + //apply value-transfer txs to local state + for (CTransaction &t : testExecResult.valueTransfers) { + nBlockWeight += GetTransactionWeight(t); + nBlockSigOpsCost += GetLegacySigOpCount(t); + } + + int proofTx = pblock->IsProofOfStake() ? 1 : 0; + + //calculate sigops from new refund/proof tx + + //first, subtract old proof tx + nBlockSigOpsCost -= GetLegacySigOpCount(*pblock->vtx[proofTx]); + + // manually rebuild refundtx + CMutableTransaction contrTx(*pblock->vtx[proofTx]); + //note, this will need changed for MPoS + int i=contrTx.vout.size(); + contrTx.vout.resize(contrTx.vout.size()+testExecResult.refundOutputs.size()); + for(CTxOut& vout : testExecResult.refundOutputs){ + contrTx.vout[i]=vout; + i++; + } + nBlockSigOpsCost += GetLegacySigOpCount(contrTx); + //all contract costs now applied to local state + + //Check if block will be too big or too expensive with this contract execution + if (nBlockSigOpsCost * WITNESS_SCALE_FACTOR > (uint64_t)dgpMaxBlockSigOps || + nBlockWeight > dgpMaxBlockWeight) { + //contract will not be added to block, so revert state to before we tried + globalState->setRoot(oldHashStateRoot); + globalState->setRootUTXO(oldHashUTXORoot); + return false; + } + + //block is not too big, so apply the contract execution and it's results to the actual block + + //apply local bytecode to global bytecode state + bceResult.usedGas += testExecResult.usedGas; + bceResult.refundSender += testExecResult.refundSender; + bceResult.refundOutputs.insert(bceResult.refundOutputs.end(), testExecResult.refundOutputs.begin(), testExecResult.refundOutputs.end()); + bceResult.valueTransfers = std::move(testExecResult.valueTransfers); + + pblock->vtx.emplace_back(iter->GetSharedTx()); + pblocktemplate->vTxFees.push_back(iter->GetFee()); + pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost()); + this->nBlockWeight += iter->GetTxWeight(); + ++nBlockTx; + this->nBlockSigOpsCost += iter->GetSigOpCost(); + nFees += iter->GetFee(); + inBlock.insert(iter->GetSharedTx()->GetHash()); + + for (CTransaction &t : bceResult.valueTransfers) { + pblock->vtx.emplace_back(MakeTransactionRef(std::move(t))); + this->nBlockWeight += GetTransactionWeight(t); + this->nBlockSigOpsCost += GetLegacySigOpCount(t); + ++nBlockTx; + } + //calculate sigops from new refund/proof tx + this->nBlockSigOpsCost -= GetLegacySigOpCount(*pblock->vtx[proofTx]); + RebuildRefundTransaction(pblock); + this->nBlockSigOpsCost += GetLegacySigOpCount(*pblock->vtx[proofTx]); + + bceResult.valueTransfers.clear(); + + return true; +} + void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) { pblocktemplate->block.vtx.emplace_back(iter->GetSharedTx()); @@ -295,7 +690,7 @@ void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, std::ve // Each time through the loop, we compare the best transaction in // mapModifiedTxs with the next transaction in the mempool to decide what // transaction package to work on next. -void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSelected, int& nDescendantsUpdated) +void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSelected, int& nDescendantsUpdated, uint64_t minGasPrice, CBlock* pblock) { AssertLockHeld(mempool.cs); @@ -305,7 +700,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele // Keep track of entries that failed inclusion, to avoid duplicate work std::set failedTx; - CTxMemPool::indexed_transaction_set::index::type::iterator mi = mempool.mapTx.get().begin(); + CTxMemPool::indexed_transaction_set::index::type::iterator mi = mempool.mapTx.get().begin(); CTxMemPool::txiter iter; // Limit the number of attempts to add transactions to the block when it is @@ -314,7 +709,11 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele const int64_t MAX_CONSECUTIVE_FAILURES = 1000; int64_t nConsecutiveFailed = 0; - while (mi != mempool.mapTx.get().end() || !mapModifiedTx.empty()) { + while (mi != mempool.mapTx.get().end() || !mapModifiedTx.empty()) { + if(nTimeLimit != 0 && GetAdjustedTimeSeconds() >= nTimeLimit){ + //no more time to add transactions, just exit + return; + } // First try to find a new transaction in mapTx to evaluate. // // Skip entries in mapTx that are already in a block or are present @@ -328,7 +727,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele // cached size/sigops/fee values that are not actually correct. /** Return true if given transaction from mapTx has already been evaluated, * or if the transaction's cached data in mapTx is incorrect. */ - if (mi != mempool.mapTx.get().end()) { + if (mi != mempool.mapTx.get().end()) { auto it = mempool.mapTx.project<0>(mi); assert(it != mempool.mapTx.end()); if (mapModifiedTx.count(it) || inBlock.count(it->GetSharedTx()->GetHash()) || failedTx.count(it->GetSharedTx()->GetHash())) { @@ -341,16 +740,16 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele // the next entry from mapTx, or the best from mapModifiedTx? bool fUsingModified = false; - modtxscoreiter modit = mapModifiedTx.get().begin(); - if (mi == mempool.mapTx.get().end()) { + modtxscoreiter modit = mapModifiedTx.get().begin(); + if (mi == mempool.mapTx.get().end()) { // We're out of entries in mapTx; use the entry from mapModifiedTx iter = modit->iter; fUsingModified = true; } else { // Try to compare the mapTx entry to the mapModifiedTx entry iter = mempool.mapTx.project<0>(mi); - if (modit != mapModifiedTx.get().end() && - CompareTxMemPoolEntryByAncestorFee()(*modit, CTxMemPoolModifiedEntry(iter))) { + if (modit != mapModifiedTx.get().end() && + CompareModifiedEntry()(*modit, CTxMemPoolModifiedEntry(iter))) { // The best entry in mapModifiedTx has higher score // than the one from mapTx. // Switch which transaction (package) to consider @@ -386,7 +785,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele // Since we always look at the best entry in mapModifiedTx, // we must erase failed entries so that we can consider the // next best entry on the next loop iteration - mapModifiedTx.get().erase(modit); + mapModifiedTx.get().erase(modit); failedTx.insert(iter->GetSharedTx()->GetHash()); } @@ -408,7 +807,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele // Test if all tx's are Final if (!TestPackageTransactions(ancestors)) { if (fUsingModified) { - mapModifiedTx.get().erase(modit); + mapModifiedTx.get().erase(modit); failedTx.insert(iter->GetSharedTx()->GetHash()); } continue; @@ -421,18 +820,59 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele std::vector sortedEntries; SortForBlock(ancestors, sortedEntries); + bool wasAdded=true; for (size_t i = 0; i < sortedEntries.size(); ++i) { - AddToBlock(sortedEntries[i]); + if(!wasAdded || (nTimeLimit != 0 && GetAdjustedTimeSeconds() >= nTimeLimit)) + { + //if out of time, or earlier ancestor failed, then skip the rest of the transactions + mapModifiedTx.erase(sortedEntries[i]); + wasAdded=false; + continue; + } + const CTransaction& tx = sortedEntries[i]->GetTx(); + if(wasAdded) { + if (tx.HasCreateOrCall()) { + wasAdded = AttemptToAddContractToBlock(sortedEntries[i], minGasPrice, pblock); + if(!wasAdded){ + if(fUsingModified) { + //this only needs to be done once to mark the whole package (everything in sortedEntries) as failed + mapModifiedTx.get().erase(modit); + failedTx.insert(iter->GetSharedTx()->GetHash()); + } + } + } else { + AddToBlock(sortedEntries[i]); + } + } // Erase from the modified set, if present mapModifiedTx.erase(sortedEntries[i]); } + if(!wasAdded){ + //skip UpdatePackages if a transaction failed to be added (match TestPackage logic) + continue; + } + ++nPackagesSelected; // Update transactions that depend on each of these nDescendantsUpdated += UpdatePackagesForAdded(mempool, ancestors, mapModifiedTx); } } + +bool CanStake() +{ + bool canStake = gArgs.GetBoolArg("-staking", DEFAULT_STAKE); + + if(canStake) + { + // Signet is for creating PoW blocks by an authorized signer + canStake = !Params().GetConsensus().signet_blocks; + } + + return canStake; +} + #ifdef ENABLE_WALLET void StakeQtums(bool fStake, wallet::CWallet *pwallet) { diff --git a/src/node/miner.h b/src/node/miner.h index a2d31cfe32..c22a6b1be7 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -125,6 +126,65 @@ struct modifiedentry_iter { } }; +// This related to the calculation in CompareTxMemPoolEntryByAncestorFeeOrGasPrice, +// except operating on CTxMemPoolModifiedEntry. +// TODO: refactor to avoid duplication of this logic. +struct CompareModifiedEntry { + bool operator()(const CTxMemPoolModifiedEntry &a, const CTxMemPoolModifiedEntry &b) const + { + int fAHasCreateOrCall = a.iter->GetTx().GetCreateOrCall(); + int fBHasCreateOrCall = b.iter->GetTx().GetCreateOrCall(); + + // If either of the two entries that we are comparing has a contract scriptPubKey, the comparison here takes precedence + if(fAHasCreateOrCall || fBHasCreateOrCall) { + + // Prioritze non-contract txs + if((fAHasCreateOrCall > CTransaction::OpNone) != (fBHasCreateOrCall > CTransaction::OpNone)) { + return fAHasCreateOrCall > CTransaction::OpNone ? false : true; + } + + // Prioritze create contract txs over send to contract txs + if((fAHasCreateOrCall > CTransaction::OpNone) && (fBHasCreateOrCall > CTransaction::OpNone) && + (fAHasCreateOrCall != fBHasCreateOrCall) && (fAHasCreateOrCall == CTransaction::OpCall || fBHasCreateOrCall == CTransaction::OpCall)){ + return fAHasCreateOrCall == CTransaction::OpCall ? false : true; + } + + // Prioritize the contract txs that have the least number of ancestors + // The reason for this is that otherwise it is possible to send one tx with a + // high gas limit but a low gas price which has a child with a low gas limit but a high gas price + // Without this condition that transaction chain would get priority in being included into the block. + // The two next checks are to see if all our ancestors have been added. + if((int64_t) a.nSizeWithAncestors == a.iter->GetTxSize() && (int64_t) b.nSizeWithAncestors != b.iter->GetTxSize()) { + return true; + } + + if((int64_t) b.nSizeWithAncestors == b.iter->GetTxSize() && (int64_t) a.nSizeWithAncestors != a.iter->GetTxSize()) { + return false; + } + + // Otherwise, prioritize the contract tx with the highest (minimum among its outputs) gas price + // The reason for using the gas price of the output that sets the minimum gas price is that + // otherwise it may be possible to game the prioritization by setting a large gas price in one output + // that does no execution, while the real execution has a very low gas price + if(a.iter->GetMinGasPrice() != b.iter->GetMinGasPrice()) { + return a.iter->GetMinGasPrice() > b.iter->GetMinGasPrice(); + } + + // Otherwise, prioritize the tx with the min size + if(a.iter->GetTxSize() != b.iter->GetTxSize()) { + return a.iter->GetTxSize() < b.iter->GetTxSize(); + } + + // If the txs are identical in their minimum gas prices and tx size + // order based on the tx hash for consistency. + return CompareIteratorByHash()(a.iter, b.iter); + } + + // If neither of the txs we are comparing are contract txs, use the standard comparison based on ancestor fees / ancestor size + return CompareTxMemPoolEntryByAncestorFee()(a, b); + } +}; + // A comparator that sorts transactions based on number of ancestors. // This is sufficient to sort an ancestor package in an order that is valid // to appear in a block. @@ -145,18 +205,18 @@ typedef boost::multi_index_container< modifiedentry_iter, CompareCTxMemPoolIter >, - // sorted by modified ancestor fee rate + // sorted by modified ancestor fee rate or gas price boost::multi_index::ordered_non_unique< // Reuse same tag from CTxMemPool's similar index - boost::multi_index::tag, + boost::multi_index::tag, boost::multi_index::identity, - CompareTxMemPoolEntryByAncestorFee + CompareModifiedEntry > > > indexed_modified_transaction_set; typedef indexed_modified_transaction_set::nth_index<0>::type::iterator modtxiter; -typedef indexed_modified_transaction_set::index::type::iterator modtxscoreiter; +typedef indexed_modified_transaction_set::index::type::iterator modtxscoreiter; struct update_for_parent_inclusion { @@ -193,11 +253,14 @@ class BlockAssembler const CChainParams& chainparams; const CTxMemPool* const m_mempool; Chainstate& m_chainstate; +#ifdef ENABLE_WALLET + wallet::CWallet *pwallet = 0; +#endif public: struct Options { // Configuration parameters for the block size - size_t nBlockMaxWeight{DEFAULT_BLOCK_MAX_WEIGHT}; + mutable size_t nBlockMaxWeight{DEFAULT_BLOCK_MAX_WEIGHT}; CFeeRate blockMinFeeRate{DEFAULT_BLOCK_MIN_TX_FEE}; // Whether to call TestBlockValidity() at the end of CreateNewBlock(). bool test_block_validity{true}; @@ -205,9 +268,27 @@ class BlockAssembler explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool); explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options); +#ifdef ENABLE_WALLET + explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, wallet::CWallet *pwallet); +#endif + +///////////////////////////////////////////// // qtum + ByteCodeExecResult bceResult; + uint64_t minGasPrice = 1; + uint64_t hardBlockGasLimit; + uint64_t softBlockGasLimit; + uint64_t txGasLimit; +///////////////////////////////////////////// + + // The original constructed reward tx (either coinbase or coinstake) without gas refund adjustments + CMutableTransaction originalRewardTx; // qtum + + //When GetAdjustedTime() exceeds this, no more transactions will attempt to be added + int32_t nTimeLimit; /** Construct a new block template with coinbase to scriptPubKeyIn */ - std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn); + std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, bool fProofOfStake=false, int64_t* pTotalFees = 0, int32_t nTime=0, int32_t nTimeLimit=0); + std::unique_ptr CreateEmptyBlock(const CScript& scriptPubKeyIn, bool fProofOfStake=false, int64_t* pTotalFees = 0, int32_t nTime=0); inline static std::optional m_last_block_num_txs{}; inline static std::optional m_last_block_weight{}; @@ -221,12 +302,16 @@ class BlockAssembler /** Add a tx to the block */ void AddToBlock(CTxMemPool::txiter iter); + bool AttemptToAddContractToBlock(CTxMemPool::txiter iter, uint64_t minGasPrice, CBlock* pblock); + // Methods for how to add transactions to a block. /** Add transactions based on feerate including unconfirmed ancestors * Increments nPackagesSelected / nDescendantsUpdated with corresponding * statistics from the package selection (for logging statistics). */ - void addPackageTxs(const CTxMemPool& mempool, int& nPackagesSelected, int& nDescendantsUpdated) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); + void addPackageTxs(const CTxMemPool& mempool, int& nPackagesSelected, int& nDescendantsUpdated, uint64_t minGasPrice, CBlock* pblock) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); + /** Rebuild the coinbase/coinstake transaction to account for new gas refunds **/ + void RebuildRefundTransaction(CBlock* pblock); // helper functions for addPackageTxs() /** Remove confirmed (inBlock) entries from given set */ void onlyUnconfirmed(CTxMemPool::setEntries& testSet); @@ -254,6 +339,9 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman); /** Apply -blockmintxfee and -blockmaxweight options from ArgsManager to BlockAssembler options. */ void ApplyArgsManOptions(const ArgsManager& gArgs, BlockAssembler::Options& options); + +/** Check if staking is enabled */ +bool CanStake(); } // namespace node #endif // BITCOIN_NODE_MINER_H