diff --git a/src/Makefile.am b/src/Makefile.am index 7b9d298373..b3cbf96910 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -642,6 +642,7 @@ libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_consensus_a_SOURCES = \ arith_uint256.cpp \ arith_uint256.h \ + consensus/consensus.cpp \ consensus/amount.h \ consensus/merkle.cpp \ consensus/merkle.h \ diff --git a/src/coins.cpp b/src/coins.cpp index b62653e0de..d0c1557ed8 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -98,12 +98,13 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi it->second.coin = std::move(coin); it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0); cachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); - TRACE5(utxocache, add, + TRACE6(utxocache, add, outpoint.hash.data(), (uint32_t)outpoint.n, (uint32_t)it->second.coin.nHeight, (int64_t)it->second.coin.out.nValue, - (bool)it->second.coin.IsCoinBase()); + (bool)it->second.coin.IsCoinBase(), + (bool)it->second.coin.IsCoinStake()); } void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) { @@ -116,12 +117,13 @@ void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coi void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) { bool fCoinbase = tx.IsCoinBase(); + bool fCoinstake = tx.IsCoinStake(); const Txid& txid = tx.GetHash(); for (size_t i = 0; i < tx.vout.size(); ++i) { bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase; // Coinbase transactions can always be overwritten, in order to correctly // deal with the pre-BIP30 occurrences of duplicate coinbase transactions. - cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite); + cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fCoinstake), overwrite); } } @@ -129,12 +131,13 @@ bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) { CCoinsMap::iterator it = FetchCoin(outpoint); if (it == cacheCoins.end()) return false; cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); - TRACE5(utxocache, spent, + TRACE6(utxocache, spent, outpoint.hash.data(), (uint32_t)outpoint.n, (uint32_t)it->second.coin.nHeight, (int64_t)it->second.coin.out.nValue, - (bool)it->second.coin.IsCoinBase()); + (bool)it->second.coin.IsCoinBase(), + (bool)it->second.coin.IsCoinStake()); if (moveout) { *moveout = std::move(it->second.coin); } @@ -285,12 +288,13 @@ void CCoinsViewCache::Uncache(const COutPoint& hash) CCoinsMap::iterator it = cacheCoins.find(hash); if (it != cacheCoins.end() && it->second.flags == 0) { cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); - TRACE5(utxocache, uncache, + TRACE6(utxocache, uncache, hash.hash.data(), (uint32_t)hash.n, (uint32_t)it->second.coin.nHeight, (int64_t)it->second.coin.out.nValue, - (bool)it->second.coin.IsCoinBase()); + (bool)it->second.coin.IsCoinBase(), + (bool)it->second.coin.IsCoinStake()); cacheCoins.erase(it); } } @@ -299,6 +303,18 @@ unsigned int CCoinsViewCache::GetCacheSize() const { return cacheCoins.size(); } +CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const +{ + if (tx.IsCoinBase()) + return 0; + + CAmount nResult = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + nResult += AccessCoin(tx.vin[i].prevout).out.nValue; + + return nResult; +} + bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsCoinBase()) { @@ -339,7 +355,7 @@ void CCoinsViewCache::SanityCheck() const } static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut()); -static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT; +static const size_t MAX_OUTPUTS_PER_BLOCK = dgpMaxBlockWeight / MIN_TRANSACTION_OUTPUT_WEIGHT; const Coin& AccessByTxid(const CCoinsViewCache& view, const Txid& txid) { @@ -377,3 +393,9 @@ bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) cons bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint &outpoint) const { return ExecuteBackedWrapper([&]() { return CCoinsViewBacked::HaveCoin(outpoint); }, m_err_callbacks); } + +const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const +{ + const Coin& coins = AccessCoin(input.prevout); + return coins.out; +} diff --git a/src/coins.h b/src/coins.h index d0f81ddc57..9e093ea840 100644 --- a/src/coins.h +++ b/src/coins.h @@ -21,11 +21,72 @@ #include #include +////////////////////////////////////////////////////////////////// // qtum +struct CSpentIndexKey { + uint256 txid; + unsigned int outputIndex; + + SERIALIZE_METHODS(CSpentIndexKey, obj) { READWRITE(obj.txid, obj.outputIndex); } + + CSpentIndexKey(uint256 t, unsigned int i) { + txid = t; + outputIndex = i; + } + + CSpentIndexKey() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + outputIndex = 0; + } +}; + +struct CSpentIndexValue { + uint256 txid; + unsigned int inputIndex; + int blockHeight; + CAmount satoshis; + int addressType; + uint256 addressHash; + + SERIALIZE_METHODS(CSpentIndexValue, obj) { READWRITE(obj.txid, obj.inputIndex, obj.blockHeight, obj.satoshis, obj.addressType, obj.addressHash); } + + CSpentIndexValue(uint256 t, unsigned int i, int h, CAmount s, int type, uint256 a) { + txid = t; + inputIndex = i; + blockHeight = h; + satoshis = s; + addressType = type; + addressHash = a; + } + + CSpentIndexValue() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + inputIndex = 0; + blockHeight = 0; + satoshis = 0; + addressType = 0; + addressHash.SetNull(); + } + + bool IsNull() const { + return txid.IsNull(); + } +}; +////////////////////////////////////////////////////////////////// + /** * A UTXO entry. * * Serialized format: - * - VARINT((coinbase ? 1 : 0) | (height << 1)) + * - VARINT((coinbase ? 1 : 0) | (height << 2)) + * - VARINT((coinstake ? 2 : 0) | (height << 2)) * - the non-spent CTxOut (via TxOutCompression) */ class Coin @@ -39,29 +100,33 @@ class Coin unsigned int fCoinStake : 1; //! at which height this containing transaction was included in the active block chain - uint32_t nHeight : 31; + uint32_t nHeight : 30; //! construct a Coin from a CTxOut and height/coinbase information. - Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn) {} - Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn) : out(outIn), fCoinBase(fCoinBaseIn),nHeight(nHeightIn) {} + Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), fCoinStake(fCoinStakeIn), nHeight(nHeightIn) {} + Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn) : out(outIn), fCoinBase(fCoinBaseIn), fCoinStake(fCoinStakeIn), nHeight(nHeightIn) {} void Clear() { out.SetNull(); fCoinBase = false; + fCoinStake = false; nHeight = 0; } //! empty constructor - Coin() : fCoinBase(false), nHeight(0) { } + Coin() : fCoinBase(false), fCoinStake(false), nHeight(0) { } bool IsCoinBase() const { return fCoinBase; } + bool IsCoinStake() const { + return fCoinStake; + } template void Serialize(Stream &s) const { assert(!IsSpent()); - uint32_t code = nHeight * uint32_t{2} + fCoinBase; + uint32_t code = (nHeight << 2) + (fCoinBase ? 1 : 0) + (fCoinStake ? 2 : 0); ::Serialize(s, VARINT(code)); ::Serialize(s, Using(out)); } @@ -70,8 +135,9 @@ class Coin void Unserialize(Stream &s) { uint32_t code = 0; ::Unserialize(s, VARINT(code)); - nHeight = code >> 1; + nHeight = code >> 2; fCoinBase = code & 1; + fCoinStake = (code >> 1) & 1; ::Unserialize(s, Using(out)); } @@ -331,6 +397,16 @@ class CCoinsViewCache : public CCoinsViewBacked //! Calculate the size of the cache (in bytes) size_t DynamicMemoryUsage() const; + /** + * Amount of bitcoins coming in to a transaction + * Note that lightweight clients may not know anything besides the hash of previous transactions, + * so may not be able to calculate this. + * + * @param[in] tx transaction for which we are checking input total + * @return Sum of value of all inputs (scriptSigs) + */ + CAmount GetValueIn(const CTransaction& tx) const; + //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx) const; @@ -344,6 +420,8 @@ class CCoinsViewCache : public CCoinsViewBacked //! Run an internal sanity check on the cache data structure. */ void SanityCheck() const; + const CTxOut &GetOutputFor(const CTxIn& input) const; + private: /** * @note this is marked const, but may actually append to `cacheCoins`, increasing diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 384f70bc10..876900c660 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -10,14 +10,24 @@ #include /** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */ +extern unsigned int dgpMaxBlockSerSize; static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 4000000; /** The maximum allowed weight for a block, see BIP 141 (network rule) */ +extern unsigned int dgpMaxBlockWeight; + +extern unsigned int dgpMaxBlockSize; // qtum static const unsigned int MAX_BLOCK_WEIGHT = 4000000; /** The maximum allowed number of signature check operations in a block (network rule) */ static const int64_t MAX_BLOCK_SIGOPS_COST = 80000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; +extern int64_t dgpMaxBlockSigOps; + +extern unsigned int dgpMaxProtoMsgLength; +extern unsigned int dgpMaxTxSigOps; + +static const int MAX_TRANSACTION_BASE_SIZE = 1000000; static const int WITNESS_SCALE_FACTOR = 4; static const size_t MIN_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 60; // 60 is the lower bound for the size of a valid serialized CTransaction @@ -27,4 +37,6 @@ static const size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * /** Interpret sequence numbers as relative lock-time constraints. */ static constexpr unsigned int LOCKTIME_VERIFY_SEQUENCE = (1 << 0); +void updateBlockSizeParams(unsigned int newBlockSize); + #endif // BITCOIN_CONSENSUS_CONSENSUS_H diff --git a/src/core_write.cpp b/src/core_write.cpp index 63f2c36b5a..923f3f45a8 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -189,6 +189,7 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry const bool have_undo = txundo != nullptr; CAmount amt_total_in = 0; CAmount amt_total_out = 0; + bool isCoinStake = tx.IsCoinStake(); for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxIn& txin = tx.vin[i]; @@ -214,14 +215,17 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry const Coin& prev_coin = txundo->vprevout[i]; const CTxOut& prev_txout = prev_coin.out; - amt_total_in += prev_txout.nValue; + if(!isCoinStake) + { + amt_total_in += prev_txout.nValue; + } if (verbosity == TxVerbosity::SHOW_DETAILS_AND_PREVOUT) { UniValue o_script_pub_key(UniValue::VOBJ); ScriptToUniv(prev_txout.scriptPubKey, /*out=*/o_script_pub_key, /*include_hex=*/true, /*include_address=*/true); UniValue p(UniValue::VOBJ); - p.pushKV("generated", bool(prev_coin.fCoinBase)); + p.pushKV("generated", bool(prev_coin.fCoinBase || prev_coin.fCoinStake)); p.pushKV("height", uint64_t(prev_coin.nHeight)); p.pushKV("value", ValueFromAmount(prev_txout.nValue)); p.pushKV("scriptPubKey", o_script_pub_key); @@ -247,13 +251,13 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry out.pushKV("scriptPubKey", o); vout.push_back(out); - if (have_undo) { + if (have_undo && !isCoinStake) { amt_total_out += txout.nValue; } } entry.pushKV("vout", vout); - if (have_undo) { + if (have_undo && !isCoinStake) { const CAmount fee = amt_total_in - amt_total_out; CHECK_NONFATAL(MoneyRange(fee)); entry.pushKV("fee", ValueFromAmount(fee)); diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp index ecd3fd21b5..8406e3164a 100644 --- a/src/index/coinstatsindex.cpp +++ b/src/index/coinstatsindex.cpp @@ -157,7 +157,7 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block) for (uint32_t j = 0; j < tx->vout.size(); ++j) { const CTxOut& out{tx->vout[j]}; - Coin coin{out, block.height, tx->IsCoinBase()}; + Coin coin{out, block.height, tx->IsCoinBase(), tx->IsCoinStake()}; COutPoint outpoint{tx->GetHash(), j}; // Skip unspendable coins @@ -435,7 +435,7 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex for (uint32_t j = 0; j < tx->vout.size(); ++j) { const CTxOut& out{tx->vout[j]}; COutPoint outpoint{tx->GetHash(), j}; - Coin coin{out, pindex->nHeight, tx->IsCoinBase()}; + Coin coin{out, pindex->nHeight, tx->IsCoinBase(), tx->IsCoinStake()}; // Skip unspendable coins if (coin.out.scriptPubKey.IsUnspendable()) { diff --git a/src/logging.cpp b/src/logging.cpp index 801474ff7b..f6e66a49a8 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -52,15 +52,19 @@ bool BCLog::Logger::StartLogging() assert(m_buffering); assert(m_fileout == nullptr); + assert(m_fileoutVM == nullptr); // qtum if (m_print_to_file) { assert(!m_file_path.empty()); + assert(!m_file_pathVM.empty()); // qtum m_fileout = fsbridge::fopen(m_file_path, "a"); - if (!m_fileout) { + m_fileoutVM = fsbridge::fopen(m_file_pathVM, "a"); + if (!m_fileout || !m_fileoutVM) { return false; } setbuf(m_fileout, nullptr); // unbuffered + setbuf(m_fileoutVM, nullptr); // unbuffered // Add newlines to the logfile to distinguish this execution from the // last one. @@ -70,12 +74,15 @@ bool BCLog::Logger::StartLogging() // dump buffered messages from before we opened the log m_buffering = false; while (!m_msgs_before_open.empty()) { - const std::string& s = m_msgs_before_open.front(); + LogMsg logmsg= m_msgs_before_open.front(); - if (m_print_to_file) FileWriteStr(s, m_fileout); - if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout); + FILE* file = logmsg.useVMLog ? m_fileoutVM : m_fileout; + if (file && m_print_to_file) FileWriteStr(logmsg.msg, file); + bool print_to_console = m_print_to_console; + if(print_to_console && logmsg.useVMLog && !m_show_evm_logs) print_to_console = false; + if (print_to_console) fwrite(logmsg.msg.data(), 1, logmsg.msg.size(), stdout); for (const auto& cb : m_print_callbacks) { - cb(s); + cb(logmsg.msg); } m_msgs_before_open.pop_front(); @@ -91,6 +98,8 @@ void BCLog::Logger::DisconnectTestLogger() m_buffering = true; if (m_fileout != nullptr) fclose(m_fileout); m_fileout = nullptr; + if (m_fileoutVM != nullptr) fclose(m_fileoutVM); + m_fileoutVM = nullptr; m_print_callbacks.clear(); } @@ -184,6 +193,9 @@ const CLogCategoryDesc LogCategories[] = {BCLog::TXRECONCILIATION, "txreconciliation"}, {BCLog::SCAN, "scan"}, {BCLog::TXPACKAGES, "txpackages"}, + {BCLog::COINSTAKE, "coinstake"}, + {BCLog::HTTPPOLL, "http-poll"}, + {BCLog::INDEX, "index"}, {BCLog::ALL, "1"}, {BCLog::ALL, "all"}, }; @@ -288,6 +300,12 @@ std::string LogCategoryToStr(BCLog::LogFlags category) return "scan"; case BCLog::LogFlags::TXPACKAGES: return "txpackages"; + case BCLog::LogFlags::COINSTAKE: + return "coinstake"; + case BCLog::LogFlags::HTTPPOLL: + return "http-poll"; + case BCLog::LogFlags::INDEX: + return "index"; case BCLog::LogFlags::ALL: return "all"; } @@ -425,7 +443,12 @@ void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& loggi } if (m_log_sourcelocations && m_started_new_line) { - str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] "); + if(useVMLog) { + str_prefixed.insert(0, "[" + logging_function + "] "); + } + else { + str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] "); + } } if (m_log_threadnames && m_started_new_line) { @@ -439,11 +462,15 @@ void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& loggi if (m_buffering) { // buffer if we haven't started logging yet - m_msgs_before_open.push_back(str_prefixed); + LogMsg logmsg(str_prefixed, useVMLog); + m_msgs_before_open.push_back(logmsg); return; } - if (m_print_to_console) { + bool print_to_console = m_print_to_console; + if(print_to_console && useVMLog && !m_show_evm_logs) print_to_console = false; + + if (print_to_console) { // print to console fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout); fflush(stdout); @@ -453,6 +480,7 @@ void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& loggi } if (m_print_to_file) { assert(m_fileout != nullptr); + assert(m_fileoutVM != nullptr); // reopen the log file, if requested if (m_reopen_file) { @@ -463,8 +491,22 @@ void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& loggi fclose(m_fileout); m_fileout = new_fileout; } + FILE* new_fileoutVM = fsbridge::fopen(m_file_pathVM, "a"); + if (new_fileoutVM) { + setbuf(new_fileoutVM, nullptr); // unbuffered + fclose(m_fileoutVM); + m_fileoutVM = new_fileoutVM; + } } - FileWriteStr(str_prefixed, m_fileout); + + //////////////////////////////// // qtum + FILE* file = m_fileout; + if(useVMLog){ + file = m_fileoutVM; + } + //////////////////////////////// + + FileWriteStr(str_prefixed, file); } } diff --git a/src/logging.h b/src/logging.h index 3b092a06ee..beb6147ff8 100644 --- a/src/logging.h +++ b/src/logging.h @@ -38,7 +38,7 @@ struct LogCategory { }; namespace BCLog { - enum LogFlags : uint32_t { + enum LogFlags : uint64_t { NONE = 0, NET = (1 << 0), TOR = (1 << 1), @@ -72,7 +72,10 @@ namespace BCLog { TXRECONCILIATION = (1 << 27), SCAN = (1 << 28), TXPACKAGES = (1 << 29), - ALL = ~(uint32_t)0, + COINSTAKE = (1 << 30), + HTTPPOLL = ((uint64_t)1 << 31), + INDEX = ((uint64_t)1 << 32), + ALL = ~(uint64_t)0, }; enum class Level { Trace = 0, // High-volume or detailed logging for development/debugging @@ -83,13 +86,25 @@ namespace BCLog { }; constexpr auto DEFAULT_LOG_LEVEL{Level::Debug}; + struct LogMsg + { + LogMsg(const std::string& _msg, bool _useVMLog) : + msg(_msg), + useVMLog(_useVMLog) + {} + + std::string msg; + bool useVMLog; + }; + class Logger { private: mutable StdMutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected FILE* m_fileout GUARDED_BY(m_cs) = nullptr; - std::list m_msgs_before_open GUARDED_BY(m_cs); + FILE* m_fileoutVM GUARDED_BY(m_cs) = nullptr; + std::list m_msgs_before_open GUARDED_BY(m_cs); bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started. /** @@ -107,7 +122,7 @@ namespace BCLog { std::atomic m_log_level{DEFAULT_LOG_LEVEL}; /** Log categories bitfield. */ - std::atomic m_categories{0}; + std::atomic m_categories{0}; std::string LogTimestampStr(const std::string& str); @@ -179,7 +194,7 @@ namespace BCLog { void SetLogLevel(Level level) { m_log_level = level; } bool SetLogLevel(const std::string& level); - uint32_t GetCategoryMask() const { return m_categories.load(); } + uint64_t GetCategoryMask() const { return m_categories.load(); } void EnableCategory(LogFlags flag); bool EnableCategory(const std::string& str); diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 3e8c15a017..fa59faf8f1 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,37 @@ static constexpr uint8_t DB_LAST_BLOCK{'l'}; // BlockTreeDB::DB_TXINDEX{'t'} // BlockTreeDB::ReadFlag("txindex") +////////////////////////////////////////// // qtum +static constexpr uint8_t DB_HEIGHTINDEX{'h'}; +static constexpr uint8_t DB_STAKEINDEX{'s'}; +static constexpr uint8_t DB_DELEGATEINDEX{'d'}; +static constexpr uint8_t DB_ADDRESSINDEX{'a'}; +static constexpr uint8_t DB_ADDRESSUNSPENTINDEX{'u'}; +static constexpr uint8_t DB_TIMESTAMPINDEX{'S'}; +static constexpr uint8_t DB_BLOCKHASHINDEX{'z'}; +static constexpr uint8_t DB_SPENTINDEX{'p'}; + +struct DelegateEntry { + uint160 address; + uint8_t fee; + DelegateEntry(uint160 _address = uint160(), uint8_t _fee = 0) : + address(_address), fee(_fee) + {} + + template + void Serialize(Stream &s) const { + s << address; + s << fee; + } + + template + void Unserialize(Stream& s) { + s >> address; + s >> fee; + } +}; +////////////////////////////////////////// + bool BlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo& info) { return Read(std::make_pair(DB_BLOCK_FILES, nFile), info); @@ -127,13 +159,23 @@ bool BlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, s pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; + pindexNew->nMoneySupply = diskindex.nMoneySupply; pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; + pindexNew->hashStateRoot = diskindex.hashStateRoot; // qtum + pindexNew->hashUTXORoot = diskindex.hashUTXORoot; // qtum + pindexNew->nStakeModifier = diskindex.nStakeModifier; + 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()); } + // NovaCoin: build setStakeSeen + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(std::make_pair(pindexNew->prevoutStake, pindexNew->nTime)); + pcursor->Next(); } else { return error("%s: failed to read value", __func__); @@ -145,10 +187,386 @@ bool BlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, s return true; } + +/////////////////////////////////////////////////////// // qtum +bool BlockTreeDB::WriteHeightIndex(const CHeightTxIndexKey &heightIndex, const std::vector& hash) { + CDBBatch batch(*this); + batch.Write(std::make_pair(DB_HEIGHTINDEX, heightIndex), hash); + return WriteBatch(batch); +} + +int BlockTreeDB::ReadHeightIndex(int low, int high, int minconf, + std::vector> &blocksOfHashes, + std::set const &addresses, ChainstateManager &chainman) { + + if ((high < low && high > -1) || (high == 0 && low == 0) || (high < -1 || low < 0)) { + return -1; + } + + std::unique_ptr pcursor(NewIterator()); + + pcursor->Seek(std::make_pair(DB_HEIGHTINDEX, CHeightTxIndexIteratorKey(low))); + + int curheight = 0; + + for (size_t count = 0; pcursor->Valid(); pcursor->Next()) { + + std::pair key; + if (!pcursor->GetKey(key) || key.first != DB_HEIGHTINDEX) { + break; + } + + int nextHeight = key.second.height; + + if (high > -1 && nextHeight > high) { + break; + } + + if (minconf > 0) { + int conf = chainman.ActiveChain().Height() - nextHeight; + if (conf < minconf) { + break; + } + } + + curheight = nextHeight; + + auto address = key.second.address; + if (!addresses.empty() && addresses.find(address) == addresses.end()) { + continue; + } + + std::vector hashesTx; + + if (!pcursor->GetValue(hashesTx)) { + break; + } + + count += hashesTx.size(); + + blocksOfHashes.push_back(hashesTx); + } + + return curheight; +} + +bool BlockTreeDB::EraseHeightIndex(const unsigned int &height) { + + std::unique_ptr pcursor(NewIterator()); + CDBBatch batch(*this); + + pcursor->Seek(std::make_pair(DB_HEIGHTINDEX, CHeightTxIndexIteratorKey(height))); + + while (pcursor->Valid()) { + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_HEIGHTINDEX && key.second.height == height) { + batch.Erase(key); + pcursor->Next(); + } else { + break; + } + } + + return WriteBatch(batch); +} + +bool BlockTreeDB::WipeHeightIndex() { + + std::unique_ptr pcursor(NewIterator()); + CDBBatch batch(*this); + + pcursor->Seek(DB_HEIGHTINDEX); + + while (pcursor->Valid()) { + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_HEIGHTINDEX) { + batch.Erase(key); + pcursor->Next(); + } else { + break; + } + } + + return WriteBatch(batch); +} + + +bool BlockTreeDB::WriteStakeIndex(unsigned int height, uint160 address) { + CDBBatch batch(*this); + batch.Write(std::make_pair(DB_STAKEINDEX, height), address); + return WriteBatch(batch); +} + +bool BlockTreeDB::ReadStakeIndex(unsigned int height, uint160& address){ + std::unique_ptr pcursor(NewIterator()); + + pcursor->Seek(std::make_pair(DB_STAKEINDEX, height)); + + while (pcursor->Valid()) { + std::pair key; + pcursor->GetKey(key); //note: it's apparently ok if this returns an error https://github.com/bitcoin/bitcoin/issues/7890 + if (key.first == DB_STAKEINDEX) { + pcursor->GetValue(address); + return true; + }else{ + return false; + } + } + return false; +} +bool BlockTreeDB::ReadStakeIndex(unsigned int high, unsigned int low, std::vector addresses){ + std::unique_ptr pcursor(NewIterator()); + + pcursor->Seek(std::make_pair(DB_STAKEINDEX, low)); + + while (pcursor->Valid()) { + std::pair key; + pcursor->GetKey(key); //note: it's apparently ok if this returns an error https://github.com/bitcoin/bitcoin/issues/7890 + if (key.first == DB_STAKEINDEX && key.second.height < high) { + uint160 value; + pcursor->GetValue(value); + addresses.push_back(value); + pcursor->Next(); + } else { + break; + } + } + + return true; +} + +bool BlockTreeDB::EraseStakeIndex(unsigned int height) { + + std::unique_ptr pcursor(NewIterator()); + CDBBatch batch(*this); + + pcursor->Seek(std::make_pair(DB_STAKEINDEX, height)); + + while (pcursor->Valid()) { + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_HEIGHTINDEX && key.second.height == height) { + batch.Erase(key); + pcursor->Next(); + } else { + break; + } + } + + return WriteBatch(batch); +} + +bool BlockTreeDB::WriteDelegateIndex(unsigned int height, uint160 address, uint8_t fee) { + CDBBatch batch(*this); + batch.Write(std::make_pair(DB_DELEGATEINDEX, height), DelegateEntry(address, fee)); + return WriteBatch(batch); +} + +bool BlockTreeDB::ReadDelegateIndex(unsigned int height, uint160& address, uint8_t& fee){ + std::unique_ptr pcursor(NewIterator()); + + pcursor->Seek(std::make_pair(DB_DELEGATEINDEX, height)); + + DelegateEntry info; + while (pcursor->Valid()) { + std::pair key; + pcursor->GetKey(key); + if (key.first == DB_DELEGATEINDEX) { + pcursor->GetValue(info); + address = info.address; + fee = info.fee; + return true; + }else{ + return false; + } + } + return false; +} + +bool BlockTreeDB::EraseDelegateIndex(unsigned int height) { + + std::unique_ptr pcursor(NewIterator()); + CDBBatch batch(*this); + + pcursor->Seek(std::make_pair(DB_DELEGATEINDEX, height)); + + while (pcursor->Valid()) { + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_HEIGHTINDEX && key.second.height == height) { + batch.Erase(key); + pcursor->Next(); + } else { + break; + } + } + + return WriteBatch(batch); +} + +bool BlockTreeDB::WriteAddressIndex(const std::vector >&vect) { + CDBBatch batch(*this); + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Write(std::make_pair(DB_ADDRESSINDEX, it->first), it->second); + return WriteBatch(batch); +} + +bool BlockTreeDB::EraseAddressIndex(const std::vector >&vect) { + CDBBatch batch(*this); + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Erase(std::make_pair(DB_ADDRESSINDEX, it->first)); + return WriteBatch(batch); +} + +bool BlockTreeDB::ReadAddressIndex(uint256 addressHash, int type, + std::vector > &addressIndex, + int start, int end) { + + std::unique_ptr pcursor(NewIterator()); + + if (start > 0 && end > 0) { + pcursor->Seek(std::make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, addressHash, start))); + } else { + pcursor->Seek(std::make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorKey(type, addressHash))); + } + + while (pcursor->Valid()) { + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash) { + if (end > 0 && key.second.blockHeight > end) { + break; + } + CAmount nValue; + if (pcursor->GetValue(nValue)) { + addressIndex.push_back(std::make_pair(key.second, nValue)); + pcursor->Next(); + } else { + return error("failed to get address index value"); + } + } else { + break; + } + } + + return true; +} + +bool BlockTreeDB::UpdateAddressUnspentIndex(const std::vector >&vect) { + CDBBatch batch(*this); + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) { + if (it->second.IsNull()) { + batch.Erase(std::make_pair(DB_ADDRESSUNSPENTINDEX, it->first)); + } else { + batch.Write(std::make_pair(DB_ADDRESSUNSPENTINDEX, it->first), it->second); + } + } + return WriteBatch(batch); +} + +bool BlockTreeDB::ReadAddressUnspentIndex(uint256 addressHash, int type, + std::vector > &unspentOutputs) { + + std::unique_ptr pcursor(NewIterator()); + pcursor->Seek(std::make_pair(DB_ADDRESSUNSPENTINDEX, CAddressIndexIteratorKey(type, addressHash))); + + while (pcursor->Valid()) { + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash) { + CAddressUnspentValue nValue; + if (pcursor->GetValue(nValue)) { + unspentOutputs.push_back(std::make_pair(key.second, nValue)); + pcursor->Next(); + } else { + return error("failed to get address unspent value"); + } + } else { + break; + } + } + + return true; +} + +bool BlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) { + CDBBatch batch(*this); + batch.Write(std::make_pair(DB_TIMESTAMPINDEX, timestampIndex), 0); + return WriteBatch(batch); +} + +bool BlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes, ChainstateManager &chainman) { + + std::unique_ptr pcursor(NewIterator()); + + pcursor->Seek(std::make_pair(DB_TIMESTAMPINDEX, CTimestampIndexIteratorKey(low))); + + while (pcursor->Valid()) { + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp < high) { + if (fActiveOnly) { + if (blockOnchainActive(key.second.blockHash, chainman)) { + hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp)); + } + } else { + hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp)); + } + + pcursor->Next(); + } else { + break; + } + } + + return true; +} + +bool BlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts) { + CDBBatch batch(*this); + batch.Write(std::make_pair(DB_BLOCKHASHINDEX, blockhashIndex), logicalts); + return WriteBatch(batch); +} + +bool BlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) { + + CTimestampBlockIndexValue lts; + if (!Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts)) + return false; + + ltimestamp = lts.ltimestamp; + return true; +} + +bool BlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) { + return Read(std::make_pair(DB_SPENTINDEX, key), value); +} + +bool BlockTreeDB::UpdateSpentIndex(const std::vector >&vect) { + CDBBatch batch(*this); + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) { + if (it->second.IsNull()) { + batch.Erase(std::make_pair(DB_SPENTINDEX, it->first)); + } else { + batch.Write(std::make_pair(DB_SPENTINDEX, it->first), it->second); + } + } + return WriteBatch(batch); +} + +bool BlockTreeDB::blockOnchainActive(const uint256 &hash, ChainstateManager &chainman) { + LOCK(cs_main); + node::BlockMap::iterator mi = chainman.BlockIndex().find(hash); + if (mi == chainman.BlockIndex().end()) + return false; + + CBlockIndex* pblockindex = &(*mi).second; + return pblockindex && chainman.ActiveChain().Contains(pblockindex); +} + bool BlockTreeDB::EraseBlockIndex(const std::vector &vect) { - return {}; + CDBBatch batch(*this); + for (std::vector::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Erase(std::make_pair(DB_BLOCK_INDEX, *it)); + return WriteBatch(batch); } +/////////////////////////////////////////////////////// } // namespace kernel namespace node { @@ -207,7 +625,8 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, CBlockInde { AssertLockHeld(cs_main); - auto [mi, inserted] = m_block_index.try_emplace(block.GetHash(), block); + uint256 hash = block.GetHash(); + auto [mi, inserted] = m_block_index.try_emplace(hash, block); if (!inserted) { return &mi->second; } @@ -218,6 +637,8 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, CBlockInde // competitive advantage. pindexNew->nSequenceId = 0; + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(std::make_pair(pindexNew->prevoutStake, pindexNew->nTime)); pindexNew->phashBlock = &((*mi).first); BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock); if (miPrev != m_block_index.end()) { @@ -555,6 +976,14 @@ bool BlockManager::LoadBlockIndexDB(const std::optional& snapshot_block m_block_tree_db->ReadReindexing(fReindexing); if (fReindexing) fReindex = true; + ///////////////////////////////////////////////////////////// // qtum + m_block_tree_db->ReadFlag("addrindex", fAddressIndex); + LogPrintf("LoadBlockIndexDB(): address index %s\n", fAddressIndex ? "enabled" : "disabled"); + ///////////////////////////////////////////////////////////// + // Check whether we have a transaction index + m_block_tree_db->ReadFlag("logevents", fLogEvents); + LogPrintf("%s: log events index %s\n", __func__, fLogEvents ? "enabled" : "disabled"); + return true; } @@ -1060,7 +1489,8 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid return true; } -bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) const +template +bool BlockManager::ReadBlockFromDisk(Block& block, const FlatFilePos& pos) const { block.SetNull(); @@ -1078,7 +1508,9 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) cons } // Check the header - if (!CheckProofOfWork(block.GetHash(), block.nBits, GetConsensus())) { + // PoS blocks can be loaded out of order from disk, which makes PoS impossible to validate. So, do not validate their headers + // they will be validated later in CheckBlock and ConnectBlock anyway + if (block.IsProofOfWork() && !CheckProofOfWork(block.GetHash(), block.nBits, GetConsensus())) { return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); } diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index c76f437be7..f94f273a93 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -79,7 +79,55 @@ class BlockTreeDB : public CDBWrapper bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function insertBlockIndex, const util::SignalInterrupt& interrupt) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + ////////////////////////////////////////////////////////////////////////////// // qtum + bool WriteHeightIndex(const CHeightTxIndexKey &heightIndex, const std::vector& hash); + + /** + * Iterates through blocks by height, starting from low. + * + * @param low start iterating from this block height + * @param high end iterating at this block height (ignored if <= 0) + * @param minconf stop iterating of the block height does not have enough confirmations (ignored if <= 0) + * @param blocksOfHashes transaction hashes in blocks iterated are collected into this vector. + * @param addresses filter out a block unless it matches one of the addresses in this set. + * + * @return the height of the latest block iterated. 0 if no block is iterated. + */ + int ReadHeightIndex(int low, int high, int minconf, + std::vector> &blocksOfHashes, + std::set const &addresses, ChainstateManager &chainman); + bool EraseHeightIndex(const unsigned int &height); + bool WipeHeightIndex(); + + + bool WriteStakeIndex(unsigned int height, uint160 address); + bool ReadStakeIndex(unsigned int height, uint160& address); + bool ReadStakeIndex(unsigned int high, unsigned int low, std::vector addresses); + bool EraseStakeIndex(unsigned int height); + + bool WriteDelegateIndex(unsigned int height, uint160 address, uint8_t fee); + bool ReadDelegateIndex(unsigned int height, uint160& address, uint8_t& fee); + bool EraseDelegateIndex(unsigned int height); + bool EraseBlockIndex(const std::vector&vect); + + // Block explorer database functions + bool WriteAddressIndex(const std::vector > &vect); + bool EraseAddressIndex(const std::vector > &vect); + bool ReadAddressIndex(uint256 addressHash, int type, + std::vector > &addressIndex, + int start = 0, int end = 0); + bool UpdateAddressUnspentIndex(const std::vector >&vect); + bool ReadAddressUnspentIndex(uint256 addressHash, int type, + std::vector > &vect); + bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex); + bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &vect, ChainstateManager & chainman); + bool WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts); + bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS); + bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); + bool UpdateSpentIndex(const std::vector >&vect); + bool blockOnchainActive(const uint256 &hash, ChainstateManager &chainman); + ////////////////////////////////////////////////////////////////////////////// }; } // namespace kernel @@ -390,7 +438,8 @@ class BlockManager bool CheckSync(int nHeight, const CBlockIndex *pindexBest) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Functions for disk access for blocks */ - bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) const; + template + bool ReadBlockFromDisk(Block& block, const FlatFilePos& pos) const; bool ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) const; bool ReadRawBlockFromDisk(std::vector& block, const FlatFilePos& pos) const; diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp index b372f25ea9..99d46b5d3b 100644 --- a/src/test/blockfilter_tests.cpp +++ b/src/test/blockfilter_tests.cpp @@ -95,9 +95,9 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test) CBlockUndo block_undo; block_undo.vtxundo.emplace_back(); - block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[3]), 1000, true); - block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, included_scripts[4]), 10000, false); - block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[3]), 100000, false); + block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[3]), 1000, true, false); + block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, included_scripts[4]), 10000, false, false); + block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[3]), 100000, false, false); BlockFilter block_filter(BlockFilterType::BASIC, block, block_undo); const GCSFilter& filter = block_filter.GetFilter(); @@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test) for (unsigned int ii = 0; ii < prev_scripts.size(); ii++) { std::vector raw_script = ParseHex(prev_scripts[ii].get_str()); CTxOut txout(0, CScript(raw_script.begin(), raw_script.end())); - tx_undo.vprevout.emplace_back(txout, 0, false); + tx_undo.vprevout.emplace_back(txout, 0, false, false); } uint256 prev_filter_header_basic; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index b6d3e7d567..576c6483d3 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) // Update the expected result to know about the new output coins assert(tx.vout.size() == 1); const COutPoint outpoint(tx.GetHash(), 0); - result[outpoint] = Coin{tx.vout[0], height, CTransaction{tx}.IsCoinBase()}; + result[outpoint] = Coin{tx.vout[0], height, CTransaction{tx}.IsCoinBase(), CTransaction{tx}.IsCoinStake()}; // Call UpdateCoins on the top cache CTxUndo undo; @@ -744,7 +744,7 @@ static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount mo try { CTxOut output; output.nValue = modify_value; - test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, coinbase), coinbase); + test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, coinbase, false), coinbase); test.cache.SelfTest(); GetCoinsMapEntry(test.cache.map(), result_value, result_flags); } catch (std::logic_error&) { diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 8f3e357a84..9cf22b3bc6 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -211,7 +211,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view) const CTransaction transaction{random_mutable_transaction}; bool is_spent = false; for (const CTxOut& tx_out : transaction.vout) { - if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) { + if (Coin{tx_out, 0, transaction.IsCoinBase(), transaction.IsCoinStake()}.IsSpent()) { is_spent = true; } } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index de340f6b6d..ac8e06df37 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -997,7 +997,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { CTransactionRef ptx = mempool.get(outpoint.hash); if (ptx) { if (outpoint.n < ptx->vout.size()) { - coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false); + coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false, false); m_non_base_coins.emplace(outpoint); return true; } else { @@ -1010,7 +1010,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx) { for (unsigned int n = 0; n < tx->vout.size(); ++n) { - m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false)); + m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false, false)); m_non_base_coins.emplace(tx->GetHash(), n); } } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index eeb8becd0a..9a48a0fe96 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2144,7 +2144,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const } const CWalletTx& wtx = mi->second; int prev_height = wtx.state() ? wtx.state()->confirmed_block_height : 0; - coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], prev_height, wtx.IsCoinBase()); + coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], prev_height, wtx.IsCoinBase(), wtx.IsCoinStake()); } std::map input_errors; return SignTransaction(tx, coins, SIGHASH_DEFAULT, input_errors);