diff --git a/contrib/guix/README.md b/contrib/guix/README.md index 93ed69d7dc54..28d570ffa2c2 100644 --- a/contrib/guix/README.md +++ b/contrib/guix/README.md @@ -11,7 +11,7 @@ We achieve bootstrappability by using Guix as a functional package manager. # Requirements -Conservatively, a x86_64 machine with: +Conservatively, you will need an x86_64 machine with: - 16GB of free disk space on the partition that /gnu/store will reside in - 8GB of free disk space **per platform triple** you're planning on building @@ -437,9 +437,8 @@ In the extraordinarily rare case where you messed up your Guix installation in an irreversible way, you may want to completely purge Guix from your system and start over. -1. Uninstall Guix itself according to the way you installed it. (e.g. `sudo apt - purge guix` for Ubuntu packaging, `sudo make uninstall` for - built-from-source). +1. Uninstall Guix itself according to the way you installed it (e.g. `sudo apt + purge guix` for Ubuntu packaging, `sudo make uninstall` for a build from source). 2. Remove all build users and groups You may check for relevant users and groups using: diff --git a/doc/release-notes-22407.md b/doc/release-notes-22407.md new file mode 100644 index 000000000000..f4233fd1b48b --- /dev/null +++ b/doc/release-notes-22407.md @@ -0,0 +1,4 @@ +Updated RPC +-------- + +- `getblockchaininfo` now returns a new `time` field, that provides the chain tip time. diff --git a/src/addrman.cpp b/src/addrman.cpp index ec1b0fcc7d20..091677c22912 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -144,7 +144,7 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in return &mapInfo[nId]; } -void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) +void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const { AssertLockHeld(cs); @@ -156,11 +156,13 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) int nId1 = vRandom[nRndPos1]; int nId2 = vRandom[nRndPos2]; - assert(mapInfo.count(nId1) == 1); - assert(mapInfo.count(nId2) == 1); + const auto it_1{mapInfo.find(nId1)}; + const auto it_2{mapInfo.find(nId2)}; + assert(it_1 != mapInfo.end()); + assert(it_2 != mapInfo.end()); - mapInfo[nId1].nRandomPos = nRndPos2; - mapInfo[nId2].nRandomPos = nRndPos1; + it_1->second.nRandomPos = nRndPos2; + it_2->second.nRandomPos = nRndPos1; vRandom[nRndPos1] = nId2; vRandom[nRndPos2] = nId1; @@ -425,7 +427,7 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) } } -CAddrInfo CAddrMan::Select_(bool newOnly) +CAddrInfo CAddrMan::Select_(bool newOnly) const { AssertLockHeld(cs); @@ -448,8 +450,9 @@ CAddrInfo CAddrMan::Select_(bool newOnly) nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE; } int nId = vvTried[nKBucket][nKBucketPos]; - assert(mapInfo.count(nId) == 1); - CAddrInfo& info = mapInfo[nId]; + const auto it_found{mapInfo.find(nId)}; + assert(it_found != mapInfo.end()); + const CAddrInfo& info{it_found->second}; if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) return info; fChanceFactor *= 1.2; @@ -465,8 +468,9 @@ CAddrInfo CAddrMan::Select_(bool newOnly) nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE; } int nId = vvNew[nUBucket][nUBucketPos]; - assert(mapInfo.count(nId) == 1); - CAddrInfo& info = mapInfo[nId]; + const auto it_found{mapInfo.find(nId)}; + assert(it_found != mapInfo.end()); + const CAddrInfo& info{it_found->second}; if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) return info; fChanceFactor *= 1.2; @@ -517,15 +521,15 @@ int CAddrMan::Check_() for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) { for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) { - if (vvTried[n][i] != -1) { - if (!setTried.count(vvTried[n][i])) - return -11; - if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n) - return -17; - if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i) - return -18; - setTried.erase(vvTried[n][i]); - } + if (vvTried[n][i] != -1) { + if (!setTried.count(vvTried[n][i])) + return -11; + if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n) + return -17; + if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i) + return -18; + setTried.erase(vvTried[n][i]); + } } } @@ -553,7 +557,7 @@ int CAddrMan::Check_() } #endif -void CAddrMan::GetAddr_(std::vector& vAddr, size_t max_addresses, size_t max_pct, std::optional network) +void CAddrMan::GetAddr_(std::vector& vAddr, size_t max_addresses, size_t max_pct, std::optional network) const { AssertLockHeld(cs); @@ -573,9 +577,10 @@ void CAddrMan::GetAddr_(std::vector& vAddr, size_t max_addresses, size int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n; SwapRandom(n, nRndPos); - assert(mapInfo.count(vRandom[n]) == 1); + const auto it{mapInfo.find(vRandom[n])}; + assert(it != mapInfo.end()); - const CAddrInfo& ai = mapInfo[vRandom[n]]; + const CAddrInfo& ai{it->second}; // Filter by network (optional) if (network != std::nullopt && ai.GetNetClass() != network) continue; diff --git a/src/addrman.h b/src/addrman.h index 7aa6590a58ee..efbdbc051714 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -55,7 +55,7 @@ class CAddrInfo : public CAddress bool fInTried{false}; //! position in vRandom - int nRandomPos{-1}; + mutable int nRandomPos{-1}; friend class CAddrMan; @@ -544,12 +544,12 @@ class CAddrMan } //! Mark an entry as accessible. - void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime()) + void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) EXCLUSIVE_LOCKS_REQUIRED(!cs) { LOCK(cs); Check(); - Good_(addr, test_before_evict, nTime); + Good_(addr, /* test_before_evict */ true, nTime); Check(); } @@ -587,7 +587,7 @@ class CAddrMan /** * Choose an address to connect to. */ - CAddrInfo Select(bool newOnly = false) + CAddrInfo Select(bool newOnly = false) const EXCLUSIVE_LOCKS_REQUIRED(!cs) { LOCK(cs); @@ -604,7 +604,7 @@ class CAddrMan * @param[in] max_pct Maximum percentage of addresses to return (0 = all). * @param[in] network Select only addresses of this network (nullopt = all). */ - std::vector GetAddr(size_t max_addresses, size_t max_pct, std::optional network) + std::vector GetAddr(size_t max_addresses, size_t max_pct, std::optional network) const EXCLUSIVE_LOCKS_REQUIRED(!cs) { LOCK(cs); @@ -650,12 +650,12 @@ class CAddrMan uint256 nKey; //! Source of random numbers for randomization in inner loops - FastRandomContext insecure_rand; + mutable FastRandomContext insecure_rand GUARDED_BY(cs); -private: //! A mutex to protect the inner data structures. mutable Mutex cs; +private: //! Serialization versions. enum Format : uint8_t { V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88 @@ -688,7 +688,9 @@ class CAddrMan std::map mapAddr GUARDED_BY(cs); //! randomly-ordered vector of all nIds - std::vector vRandom GUARDED_BY(cs); + //! This is mutable because it is unobservable outside the class, so any + //! changes to it (even in const methods) are also unobservable. + mutable std::vector vRandom GUARDED_BY(cs); // number of "tried" entries int nTried GUARDED_BY(cs); @@ -718,7 +720,7 @@ class CAddrMan CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Swap two elements in vRandom. - void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs); + void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs); //! Move an entry from the "new" table(s) to the "tried" table void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs); @@ -739,7 +741,7 @@ class CAddrMan void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. - CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs); + CAddrInfo Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs); //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs); @@ -748,7 +750,7 @@ class CAddrMan CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs); //! Consistency check - void Check() + void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs) { #ifdef DEBUG_ADDRMAN @@ -762,7 +764,7 @@ class CAddrMan #ifdef DEBUG_ADDRMAN //! Perform consistency check. Returns an error code or zero. - int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs); + int Check_() const EXCLUSIVE_LOCKS_REQUIRED(cs); #endif /** @@ -773,7 +775,7 @@ class CAddrMan * @param[in] max_pct Maximum percentage of addresses to return (0 = all). * @param[in] network Select only addresses of this network (nullopt = all). */ - void GetAddr_(std::vector& vAddr, size_t max_addresses, size_t max_pct, std::optional network) EXCLUSIVE_LOCKS_REQUIRED(cs); + void GetAddr_(std::vector& vAddr, size_t max_addresses, size_t max_pct, std::optional network) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** We have successfully connected to this peer. Calling this function * updates the CAddress's nTime, which is used in our IsTerrible() diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index f13fe5767a25..a63acb1aeb79 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -145,7 +145,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in return nSigOps; } -unsigned int GetTransactionSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs, int flags) +unsigned int GetTransactionSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags) { unsigned int nSigOps = GetLegacySigOpCount(tx); diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index a2dcf362fc0f..894d5c82e444 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -52,7 +52,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma * @param[out] flags Script verification flags * @return Total signature operation count for a tx */ -unsigned int GetTransactionSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs, int flags); +unsigned int GetTransactionSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags); /** * Check if transaction is final and can be included in a block with the diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index 51c82a221f9c..8b80ac9b4890 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -4,8 +4,10 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include +#include #include #include #include @@ -90,3 +92,38 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t return TransactionError::OK; } + +CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock) +{ + LOCK(cs_main); + + if (mempool && !block_index) { + CTransactionRef ptx = mempool->get(hash); + if (ptx) return ptx; + } + if (g_txindex) { + CTransactionRef tx; + uint256 block_hash; + if (g_txindex->FindTx(hash, block_hash, tx)) { + if (!block_index || block_index->GetBlockHash() == block_hash) { + // Don't return the transaction if the provided block hash doesn't match. + // The case where a transaction appears in multiple blocks (e.g. reorgs or + // BIP30) is handled by the block lookup below. + hashBlock = block_hash; + return tx; + } + } + } + if (block_index) { + CBlock block; + if (ReadBlockFromDisk(block, block_index, consensusParams)) { + for (const auto& tx : block.vtx) { + if (tx->GetHash() == hash) { + hashBlock = block_index->GetBlockHash(); + return tx; + } + } + } + } + return nullptr; +} diff --git a/src/node/transaction.h b/src/node/transaction.h index 69aba271ce8f..7da45f602865 100644 --- a/src/node/transaction.h +++ b/src/node/transaction.h @@ -10,7 +10,12 @@ #include #include +class CBlockIndex; +class CTxMemPool; struct NodeContext; +namespace Consensus { +struct Params; +} /** Maximum fee rate for sendrawtransaction and testmempoolaccept RPC calls. * Also used by the GUI when broadcasting a completed PSBT. @@ -38,4 +43,19 @@ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10}; */ [[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& highfee, bool relay, bool wait_callback, bool bypass_limits = false); +/** + * Return transaction with a given hash. + * If mempool is provided and block_index is not provided, check it first for the tx. + * If -txindex is available, check it next for the tx. + * Finally, if block_index is provided, check for tx by reading entire block from disk. + * + * @param[in] block_index The block to read from disk, or nullptr + * @param[in] mempool If provided, check mempool for tx + * @param[in] hash The txid + * @param[in] consensusParams The params + * @param[out] hashBlock The block hash, if the tx was found via -txindex or block_index + * @returns The tx if found, otherwise nullptr + */ +CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); + #endif // BITCOIN_NODE_TRANSACTION_H diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 0f3c3a2caa90..25e396b77745 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1662,7 +1662,8 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"}, {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"}, {RPCResult::Type::NUM, "difficulty", "the current difficulty"}, - {RPCResult::Type::NUM, "mediantime", "median time for the current best block"}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"}, {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"}, {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"}, @@ -1720,6 +1721,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); obj.pushKV("difficulty", (double)GetDifficulty(tip)); + obj.pushKV("time", (int64_t)tip->nTime); obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast()); obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload()); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index bcda6799c515..9e942a022e78 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -113,11 +113,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) RPCHelpMan{ "getrawtransaction", "\nReturn the raw transaction data.\n" - "\nBy default this function only works for mempool transactions. When called with a blockhash\n" - "argument, getrawtransaction will return the transaction if the specified block is available and\n" - "the transaction is found in that block. When called without a blockhash argument, getrawtransaction\n" - "will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction\n" - "is in a block in the blockchain.\n" + + "\nBy default, this call only returns a transaction if it is in the mempool. If -txindex is enabled\n" + "and no blockhash argument is passed, it will return the transaction if it is in the mempool or any block.\n" + "If a blockhash argument is passed, it will return the transaction if\n" + "the specified block is available and the transaction is in that block.\n" "\nHint: Use gettransaction for wallet transactions.\n" "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 929e50275dc2..7ebc6eb91b79 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -32,8 +32,7 @@ enum * All flags are intended to be soft forks: the set of acceptable scripts under * flags (A | B) is a subset of the acceptable scripts under flag (A). */ -enum -{ +enum : uint32_t { SCRIPT_VERIFY_NONE = 0, // Evaluate P2SH subscripts (BIP16). @@ -102,6 +101,10 @@ enum // Making OP_CODESEPARATOR and FindAndDelete fail // SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16), + + // Constants to point to the highest flag in use. Add new flags above this line. + // + SCRIPT_VERIFY_END_MARKER }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index fbde09ee8f9d..52300422e94a 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -35,6 +35,7 @@ class CAddrManTest : public CAddrMan //! Ensure that bucket placement is always the same for testing purposes. void MakeDeterministic() { + LOCK(cs); nKey.SetNull(); insecure_rand = FastRandomContext(true); } @@ -77,7 +78,7 @@ class CAddrManTest : public CAddrMan { int64_t nLastSuccess = 1; // Set last good connection in the deep past. - Good(addr, true, nLastSuccess); + Good(addr, nLastSuccess); bool count_failure = false; int64_t nLastTry = GetAdjustedTime()-61; @@ -88,11 +89,11 @@ class CAddrManTest : public CAddrMan { CAddrMan::Clear(); if (deterministic) { + LOCK(cs); nKey.SetNull(); insecure_rand = FastRandomContext(true); } } - }; static CNetAddr ResolveIP(const std::string& ip) diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 108ce541e501..ce871642f427 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -27,7 +27,7 @@ class CAddrManDeterministic : public CAddrMan public: void MakeDeterministic(const uint256& random_seed) { - insecure_rand = FastRandomContext{random_seed}; + WITH_LOCK(cs, insecure_rand = FastRandomContext{random_seed}); Clear(); } }; @@ -100,7 +100,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman) [&] { const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); if (opt_service) { - addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + addr_man.Good(*opt_service, ConsumeTime(fuzzed_data_provider)); } }, [&] { diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 624bec097681..db5e0ddb8c65 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -251,7 +251,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view) // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. return; } - const int flags = fuzzed_data_provider.ConsumeIntegral(); + const auto flags{fuzzed_data_provider.ConsumeIntegral()}; if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_P2SH) == 0) { // Avoid: // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed. diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 2d77313a7d98..a0d04aec49f3 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -36,6 +36,7 @@ class CAddrManSerializationMock : public CAddrMan //! Ensure that bucket placement is always the same for testing purposes. void MakeDeterministic() { + LOCK(cs); nKey.SetNull(); insecure_rand = FastRandomContext(true); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 879139789c0d..c27ac5317b20 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -123,7 +123,7 @@ static ScriptError_t ParseScriptError(const std::string& name) BOOST_FIXTURE_TEST_SUITE(script_tests, BasicTestingSetup) -void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, const std::string& message, int scriptError) +void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, uint32_t flags, const std::string& message, int scriptError) { bool expect = (scriptError == SCRIPT_ERR_OK); bool fEnableDIP0020Opcodes = (SCRIPT_ENABLE_DIP0020_OPCODES & flags) != 0; @@ -136,8 +136,8 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, co // Verify that removing flags from a passing test or adding flags to a failing test does not change the result. for (int i = 0; i < 16; ++i) { - int extra_flags = InsecureRandBits(16); - int combined_flags = expect ? (flags & ~extra_flags) : (flags | extra_flags); + uint32_t extra_flags(InsecureRandBits(16)); + uint32_t combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)}; // Weed out some invalid flag combinations. if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & SCRIPT_VERIFY_P2SH) continue; // Make sure DIP0020 opcodes flag stays unchanged. @@ -148,7 +148,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, co #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << tx2; - int libconsensus_flags = flags & dashconsensus_SCRIPT_FLAGS_VERIFY_ALL; + uint32_t libconsensus_flags{flags & dashconsensus_SCRIPT_FLAGS_VERIFY_ALL}; if (libconsensus_flags == flags) { int expectedSuccessCode = expect ? 1 : 0; BOOST_CHECK_MESSAGE(dashconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message); @@ -240,7 +240,7 @@ class TestBuilder bool havePush; std::vector push; std::string comment; - int flags; + uint32_t flags; int scriptError; void DoPush() @@ -259,7 +259,7 @@ class TestBuilder } public: - TestBuilder(const CScript& script_, const std::string& comment_, int flags_, bool P2SH = false) : scriptPubKey(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK) + TestBuilder(const CScript& script_, const std::string& comment_, uint32_t flags_, bool P2SH = false) : scriptPubKey(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK) { if (P2SH) { creditTx = MakeTransactionRef(BuildCreditingTransaction(CScript() << OP_HASH160 << ToByteVector(CScriptID(script_)) << OP_EQUAL)); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 68366a50cdfa..3d33725ee5f5 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -108,10 +108,15 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { PrecomputedTransactionData txdata; - // If we add many more flags, this loop can get too expensive, but we can - // rewrite in the future to randomly pick a set of flags to evaluate. - for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) { + + FastRandomContext insecure_rand(true); + + for (int count = 0; count < 10000; ++count) { TxValidationState state; + + // Randomly selects flag combinations + uint32_t test_flags = (uint32_t) insecure_rand.randrange((SCRIPT_VERIFY_END_MARKER - 1) << 1); + // Filter out incompatible flag choices if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { // CLEANSTACK requires P2SH, see VerifyScript() in diff --git a/src/validation.cpp b/src/validation.cpp index 4d8bd7d33ddd..7858f944ff46 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -1068,32 +1067,6 @@ bool GetAddressUnspent(uint160 addressHash, AddressType type, return true; } -CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock) -{ - LOCK(cs_main); - - if (block_index) { - CBlock block; - if (ReadBlockFromDisk(block, block_index, consensusParams)) { - for (const auto& tx : block.vtx) { - if (tx->GetHash() == hash) { - hashBlock = block_index->GetBlockHash(); - return tx; - } - } - } - return nullptr; - } - if (mempool) { - CTransactionRef ptx = mempool->get(hash); - if (ptx) return ptx; - } - if (g_txindex) { - CTransactionRef tx; - if (g_txindex->FindTx(hash, hashBlock, tx)) return tx; - } - return nullptr; -} double ConvertBitsToDouble(unsigned int nBits) { diff --git a/src/validation.h b/src/validation.h index 15638bbe74c4..1695be307388 100644 --- a/src/validation.h +++ b/src/validation.h @@ -184,18 +184,7 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman); void StartScriptCheckWorkerThreads(int threads_num); /** Stop all of the script checking worker threads */ void StopScriptCheckWorkerThreads(); -/** - * Return transaction from the block at block_index. - * If block_index is not provided, fall back to mempool. - * If mempool is not provided or the tx couldn't be found in mempool, fall back to g_txindex. - * - * @param[in] block_index The block to read from disk, or nullptr - * @param[in] mempool If block_index is not provided, look in the mempool, if provided - * @param[in] hash The txid - * @param[in] consensusParams The params - * @param[out] hashBlock The hash of block_index, if the tx was found via block_index - * @returns The tx if found, otherwise nullptr - */ + CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); double ConvertBitsToDouble(unsigned int nBits); diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index d9d0d58c45e7..19ac7544e4f8 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -93,11 +93,14 @@ def _test_getblockchaininfo(self): 'pruned', 'size_on_disk', 'softforks', + 'time', 'verificationprogress', 'warnings', ] res = self.nodes[0].getblockchaininfo() + assert isinstance(res['time'], int) + # result should have these additional pruning keys if manual pruning is enabled assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys)) diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 84c40f857d2f..044d6d2a0352 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -422,5 +422,15 @@ def run_test(self): assert_equal(testres['allowed'], True) self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.20000000') + self.log.info('sendrawtransaction/testmempoolaccept with tx that is already in the chain') + self.nodes[2].generate(1) + self.sync_blocks() + for node in self.nodes: + testres = node.testmempoolaccept([rawTxSigned['hex']])[0] + assert_equal(testres['allowed'], False) + assert_equal(testres['reject-reason'], 'txn-already-known') + assert_raises_rpc_error(-27, 'Transaction already in block chain', node.sendrawtransaction, rawTxSigned['hex']) + + if __name__ == '__main__': RawTransactionsTest().main() diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py index 4961b46e577e..72d7902880e8 100644 --- a/test/functional/test_framework/coverage.py +++ b/test/functional/test_framework/coverage.py @@ -10,6 +10,7 @@ import os +from .authproxy import AuthServiceProxy REFERENCE_FILENAME = 'rpc_interface.txt' @@ -19,16 +20,17 @@ class AuthServiceProxyWrapper(): An object that wraps AuthServiceProxy to record specific RPC calls. """ - def __init__(self, auth_service_proxy_instance, coverage_logfile=None): + def __init__(self, auth_service_proxy_instance: AuthServiceProxy, rpc_url: str, coverage_logfile: str=None): """ Kwargs: - auth_service_proxy_instance (AuthServiceProxy): the instance - being wrapped. - coverage_logfile (str): if specified, write each service_name + auth_service_proxy_instance: the instance being wrapped. + rpc_url: url of the RPC instance being wrapped + coverage_logfile: if specified, write each service_name out to a file when called. """ self.auth_service_proxy_instance = auth_service_proxy_instance + self.rpc_url = rpc_url self.coverage_logfile = coverage_logfile def __getattr__(self, name): @@ -36,7 +38,7 @@ def __getattr__(self, name): if not isinstance(return_val, type(self.auth_service_proxy_instance)): # If proxy getattr returned an unwrapped value, do the same here. return return_val - return AuthServiceProxyWrapper(return_val, self.coverage_logfile) + return AuthServiceProxyWrapper(return_val, self.rpc_url, self.coverage_logfile) def __call__(self, *args, **kwargs): """ @@ -57,6 +59,7 @@ def _log_call(self): def __truediv__(self, relative_uri): return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri, + self.rpc_url, self.coverage_logfile) def get_request(self, *args, **kwargs): @@ -74,18 +77,18 @@ def get_filename(dirname, n_node): dirname, "coverage.pid%s.node%s.txt" % (pid, str(n_node))) -def write_all_rpc_commands(dirname, node): +def write_all_rpc_commands(dirname: str, node: AuthServiceProxy) -> bool: """ Write out a list of all RPC functions available in `dash-cli` for coverage comparison. This will only happen once per coverage directory. Args: - dirname (str): temporary test dir - node (AuthServiceProxy): client + dirname: temporary test dir + node: client Returns: - bool. if the RPC interface file was written. + if the RPC interface file was written. """ filename = os.path.join(dirname, REFERENCE_FILENAME) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index c7cf46b03c3a..be471b7f0a20 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -271,7 +271,7 @@ def wait_for_rpc_connection(self): return self.rpc = rpc self.rpc_connected = True - self.url = self.rpc.url + self.url = self.rpc.rpc_url return except JSONRPCException as e: # Initialization phase # -28 RPC in warmup diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index ac6703174d6d..f0ba1fd9fa04 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -260,15 +260,16 @@ class PortSeed: # Must be initialized with a unique integer for each process n = None -def get_rpc_proxy(url, node_number, *, timeout=None, coveragedir=None): + +def get_rpc_proxy(url: str, node_number: int, *, timeout: int=None, coveragedir: str=None) -> coverage.AuthServiceProxyWrapper: """ Args: - url (str): URL of the RPC server to call - node_number (int): the node number (or id) that this calls to + url: URL of the RPC server to call + node_number: the node number (or id) that this calls to Kwargs: - timeout (int): HTTP timeout in seconds - coveragedir (str): Directory + timeout: HTTP timeout in seconds + coveragedir: Directory Returns: AuthServiceProxy. convenience object for making RPC calls. @@ -279,12 +280,11 @@ def get_rpc_proxy(url, node_number, *, timeout=None, coveragedir=None): proxy_kwargs['timeout'] = int(timeout) proxy = AuthServiceProxy(url, **proxy_kwargs) - proxy.url = url # store URL on proxy for info coverage_logfile = coverage.get_filename( coveragedir, node_number) if coveragedir else None - return coverage.AuthServiceProxyWrapper(proxy, coverage_logfile) + return coverage.AuthServiceProxyWrapper(proxy, url, coverage_logfile) def p2p_port(n): assert n <= MAX_NODES diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 82c2bb234547..753667768225 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -10,7 +10,6 @@ export LC_ALL=C EXPECTED_CIRCULAR_DEPENDENCIES=( "chainparamsbase -> util/system -> chainparamsbase" - "index/txindex -> validation -> index/txindex" "node/blockstorage -> validation -> node/blockstorage" "index/blockfilterindex -> node/blockstorage -> validation -> index/blockfilterindex" "index/base -> validation -> index/blockfilterindex -> index/base"