Merge #6296: backport: merge bitcoin#22772, #22987, #23185, #23157, #23173, #23249, #23211, #22677, #23649, #23683, #23636, #22626 (auxiliary backports: part 17)

8c3ff618d3 chore: apply some `clang-format-diff.py` suggestions (Kittywhiskers Van Gogh)
aa1f56f126 merge bitcoin#22626: Remove txindex migration code (Kittywhiskers Van Gogh)
145d94d700 merge bitcoin#23636: Remove GetAdjustedTime from init.cpp (Kittywhiskers Van Gogh)
150ca008fe merge bitcoin#23683: valid but different LockPoints after a reorg (Kittywhiskers Van Gogh)
e85862ba11 merge bitcoin#23649: circular dependency followups (Kittywhiskers Van Gogh)
8ab99290f9 merge bitcoin#22677: cut the validation <-> txmempool circular dependency (Kittywhiskers Van Gogh)
ee49383cd6 merge bitcoin#23211: move `update_*` structs from txmempool.h to .cpp file (Kittywhiskers Van Gogh)
3d769c7a64 merge bitcoin#23249: ParseByteUnits - Parse a string with suffix unit (Kittywhiskers Van Gogh)
edd0bab6b5 chore: remove superfluous `ParseHDKeypath` definition (Kittywhiskers Van Gogh)
0073b66aaa refactor: migrate some Dash code to use `ChainstateManager::ProcessTransaction` (Kittywhiskers Van Gogh)
c8571c0956 merge bitcoin#23173: Add `ChainstateManager::ProcessTransaction` (Kittywhiskers Van Gogh)
a21bfd02e9 merge bitcoin#23157: improve performance of check() and remove dependency on validation (Kittywhiskers Van Gogh)
b35dc7236d merge bitcoin#23185: Add ParseMoney and ParseScript tests (Kittywhiskers Van Gogh)
7c03133be3 merge bitcoin#22987: Fix "RuntimeError: Event loop is closed" on Windows (Kittywhiskers Van Gogh)
ba60d5459e merge bitcoin#22772: hasher cleanup (follow-up to bitcoin#19935) (Kittywhiskers Van Gogh)

Pull request description:

  ## Additional Information

  * When backporting [bitcoin#23173](https://github.com/bitcoin/bitcoin/pull/23173), `bypass_limits` had to be extended to `ChainstateManager::ProcessTransaction()` as Dash allows the `sendrawtransaction` RPC to bypass limits with the optional `bypasslimits` boolean (introduced in [dash#2110](https://github.com/dashpay/dash/pull/2110)).

    The bool arguments are not in alphabetical order to prevent breakage with Bitcoin code that expects `bypass_limits` to always be `false`.

  ## Breaking Changes

  None expected.

  ## Checklist

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)**
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  UdjinM6:
    utACK 8c3ff618d3
  PastaPastaPasta:
    utACK 8c3ff618d3

Tree-SHA512: ea1eaad7279b6608a07c1175e5c3b44385d42e33afa8ce5160d871fc9b37a014e9981eafca379ac3ad6dc141b5fda6f1e676b4cc9658a6d1775fe929a134ff67
This commit is contained in:
pasta 2024-10-07 15:00:10 -05:00
commit 5cba8e29fc
No known key found for this signature in database
GPG Key ID: E2F3D7916E722D38
55 changed files with 642 additions and 498 deletions

View File

@ -0,0 +1,6 @@
Updated settings
----------------
- `-maxuploadtarget` now allows human readable byte units [k|K|m|M|g|G|t|T].
E.g. `-maxuploadtarget=500g`. No whitespace, +- or fractions allowed.
Default is `M` if no suffix provided.

View File

@ -150,8 +150,9 @@ BITCOIN_TESTS =\
test/script_p2sh_tests.cpp \ test/script_p2sh_tests.cpp \
test/script_p2pk_tests.cpp \ test/script_p2pk_tests.cpp \
test/script_p2pkh_tests.cpp \ test/script_p2pkh_tests.cpp \
test/script_tests.cpp \ test/script_parse_tests.cpp \
test/script_standard_tests.cpp \ test/script_standard_tests.cpp \
test/script_tests.cpp \
test/scriptnum_tests.cpp \ test/scriptnum_tests.cpp \
test/serfloat_tests.cpp \ test/serfloat_tests.cpp \
test/serialize_tests.cpp \ test/serialize_tests.cpp \

View File

@ -39,10 +39,10 @@ static void AssembleBlock(benchmark::Bench& bench)
txs.at(b) = MakeTransactionRef(tx); txs.at(b) = MakeTransactionRef(tx);
} }
{ {
LOCK(::cs_main); // Required for ::AcceptToMemoryPool. LOCK(::cs_main);
for (const auto& txr : txs) { for (const auto& txr : txs) {
const MempoolAcceptResult res = ::AcceptToMemoryPool(test_setup->m_node.chainman->ActiveChainstate(), *test_setup->m_node.mempool, txr, false /* bypass_limits */); const MempoolAcceptResult res = test_setup->m_node.chainman->ProcessTransaction(txr);
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID); assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
} }
} }

View File

@ -6,6 +6,7 @@
#include <policy/policy.h> #include <policy/policy.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <txmempool.h> #include <txmempool.h>
#include <validation.h>
#include <vector> #include <vector>
@ -26,14 +27,8 @@ struct Available {
Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){} Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}
}; };
static void ComplexMemPool(benchmark::Bench& bench) static std::vector<CTransactionRef> CreateOrderedCoins(FastRandomContext& det_rand, int childTxs, int min_ancestors)
{ {
int childTxs = 800;
if (bench.complexityN() > 1) {
childTxs = static_cast<int>(bench.complexityN());
}
FastRandomContext det_rand{true};
std::vector<Available> available_coins; std::vector<Available> available_coins;
std::vector<CTransactionRef> ordered_coins; std::vector<CTransactionRef> ordered_coins;
// Create some base transactions // Create some base transactions
@ -57,8 +52,10 @@ static void ComplexMemPool(benchmark::Bench& bench)
size_t idx = det_rand.randrange(available_coins.size()); size_t idx = det_rand.randrange(available_coins.size());
Available coin = available_coins[idx]; Available coin = available_coins[idx];
uint256 hash = coin.ref->GetHash(); uint256 hash = coin.ref->GetHash();
// biased towards taking just one ancestor, but maybe more // biased towards taking min_ancestors parents, but maybe more
size_t n_to_take = det_rand.randrange(2) == 0 ? 1 : 1+det_rand.randrange(coin.ref->vout.size() - coin.vin_left); size_t n_to_take = det_rand.randrange(2) == 0 ?
min_ancestors :
min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
for (size_t i = 0; i < n_to_take; ++i) { for (size_t i = 0; i < n_to_take; ++i) {
tx.vin.emplace_back(); tx.vin.emplace_back();
tx.vin.back().prevout = COutPoint(hash, coin.vin_left++); tx.vin.back().prevout = COutPoint(hash, coin.vin_left++);
@ -77,6 +74,17 @@ static void ComplexMemPool(benchmark::Bench& bench)
ordered_coins.emplace_back(MakeTransactionRef(tx)); ordered_coins.emplace_back(MakeTransactionRef(tx));
available_coins.emplace_back(ordered_coins.back(), tx_counter++); available_coins.emplace_back(ordered_coins.back(), tx_counter++);
} }
return ordered_coins;
}
static void ComplexMemPool(benchmark::Bench& bench)
{
FastRandomContext det_rand{true};
int childTxs = 800;
if (bench.complexityN() > 1) {
childTxs = static_cast<int>(bench.complexityN());
}
std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 1);
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN); const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
CTxMemPool pool; CTxMemPool pool;
LOCK2(cs_main, pool.cs); LOCK2(cs_main, pool.cs);
@ -89,4 +97,21 @@ static void ComplexMemPool(benchmark::Bench& bench)
}); });
} }
static void MempoolCheck(benchmark::Bench& bench)
{
FastRandomContext det_rand{true};
const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000;
const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 5);
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN, {"-checkmempool=1"});
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();
for (auto& tx : ordered_coins) AddTx(tx, pool);
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
pool.check(coins_tip, /* spendheight */ 2);
});
}
BENCHMARK(ComplexMemPool); BENCHMARK(ComplexMemPool);
BENCHMARK(MempoolCheck);

View File

@ -793,7 +793,8 @@ bool CCoinJoinClientManager::CheckAutomaticBackup()
// //
// Passively run mixing in the background to mix funds based on the given configuration. // Passively run mixing in the background to mix funds based on the given configuration.
// //
bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun) bool CCoinJoinClientSession::DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman,
const CTxMemPool& mempool, bool fDryRun)
{ {
if (m_is_masternode) return false; // no client-side mixing on masternodes if (m_is_masternode) return false; // no client-side mixing on masternodes
if (nState != POOL_STATE_IDLE) return false; if (nState != POOL_STATE_IDLE) return false;
@ -946,7 +947,7 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainst
return false; return false;
} }
} else { } else {
if (!CoinJoin::IsCollateralValid(active_chainstate, mempool, CTransaction(txMyCollateral))) { if (!CoinJoin::IsCollateralValid(chainman, mempool, CTransaction(txMyCollateral))) {
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n"); WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n");
if (!CreateCollateralTransaction(txMyCollateral, strReason)) { if (!CreateCollateralTransaction(txMyCollateral, strReason)) {
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason); WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason);
@ -973,7 +974,8 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainst
return false; return false;
} }
bool CCoinJoinClientManager::DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun) bool CCoinJoinClientManager::DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman,
const CTxMemPool& mempool, bool fDryRun)
{ {
if (m_is_masternode) return false; // no client-side mixing on masternodes if (m_is_masternode) return false; // no client-side mixing on masternodes
if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return false; if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return false;
@ -1016,7 +1018,7 @@ bool CCoinJoinClientManager::DoAutomaticDenominating(CChainState& active_chainst
return false; return false;
} }
fResult &= session.DoAutomaticDenominating(active_chainstate, connman, mempool, fDryRun); fResult &= session.DoAutomaticDenominating(chainman, connman, mempool, fDryRun);
} }
return fResult; return fResult;
@ -1864,7 +1866,7 @@ void CCoinJoinClientQueueManager::DoMaintenance()
CheckQueue(); CheckQueue();
} }
void CCoinJoinClientManager::DoMaintenance(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool) void CCoinJoinClientManager::DoMaintenance(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool)
{ {
if (!CCoinJoinClientOptions::IsEnabled()) return; if (!CCoinJoinClientOptions::IsEnabled()) return;
if (m_is_masternode) return; // no client-side mixing on masternodes if (m_is_masternode) return; // no client-side mixing on masternodes
@ -1878,7 +1880,7 @@ void CCoinJoinClientManager::DoMaintenance(CChainState& active_chainstate, CConn
CheckTimeout(); CheckTimeout();
ProcessPendingDsaRequest(connman); ProcessPendingDsaRequest(connman);
if (nDoAutoNextRun == nTick) { if (nDoAutoNextRun == nTick) {
DoAutomaticDenominating(active_chainstate, connman, mempool); DoAutomaticDenominating(chainman, connman, mempool);
nDoAutoNextRun = nTick + COINJOIN_AUTO_TIMEOUT_MIN + GetRandInt(COINJOIN_AUTO_TIMEOUT_MAX - COINJOIN_AUTO_TIMEOUT_MIN); nDoAutoNextRun = nTick + COINJOIN_AUTO_TIMEOUT_MIN + GetRandInt(COINJOIN_AUTO_TIMEOUT_MAX - COINJOIN_AUTO_TIMEOUT_MIN);
} }
} }
@ -1930,7 +1932,7 @@ void CoinJoinWalletManager::DoMaintenance()
{ {
LOCK(cs_wallet_manager_map); LOCK(cs_wallet_manager_map);
for (auto& [_, clientman] : m_wallet_manager_map) { for (auto& [_, clientman] : m_wallet_manager_map) {
clientman->DoMaintenance(m_chainstate, m_connman, m_mempool); clientman->DoMaintenance(m_chainman, m_connman, m_mempool);
} }
} }

View File

@ -22,9 +22,10 @@ class CCoinJoinClientQueueManager;
class CConnman; class CConnman;
class CDeterministicMN; class CDeterministicMN;
class CDeterministicMNManager; class CDeterministicMNManager;
class CNode; class ChainstateManager;
class CMasternodeMetaMan; class CMasternodeMetaMan;
class CMasternodeSync; class CMasternodeSync;
class CNode;
class CoinJoinWalletManager; class CoinJoinWalletManager;
class CTxMemPool; class CTxMemPool;
@ -74,10 +75,17 @@ public:
using wallet_name_cjman_map = std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>>; using wallet_name_cjman_map = std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>>;
public: public:
CoinJoinWalletManager(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool, CoinJoinWalletManager(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
const CMasternodeSync& mn_sync, const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode) CMasternodeMetaMan& mn_metaman, const CTxMemPool& mempool, const CMasternodeSync& mn_sync,
: m_chainstate(chainstate), m_connman(connman), m_dmnman(dmnman), m_mn_metaman(mn_metaman), m_mempool(mempool), m_mn_sync(mn_sync), const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode) :
m_queueman(queueman), m_is_masternode{is_masternode} m_chainman(chainman),
m_connman(connman),
m_dmnman(dmnman),
m_mn_metaman(mn_metaman),
m_mempool(mempool),
m_mn_sync(mn_sync),
m_queueman(queueman),
m_is_masternode{is_masternode}
{} {}
~CoinJoinWalletManager() { ~CoinJoinWalletManager() {
@ -112,11 +120,11 @@ public:
}; };
private: private:
CChainState& m_chainstate; ChainstateManager& m_chainman;
CConnman& m_connman; CConnman& m_connman;
CDeterministicMNManager& m_dmnman; CDeterministicMNManager& m_dmnman;
CMasternodeMetaMan& m_mn_metaman; CMasternodeMetaMan& m_mn_metaman;
CTxMemPool& m_mempool; const CTxMemPool& m_mempool;
const CMasternodeSync& m_mn_sync; const CMasternodeSync& m_mn_sync;
const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman; const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
@ -202,7 +210,8 @@ public:
bool GetMixingMasternodeInfo(CDeterministicMNCPtr& ret) const; bool GetMixingMasternodeInfo(CDeterministicMNCPtr& ret) const;
/// Passively run mixing in the background according to the configuration in settings /// Passively run mixing in the background according to the configuration in settings
bool DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin); bool DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool,
bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin);
/// As a client, submit part of a future mixing transaction to a Masternode to start the process /// As a client, submit part of a future mixing transaction to a Masternode to start the process
bool SubmitDenominate(CConnman& connman); bool SubmitDenominate(CConnman& connman);
@ -310,7 +319,8 @@ public:
bool GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); bool GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
/// Passively run mixing in the background according to the configuration in settings /// Passively run mixing in the background according to the configuration in settings
bool DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); bool DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool,
bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
bool TrySubmitDenominate(const CService& mnAddr, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); bool TrySubmitDenominate(const CService& mnAddr, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
bool MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); bool MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
@ -326,7 +336,8 @@ public:
void UpdatedBlockTip(const CBlockIndex* pindex); void UpdatedBlockTip(const CBlockIndex* pindex);
void DoMaintenance(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); void DoMaintenance(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool)
EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
void GetJsonInfo(UniValue& obj) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); void GetJsonInfo(UniValue& obj) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
}; };

View File

@ -308,10 +308,11 @@ bool CCoinJoinBaseSession::IsValidInOuts(CChainState& active_chainstate, const C
// Responsibility for checking fee sanity is moved from the mempool to the client (BroadcastTransaction) // Responsibility for checking fee sanity is moved from the mempool to the client (BroadcastTransaction)
// but CoinJoin still requires ATMP with fee sanity checks so we need to implement them separately // but CoinJoin still requires ATMP with fee sanity checks so we need to implement them separately
bool ATMPIfSaneFee(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef &tx, bool test_accept) { bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept)
{
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
const MempoolAcceptResult result = AcceptToMemoryPool(active_chainstate, pool, tx, /* bypass_limits */ false, /* test_accept */ true); const MempoolAcceptResult result = chainman.ProcessTransaction(tx, /*test_accept=*/true);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
/* Fetch fee and fast-fail if ATMP fails regardless */ /* Fetch fee and fast-fail if ATMP fails regardless */
return false; return false;
@ -322,11 +323,11 @@ bool ATMPIfSaneFee(CChainState& active_chainstate, CTxMemPool& pool, const CTran
/* Don't re-run ATMP if only doing test run */ /* Don't re-run ATMP if only doing test run */
return true; return true;
} }
return AcceptToMemoryPool(active_chainstate, pool, tx, /* bypass_limits */ false, test_accept).m_result_type == MempoolAcceptResult::ResultType::VALID; return chainman.ProcessTransaction(tx, test_accept).m_result_type == MempoolAcceptResult::ResultType::VALID;
} }
// check to make sure the collateral provided by the client is valid // check to make sure the collateral provided by the client is valid
bool CoinJoin::IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mempool, const CTransaction& txCollateral) bool CoinJoin::IsCollateralValid(ChainstateManager& chainman, const CTxMemPool& mempool, const CTransaction& txCollateral)
{ {
if (txCollateral.vout.empty()) return false; if (txCollateral.vout.empty()) return false;
if (txCollateral.nLockTime != 0) return false; if (txCollateral.nLockTime != 0) return false;
@ -352,7 +353,7 @@ bool CoinJoin::IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mem
return false; return false;
} }
nValueIn += mempoolTx->vout[txin.prevout.n].nValue; nValueIn += mempoolTx->vout[txin.prevout.n].nValue;
} else if (GetUTXOCoin(active_chainstate, txin.prevout, coin)) { } else if (GetUTXOCoin(chainman.ActiveChainstate(), txin.prevout, coin)) {
nValueIn += coin.out.nValue; nValueIn += coin.out.nValue;
} else { } else {
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); /* Continued */ LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); /* Continued */
@ -370,8 +371,8 @@ bool CoinJoin::IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mem
{ {
LOCK(cs_main); LOCK(cs_main);
if (!ATMPIfSaneFee(active_chainstate, mempool, MakeTransactionRef(txCollateral), /*test_accept=*/true)) { if (!ATMPIfSaneFee(chainman, MakeTransactionRef(txCollateral), /*test_accept=*/true)) {
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n"); LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- didn't pass ATMPIfSaneFee()\n");
return false; return false;
} }
} }

View File

@ -27,6 +27,7 @@ class CChainState;
class CConnman; class CConnman;
class CBLSPublicKey; class CBLSPublicKey;
class CBlockIndex; class CBlockIndex;
class ChainstateManager;
class CMasternodeSync; class CMasternodeSync;
class CTxMemPool; class CTxMemPool;
class TxValidationState; class TxValidationState;
@ -368,8 +369,7 @@ namespace CoinJoin
constexpr CAmount GetMaxPoolAmount() { return COINJOIN_ENTRY_MAX_SIZE * vecStandardDenominations.front(); } constexpr CAmount GetMaxPoolAmount() { return COINJOIN_ENTRY_MAX_SIZE * vecStandardDenominations.front(); }
/// If the collateral is valid given by a client /// If the collateral is valid given by a client
bool IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mempool, const CTransaction& txCollateral); bool IsCollateralValid(ChainstateManager& chainman, const CTxMemPool& mempool, const CTransaction& txCollateral);
} }
class CDSTXManager class CDSTXManager
@ -402,7 +402,7 @@ private:
}; };
bool ATMPIfSaneFee(CChainState& active_chainstate, CTxMemPool& pool, bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept = false)
const CTransactionRef &tx, bool test_accept = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); EXCLUSIVE_LOCKS_REQUIRED(cs_main);
#endif // BITCOIN_COINJOIN_COINJOIN_H #endif // BITCOIN_COINJOIN_COINJOIN_H

View File

@ -9,20 +9,20 @@
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
#include <coinjoin/server.h> #include <coinjoin/server.h>
CJContext::CJContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CJContext::CJContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync, const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
std::unique_ptr<PeerManager>& peerman, bool relay_txes) : std::unique_ptr<PeerManager>& peerman, bool relay_txes) :
dstxman{std::make_unique<CDSTXManager>()}, dstxman{std::make_unique<CDSTXManager>()},
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
walletman{std::make_unique<CoinJoinWalletManager>(chainstate, connman, dmnman, mn_metaman, mempool, mn_sync, walletman{std::make_unique<CoinJoinWalletManager>(chainman, connman, dmnman, mn_metaman, mempool, mn_sync, queueman,
queueman, /* is_masternode = */ mn_activeman != nullptr)}, /* is_masternode = */ mn_activeman != nullptr)},
queueman{relay_txes queueman{relay_txes
? std::make_unique<CCoinJoinClientQueueManager>(connman, peerman, *walletman, dmnman, mn_metaman, ? std::make_unique<CCoinJoinClientQueueManager>(connman, peerman, *walletman, dmnman, mn_metaman,
mn_sync, /* is_masternode = */ mn_activeman != nullptr) mn_sync, /* is_masternode = */ mn_activeman != nullptr)
: nullptr}, : nullptr},
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
server{std::make_unique<CCoinJoinServer>(chainstate, connman, dmnman, *dstxman, mn_metaman, mempool, mn_activeman, server{std::make_unique<CCoinJoinServer>(chainman, connman, dmnman, *dstxman, mn_metaman, mempool, mn_activeman,
mn_sync, peerman)} mn_sync, peerman)}
{} {}

View File

@ -13,11 +13,11 @@
class CActiveMasternodeManager; class CActiveMasternodeManager;
class CBlockPolicyEstimator; class CBlockPolicyEstimator;
class CChainState;
class CCoinJoinServer; class CCoinJoinServer;
class CConnman; class CConnman;
class CDeterministicMNManager; class CDeterministicMNManager;
class CDSTXManager; class CDSTXManager;
class ChainstateManager;
class CMasternodeMetaMan; class CMasternodeMetaMan;
class CMasternodeSync; class CMasternodeSync;
class CTxMemPool; class CTxMemPool;
@ -31,7 +31,7 @@ class CoinJoinWalletManager;
struct CJContext { struct CJContext {
CJContext() = delete; CJContext() = delete;
CJContext(const CJContext&) = delete; CJContext(const CJContext&) = delete;
CJContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CJContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman,
const CMasternodeSync& mn_sync, std::unique_ptr<PeerManager>& peerman, bool relay_txes); const CMasternodeSync& mn_sync, std::unique_ptr<PeerManager>& peerman, bool relay_txes);
~CJContext(); ~CJContext();

View File

@ -331,8 +331,8 @@ void CCoinJoinServer::CommitFinalTransaction()
// See if the transaction is valid // See if the transaction is valid
TRY_LOCK(cs_main, lockMain); TRY_LOCK(cs_main, lockMain);
mempool.PrioritiseTransaction(hashTx, 0.1 * COIN); mempool.PrioritiseTransaction(hashTx, 0.1 * COIN);
if (!lockMain || !ATMPIfSaneFee(m_chainstate, mempool, finalTransaction)) { if (!lockMain || !ATMPIfSaneFee(m_chainman, finalTransaction)) {
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n"); LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- ATMPIfSaneFee() error: Transaction not valid\n");
WITH_LOCK(cs_coinjoin, SetNull()); WITH_LOCK(cs_coinjoin, SetNull());
// not much we can do in this case, just notify clients // not much we can do in this case, just notify clients
RelayCompletedTransaction(ERR_INVALID_TX); RelayCompletedTransaction(ERR_INVALID_TX);
@ -463,8 +463,8 @@ void CCoinJoinServer::ChargeRandomFees() const
void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const
{ {
LOCK(cs_main); LOCK(cs_main);
if (!ATMPIfSaneFee(m_chainstate, mempool, txref, false /* bypass_limits */)) { if (!ATMPIfSaneFee(m_chainman, txref)) {
LogPrint(BCLog::COINJOIN, "%s -- AcceptToMemoryPool failed\n", __func__); LogPrint(BCLog::COINJOIN, "%s -- ATMPIfSaneFee failed\n", __func__);
} else { } else {
Assert(m_peerman)->RelayTransaction(txref->GetHash()); Assert(m_peerman)->RelayTransaction(txref->GetHash());
LogPrint(BCLog::COINJOIN, "%s -- Collateral was consumed\n", __func__); LogPrint(BCLog::COINJOIN, "%s -- Collateral was consumed\n", __func__);
@ -582,7 +582,7 @@ bool CCoinJoinServer::AddEntry(const CCoinJoinEntry& entry, PoolMessage& nMessag
return false; return false;
} }
if (!CoinJoin::IsCollateralValid(m_chainstate, mempool, *entry.txCollateral)) { if (!CoinJoin::IsCollateralValid(m_chainman, mempool, *entry.txCollateral)) {
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR: collateral not valid!\n", __func__); LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR: collateral not valid!\n", __func__);
nMessageIDRet = ERR_INVALID_COLLATERAL; nMessageIDRet = ERR_INVALID_COLLATERAL;
return false; return false;
@ -616,7 +616,7 @@ bool CCoinJoinServer::AddEntry(const CCoinJoinEntry& entry, PoolMessage& nMessag
} }
bool fConsumeCollateral{false}; bool fConsumeCollateral{false};
if (!IsValidInOuts(m_chainstate, mempool, vin, entry.vecTxOut, nMessageIDRet, &fConsumeCollateral)) { if (!IsValidInOuts(m_chainman.ActiveChainstate(), mempool, vin, entry.vecTxOut, nMessageIDRet, &fConsumeCollateral)) {
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR! IsValidInOuts() failed: %s\n", __func__, CoinJoin::GetMessageByID(nMessageIDRet).translated); LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR! IsValidInOuts() failed: %s\n", __func__, CoinJoin::GetMessageByID(nMessageIDRet).translated);
if (fConsumeCollateral) { if (fConsumeCollateral) {
ConsumeCollateral(entry.txCollateral); ConsumeCollateral(entry.txCollateral);
@ -693,7 +693,7 @@ bool CCoinJoinServer::IsAcceptableDSA(const CCoinJoinAccept& dsa, PoolMessage& n
} }
// check collateral // check collateral
if (!fUnitTest && !CoinJoin::IsCollateralValid(m_chainstate, mempool, CTransaction(dsa.txCollateral))) { if (!fUnitTest && !CoinJoin::IsCollateralValid(m_chainman, mempool, CTransaction(dsa.txCollateral))) {
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- collateral not valid!\n", __func__); LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- collateral not valid!\n", __func__);
nMessageIDRet = ERR_INVALID_COLLATERAL; nMessageIDRet = ERR_INVALID_COLLATERAL;
return false; return false;

View File

@ -10,11 +10,11 @@
#include <protocol.h> #include <protocol.h>
class CActiveMasternodeManager; class CActiveMasternodeManager;
class CChainState;
class CCoinJoinServer; class CCoinJoinServer;
class CDataStream; class CDataStream;
class CDeterministicMNManager; class CDeterministicMNManager;
class CDSTXManager; class CDSTXManager;
class ChainstateManager;
class CMasternodeMetaMan; class CMasternodeMetaMan;
class CNode; class CNode;
class CTxMemPool; class CTxMemPool;
@ -27,7 +27,7 @@ class UniValue;
class CCoinJoinServer : public CCoinJoinBaseSession, public CCoinJoinBaseManager class CCoinJoinServer : public CCoinJoinBaseSession, public CCoinJoinBaseManager
{ {
private: private:
CChainState& m_chainstate; ChainstateManager& m_chainman;
CConnman& connman; CConnman& connman;
CDeterministicMNManager& m_dmnman; CDeterministicMNManager& m_dmnman;
CDSTXManager& m_dstxman; CDSTXManager& m_dstxman;
@ -90,11 +90,11 @@ private:
void SetNull() override EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin); void SetNull() override EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
public: public:
explicit CCoinJoinServer(CChainState& chainstate, CConnman& _connman, CDeterministicMNManager& dmnman, explicit CCoinJoinServer(ChainstateManager& chainman, CConnman& _connman, CDeterministicMNManager& dmnman,
CDSTXManager& dstxman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool, CDSTXManager& dstxman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync, const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
std::unique_ptr<PeerManager>& peerman) : std::unique_ptr<PeerManager>& peerman) :
m_chainstate(chainstate), m_chainman(chainman),
connman(_connman), connman(_connman),
m_dmnman(dmnman), m_dmnman(dmnman),
m_dstxman(dstxman), m_dstxman(dstxman),

View File

@ -31,6 +31,7 @@ enum class TxValidationResult {
TX_CONFLICT, TX_CONFLICT,
TX_CONFLICT_LOCK, //!< conflicts with InstantSend lock TX_CONFLICT_LOCK, //!< conflicts with InstantSend lock
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/etc limits TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/etc limits
TX_NO_MEMPOOL, //!< this node does not have a mempool so can't validate the transaction
}; };
/** A "reason" why a block was invalid, suitable for determining whether the /** A "reason" why a block was invalid, suitable for determining whether the

View File

@ -23,6 +23,7 @@
#include <protocol.h> #include <protocol.h>
#include <shutdown.h> #include <shutdown.h>
#include <spork.h> #include <spork.h>
#include <timedata.h>
#include <util/ranges.h> #include <util/ranges.h>
#include <util/time.h> #include <util/time.h>
#include <validation.h> #include <validation.h>

View File

@ -12,6 +12,7 @@
#include <masternode/sync.h> #include <masternode/sync.h>
#include <messagesigner.h> #include <messagesigner.h>
#include <net_processing.h> #include <net_processing.h>
#include <timedata.h>
#include <util/string.h> #include <util/string.h>
#include <util/system.h> #include <util/system.h>
#include <validation.h> #include <validation.h>

View File

@ -6,13 +6,12 @@
#define BITCOIN_INDEX_BASE_H #define BITCOIN_INDEX_BASE_H
#include <dbwrapper.h> #include <dbwrapper.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <threadinterrupt.h> #include <threadinterrupt.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <atomic> #include <atomic>
class CBlock;
class CBlockIndex; class CBlockIndex;
class CChainState; class CChainState;

View File

@ -2,18 +2,14 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <index/disktxpos.h>
#include <index/txindex.h> #include <index/txindex.h>
#include <index/disktxpos.h>
#include <node/blockstorage.h> #include <node/blockstorage.h>
#include <node/ui_interface.h>
#include <shutdown.h>
#include <util/system.h> #include <util/system.h>
#include <util/translation.h>
#include <validation.h> #include <validation.h>
constexpr uint8_t DB_BEST_BLOCK{'B'};
constexpr uint8_t DB_TXINDEX{'t'}; constexpr uint8_t DB_TXINDEX{'t'};
constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
std::unique_ptr<TxIndex> g_txindex; std::unique_ptr<TxIndex> g_txindex;
@ -30,10 +26,6 @@ public:
/// Write a batch of transaction positions to the DB. /// Write a batch of transaction positions to the DB.
bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos); bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos);
/// Migrate txindex data from the block tree DB, where it may be for older nodes that have not
/// been upgraded yet to the new database.
bool MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator);
}; };
TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) : TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
@ -54,163 +46,12 @@ bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_
return WriteBatch(batch); return WriteBatch(batch);
} }
/*
* Safely persist a transfer of data from the old txindex database to the new one, and compact the
* range of keys updated. This is used internally by MigrateData.
*/
static void WriteTxIndexMigrationBatches(CDBWrapper& newdb, CDBWrapper& olddb,
CDBBatch& batch_newdb, CDBBatch& batch_olddb,
const std::pair<uint8_t, uint256>& begin_key,
const std::pair<uint8_t, uint256>& end_key)
{
// Sync new DB changes to disk before deleting from old DB.
newdb.WriteBatch(batch_newdb, /*fSync=*/ true);
olddb.WriteBatch(batch_olddb);
olddb.CompactRange(begin_key, end_key);
batch_newdb.Clear();
batch_olddb.Clear();
}
bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator)
{
// The prior implementation of txindex was always in sync with block index
// and presence was indicated with a boolean DB flag. If the flag is set,
// this means the txindex from a previous version is valid and in sync with
// the chain tip. The first step of the migration is to unset the flag and
// write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the
// index entries are copied over in batches to the new database. Finally,
// DB_TXINDEX_BLOCK is erased from the old database and the block hash is
// written to the new database.
//
// Unsetting the boolean flag ensures that if the node is downgraded to a
// previous version, it will not see a corrupted, partially migrated index
// -- it will see that the txindex is disabled. When the node is upgraded
// again, the migration will pick up where it left off and sync to the block
// with hash DB_TXINDEX_BLOCK.
bool f_legacy_flag = false;
block_tree_db.ReadFlag("txindex", f_legacy_flag);
if (f_legacy_flag) {
if (!block_tree_db.Write(DB_TXINDEX_BLOCK, best_locator)) {
return error("%s: cannot write block indicator", __func__);
}
if (!block_tree_db.WriteFlag("txindex", false)) {
return error("%s: cannot write block index db flag", __func__);
}
}
CBlockLocator locator;
if (!block_tree_db.Read(DB_TXINDEX_BLOCK, locator)) {
return true;
}
int64_t count = 0;
LogPrintf("Upgrading txindex database... [0%%]\n");
uiInterface.ShowProgress(_("Upgrading txindex database").translated, 0, true);
int report_done = 0;
const size_t batch_size = 1 << 24; // 16 MiB
CDBBatch batch_newdb(*this);
CDBBatch batch_olddb(block_tree_db);
std::pair<uint8_t, uint256> key;
std::pair<uint8_t, uint256> begin_key{DB_TXINDEX, uint256()};
std::pair<uint8_t, uint256> prev_key = begin_key;
bool interrupted = false;
std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) {
if (ShutdownRequested()) {
interrupted = true;
break;
}
if (!cursor->GetKey(key)) {
return error("%s: cannot get key from valid cursor", __func__);
}
if (key.first != DB_TXINDEX) {
break;
}
// Log progress every 10%.
if (++count % 256 == 0) {
// Since txids are uniformly random and traversed in increasing order, the high 16 bits
// of the hash can be used to estimate the current progress.
const uint256& txid = key.second;
uint32_t high_nibble =
(static_cast<uint32_t>(*(txid.begin() + 0)) << 8) +
(static_cast<uint32_t>(*(txid.begin() + 1)) << 0);
int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5);
uiInterface.ShowProgress(_("Upgrading txindex database").translated, percentage_done, true);
if (report_done < percentage_done/10) {
LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done);
report_done = percentage_done/10;
}
}
CDiskTxPos value;
if (!cursor->GetValue(value)) {
return error("%s: cannot parse txindex record", __func__);
}
batch_newdb.Write(key, value);
batch_olddb.Erase(key);
if (batch_newdb.SizeEstimate() > batch_size || batch_olddb.SizeEstimate() > batch_size) {
// NOTE: it's OK to delete the key pointed at by the current DB cursor while iterating
// because LevelDB iterators are guaranteed to provide a consistent view of the
// underlying data, like a lightweight snapshot.
WriteTxIndexMigrationBatches(*this, block_tree_db,
batch_newdb, batch_olddb,
prev_key, key);
prev_key = key;
}
}
// If these final DB batches complete the migration, write the best block
// hash marker to the new database and delete from the old one. This signals
// that the former is fully caught up to that point in the blockchain and
// that all txindex entries have been removed from the latter.
if (!interrupted) {
batch_olddb.Erase(DB_TXINDEX_BLOCK);
batch_newdb.Write(DB_BEST_BLOCK, locator);
}
WriteTxIndexMigrationBatches(*this, block_tree_db,
batch_newdb, batch_olddb,
begin_key, key);
if (interrupted) {
LogPrintf("[CANCELLED].\n");
return false;
}
uiInterface.ShowProgress("", 100, false);
LogPrintf("[DONE].\n");
return true;
}
TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe) TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
: m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe)) : m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
{} {}
TxIndex::~TxIndex() {} TxIndex::~TxIndex() {}
bool TxIndex::Init()
{
LOCK(cs_main);
// Attempt to migrate txindex from the old database to the new one. Even if
// chain_tip is null, the node could be reindexing and we still want to
// delete txindex records in the old database.
if (!m_db->MigrateData(*m_chainstate->m_blockman.m_block_tree_db, m_chainstate->m_chain.GetLocator())) {
return false;
}
return BaseIndex::Init();
}
bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
{ {
// Exclude genesis block transaction because outputs are not spendable. // Exclude genesis block transaction because outputs are not spendable.

View File

@ -5,9 +5,7 @@
#ifndef BITCOIN_INDEX_TXINDEX_H #ifndef BITCOIN_INDEX_TXINDEX_H
#define BITCOIN_INDEX_TXINDEX_H #define BITCOIN_INDEX_TXINDEX_H
#include <chain.h>
#include <index/base.h> #include <index/base.h>
#include <txdb.h>
/** /**
* TxIndex is used to look up transactions included in the blockchain by hash. * TxIndex is used to look up transactions included in the blockchain by hash.
@ -23,9 +21,6 @@ private:
const std::unique_ptr<DB> m_db; const std::unique_ptr<DB> m_db;
protected: protected:
/// Override base class init to migrate from old database.
bool Init() override;
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
BaseIndex::DB& GetDB() const override; BaseIndex::DB& GetDB() const override;

View File

@ -583,7 +583,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection memory usage for the send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection memory usage for the send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target per 24h. Limit does not apply to peers with 'download' permission or blocks created within past week. 0 = no limit (default: %s). Optional suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 base while uppercase is 1024 base", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@ -1440,6 +1440,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
{ {
const ArgsManager& args = *Assert(node.args); const ArgsManager& args = *Assert(node.args);
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
auto opt_max_upload = ParseByteUnits(args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET), ByteUnit::M);
if (!opt_max_upload) {
return InitError(strprintf(_("Unable to parse -maxuploadtarget: '%s' (possible integer overflow?)"), args.GetArg("-maxuploadtarget", "")));
}
// ********************************************************* Step 4a: application initialization // ********************************************************* Step 4a: application initialization
if (!CreatePidFile(args)) { if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile(). // Detailed error printed inside CreatePidFile().
@ -1891,7 +1897,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
node.llmq_ctx->Stop(); node.llmq_ctx->Stop();
} }
node.llmq_ctx.reset(); node.llmq_ctx.reset();
node.llmq_ctx = std::make_unique<LLMQContext>(chainman.ActiveChainstate(), *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman, node.llmq_ctx = std::make_unique<LLMQContext>(chainman, *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman,
*node.mempool, node.mn_activeman.get(), *node.mn_sync, node.peerman, /* unit_tests = */ false, /* wipe = */ fReset || fReindexChainState); *node.mempool, node.mn_activeman.get(), *node.mn_sync, node.peerman, /* unit_tests = */ false, /* wipe = */ fReset || fReindexChainState);
// Enable CMNHFManager::{Process, Undo}Block // Enable CMNHFManager::{Process, Undo}Block
node.mnhf_manager->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get()); node.mnhf_manager->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get());
@ -2050,7 +2056,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
const CBlockIndex* tip = chainstate->m_chain.Tip(); const CBlockIndex* tip = chainstate->m_chain.Tip();
RPCNotifyBlockChange(tip); RPCNotifyBlockChange(tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
strLoadError = _("The block database contains a block which appears to be from the future. " strLoadError = _("The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. " "This may be due to your computer's date and time being set incorrectly. "
"Only rebuild the block database if you are sure that your computer's date and time are correct"); "Only rebuild the block database if you are sure that your computer's date and time are correct");
@ -2153,7 +2159,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 7c: Setup CoinJoin // ********************************************************* Step 7c: Setup CoinJoin
node.cj_ctx = std::make_unique<CJContext>(chainman.ActiveChainstate(), *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool, node.cj_ctx = std::make_unique<CJContext>(chainman, *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool,
node.mn_activeman.get(), *node.mn_sync, node.peerman, !ignores_incoming_txs); node.mn_activeman.get(), *node.mn_sync, node.peerman, !ignores_incoming_txs);
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
@ -2193,6 +2199,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 8: start indexers // ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LOCK(::cs_main);
if (const auto error{CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db))}) {
return InitError(*error);
}
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex); g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
if (!g_txindex->Start(chainman.ActiveChainstate())) { if (!g_txindex->Start(chainman.ActiveChainstate())) {
return false; return false;
@ -2388,7 +2399,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode"); connOptions.m_added_nodes = args.GetArgs("-addnode");
connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET); connOptions.nMaxOutboundLimit = *opt_max_upload;
connOptions.m_peer_connect_timeout = peer_connect_timeout; connOptions.m_peer_connect_timeout = peer_connect_timeout;
// Port to bind to if `-bind=addr` is provided without a `:port` suffix. // Port to bind to if `-bind=addr` is provided without a `:port` suffix.

View File

@ -5,6 +5,7 @@
#include <llmq/context.h> #include <llmq/context.h>
#include <dbwrapper.h> #include <dbwrapper.h>
#include <validation.h>
#include <llmq/blockprocessor.h> #include <llmq/blockprocessor.h>
#include <llmq/chainlocks.h> #include <llmq/chainlocks.h>
@ -17,36 +18,41 @@
#include <llmq/signing.h> #include <llmq/signing.h>
#include <llmq/signing_shares.h> #include <llmq/signing_shares.h>
LLMQContext::LLMQContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CEvoDB& evo_db, LLMQContext::LLMQContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman, CEvoDB& evo_db, CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman,
CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman, CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman,
const CMasternodeSync& mn_sync, const std::unique_ptr<PeerManager>& peerman, bool unit_tests, const CMasternodeSync& mn_sync, const std::unique_ptr<PeerManager>& peerman, bool unit_tests,
bool wipe) : bool wipe) :
is_masternode{mn_activeman != nullptr}, is_masternode{mn_activeman != nullptr},
bls_worker{std::make_shared<CBLSWorker>()}, bls_worker{std::make_shared<CBLSWorker>()},
dkg_debugman{std::make_unique<llmq::CDKGDebugManager>()}, dkg_debugman{std::make_unique<llmq::CDKGDebugManager>()},
quorum_block_processor{std::make_unique<llmq::CQuorumBlockProcessor>(chainstate, dmnman, evo_db)}, quorum_block_processor{std::make_unique<llmq::CQuorumBlockProcessor>(chainman.ActiveChainstate(), dmnman, evo_db)},
qdkgsman{std::make_unique<llmq::CDKGSessionManager>(*bls_worker, chainstate, connman, dmnman, *dkg_debugman, qdkgsman{std::make_unique<llmq::CDKGSessionManager>(*bls_worker, chainman.ActiveChainstate(), connman, dmnman,
mn_metaman, *quorum_block_processor, mn_activeman, sporkman, *dkg_debugman, mn_metaman, *quorum_block_processor,
peerman, unit_tests, wipe)}, mn_activeman, sporkman, peerman, unit_tests, wipe)},
qman{std::make_unique<llmq::CQuorumManager>(*bls_worker, chainstate, connman, dmnman, *qdkgsman, evo_db, qman{std::make_unique<llmq::CQuorumManager>(*bls_worker, chainman.ActiveChainstate(), connman, dmnman, *qdkgsman,
*quorum_block_processor, mn_activeman, mn_sync, sporkman, unit_tests, evo_db, *quorum_block_processor, mn_activeman, mn_sync, sporkman,
wipe)}, unit_tests, wipe)},
sigman{std::make_unique<llmq::CSigningManager>(connman, mn_activeman, chainstate, *qman, peerman, unit_tests, wipe)}, sigman{std::make_unique<llmq::CSigningManager>(connman, mn_activeman, chainman.ActiveChainstate(), *qman, peerman,
unit_tests, wipe)},
shareman{std::make_unique<llmq::CSigSharesManager>(connman, *sigman, mn_activeman, *qman, sporkman, peerman)}, shareman{std::make_unique<llmq::CSigSharesManager>(connman, *sigman, mn_activeman, *qman, sporkman, peerman)},
clhandler{[&]() -> llmq::CChainLocksHandler* const { clhandler{[&]() -> llmq::CChainLocksHandler* const {
assert(llmq::chainLocksHandler == nullptr); assert(llmq::chainLocksHandler == nullptr);
llmq::chainLocksHandler = std::make_unique<llmq::CChainLocksHandler>(chainstate, *qman, *sigman, *shareman, llmq::chainLocksHandler = std::make_unique<llmq::CChainLocksHandler>(chainman.ActiveChainstate(), *qman,
sporkman, mempool, mn_sync, is_masternode); *sigman, *shareman, sporkman, mempool,
mn_sync, is_masternode);
return llmq::chainLocksHandler.get(); return llmq::chainLocksHandler.get();
}()}, }()},
isman{[&]() -> llmq::CInstantSendManager* const { isman{[&]() -> llmq::CInstantSendManager* const {
assert(llmq::quorumInstantSendManager == nullptr); assert(llmq::quorumInstantSendManager == nullptr);
llmq::quorumInstantSendManager = std::make_unique<llmq::CInstantSendManager>(*llmq::chainLocksHandler, chainstate, connman, *qman, *sigman, *shareman, sporkman, mempool, mn_sync, peerman, is_masternode, unit_tests, wipe); llmq::quorumInstantSendManager = std::make_unique<llmq::CInstantSendManager>(*llmq::chainLocksHandler,
chainman.ActiveChainstate(),
connman, *qman, *sigman, *shareman,
sporkman, mempool, mn_sync, peerman,
is_masternode, unit_tests, wipe);
return llmq::quorumInstantSendManager.get(); return llmq::quorumInstantSendManager.get();
}()}, }()},
ehfSignalsHandler{ ehfSignalsHandler{std::make_unique<llmq::CEHFSignalsHandler>(chainman, mnhfman, *sigman, *shareman, *qman)}
std::make_unique<llmq::CEHFSignalsHandler>(chainstate, mnhfman, *sigman, *shareman, mempool, *qman)}
{ {
} }

View File

@ -9,8 +9,8 @@
class CActiveMasternodeManager; class CActiveMasternodeManager;
class CBLSWorker; class CBLSWorker;
class CChainState;
class CConnman; class CConnman;
class ChainstateManager;
class CDeterministicMNManager; class CDeterministicMNManager;
class CDBWrapper; class CDBWrapper;
class CEvoDB; class CEvoDB;
@ -40,7 +40,7 @@ private:
public: public:
LLMQContext() = delete; LLMQContext() = delete;
LLMQContext(const LLMQContext&) = delete; LLMQContext(const LLMQContext&) = delete;
LLMQContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CEvoDB& evo_db, LLMQContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman, CEvoDB& evo_db,
CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman, CTxMemPool& mempool, CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman, CTxMemPool& mempool,
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync, const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
const std::unique_ptr<PeerManager>& peerman, bool unit_tests, bool wipe); const std::unique_ptr<PeerManager>& peerman, bool unit_tests, bool wipe);

View File

@ -15,19 +15,17 @@
#include <deploymentstatus.h> #include <deploymentstatus.h>
#include <index/txindex.h> // g_txindex #include <index/txindex.h> // g_txindex
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <txmempool.h>
#include <validation.h> #include <validation.h>
namespace llmq { namespace llmq {
CEHFSignalsHandler::CEHFSignalsHandler(CChainState& chainstate, CMNHFManager& mnhfman, CSigningManager& sigman, CEHFSignalsHandler::CEHFSignalsHandler(ChainstateManager& chainman, CMNHFManager& mnhfman, CSigningManager& sigman,
CSigSharesManager& shareman, CTxMemPool& mempool, const CQuorumManager& qman) : CSigSharesManager& shareman, const CQuorumManager& qman) :
chainstate(chainstate), m_chainman(chainman),
mnhfman(mnhfman), mnhfman(mnhfman),
sigman(sigman), sigman(sigman),
shareman(shareman), shareman(shareman),
mempool(mempool),
qman(qman) qman(qman)
{ {
sigman.RegisterRecoveredSigsListener(this); sigman.RegisterRecoveredSigsListener(this);
@ -77,7 +75,7 @@ void CEHFSignalsHandler::trySignEHFSignal(int bit, const CBlockIndex* const pind
return; return;
} }
const auto quorum = llmq::SelectQuorumForSigning(llmq_params_opt.value(), chainstate.m_chain, qman, requestId); const auto quorum = llmq::SelectQuorumForSigning(llmq_params_opt.value(), m_chainman.ActiveChain(), qman, requestId);
if (!quorum) { if (!quorum) {
LogPrintf("CEHFSignalsHandler::trySignEHFSignal no quorum for id=%s\n", requestId.ToString()); LogPrintf("CEHFSignalsHandler::trySignEHFSignal no quorum for id=%s\n", requestId.ToString());
return; return;
@ -103,7 +101,7 @@ MessageProcessingResult CEHFSignalsHandler::HandleNewRecoveredSig(const CRecover
} }
MessageProcessingResult ret; MessageProcessingResult ret;
const auto ehfSignals = mnhfman.GetSignalsStage(WITH_LOCK(cs_main, return chainstate.m_chain.Tip())); const auto ehfSignals = mnhfman.GetSignalsStage(WITH_LOCK(cs_main, return m_chainman.ActiveTip()));
MNHFTxPayload mnhfPayload; MNHFTxPayload mnhfPayload;
for (const auto& deployment : Params().GetConsensus().vDeployments) { for (const auto& deployment : Params().GetConsensus().vDeployments) {
// skip deployments that do not use dip0023 or that have already been mined // skip deployments that do not use dip0023 or that have already been mined
@ -126,7 +124,7 @@ MessageProcessingResult CEHFSignalsHandler::HandleNewRecoveredSig(const CRecover
CTransactionRef tx_to_sent = MakeTransactionRef(std::move(tx)); CTransactionRef tx_to_sent = MakeTransactionRef(std::move(tx));
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig Special EHF TX is created hash=%s\n", tx_to_sent->GetHash().ToString()); LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig Special EHF TX is created hash=%s\n", tx_to_sent->GetHash().ToString());
LOCK(cs_main); LOCK(cs_main);
const MempoolAcceptResult result = AcceptToMemoryPool(chainstate, mempool, tx_to_sent, /* bypass_limits */ false); const MempoolAcceptResult result = m_chainman.ProcessTransaction(tx_to_sent);
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
ret.m_transactions.push_back(tx_to_sent->GetHash()); ret.m_transactions.push_back(tx_to_sent->GetHash());
} else { } else {

View File

@ -10,9 +10,8 @@
#include <set> #include <set>
class CBlockIndex; class CBlockIndex;
class CChainState; class ChainstateManager;
class CMNHFManager; class CMNHFManager;
class CTxMemPool;
namespace llmq namespace llmq
{ {
@ -23,11 +22,10 @@ class CSigningManager;
class CEHFSignalsHandler : public CRecoveredSigsListener class CEHFSignalsHandler : public CRecoveredSigsListener
{ {
private: private:
CChainState& chainstate; ChainstateManager& m_chainman;
CMNHFManager& mnhfman; CMNHFManager& mnhfman;
CSigningManager& sigman; CSigningManager& sigman;
CSigSharesManager& shareman; CSigSharesManager& shareman;
CTxMemPool& mempool;
const CQuorumManager& qman; const CQuorumManager& qman;
/** /**
@ -36,8 +34,8 @@ private:
mutable Mutex cs; mutable Mutex cs;
std::set<uint256> ids GUARDED_BY(cs); std::set<uint256> ids GUARDED_BY(cs);
public: public:
explicit CEHFSignalsHandler(CChainState& chainstate, CMNHFManager& mnhfman, CSigningManager& sigman, explicit CEHFSignalsHandler(ChainstateManager& chainman, CMNHFManager& mnhfman, CSigningManager& sigman,
CSigSharesManager& shareman, CTxMemPool& mempool, const CQuorumManager& qman); CSigSharesManager& shareman, const CQuorumManager& qman);
~CEHFSignalsHandler(); ~CEHFSignalsHandler();

View File

@ -12,6 +12,7 @@
#include <netmessagemaker.h> #include <netmessagemaker.h>
#include <addrdb.h> #include <addrdb.h>
#include <addrman.h>
#include <banman.h> #include <banman.h>
#include <clientversion.h> #include <clientversion.h>
#include <compat.h> #include <compat.h>

View File

@ -6,7 +6,6 @@
#ifndef BITCOIN_NET_H #ifndef BITCOIN_NET_H
#define BITCOIN_NET_H #define BITCOIN_NET_H
#include <addrman.h>
#include <bip324.h> #include <bip324.h>
#include <bloom.h> #include <bloom.h>
#include <chainparams.h> #include <chainparams.h>
@ -50,14 +49,15 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
class AddrMan;
class BanMan;
class CConnman; class CConnman;
class CDeterministicMNList; class CDeterministicMNList;
class CDeterministicMNManager; class CDeterministicMNManager;
class CMasternodeMetaMan; class CMasternodeMetaMan;
class CMasternodeSync; class CMasternodeSync;
class CScheduler;
class CNode; class CNode;
class BanMan; class CScheduler;
struct bilingual_str; struct bilingual_str;
/** Default for -whitelistrelay. */ /** Default for -whitelistrelay. */
@ -100,7 +100,7 @@ static const bool DEFAULT_LISTEN = true;
*/ */
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
/** The default for -maxuploadtarget. 0 = Unlimited */ /** The default for -maxuploadtarget. 0 = Unlimited */
static constexpr uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; static const std::string DEFAULT_MAX_UPLOAD_TARGET{"0M"};
/** Default for blocks only*/ /** Default for blocks only*/
static const bool DEFAULT_BLOCKSONLY = false; static const bool DEFAULT_BLOCKSONLY = false;
/** -peertimeout default */ /** -peertimeout default */

View File

@ -856,9 +856,9 @@ private:
EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex); EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
/** /**
* Filter for transactions that were recently rejected by * Filter for transactions that were recently rejected by the mempool.
* AcceptToMemoryPool. These are not rerequested until the chain tip * These are not rerequested until the chain tip changes, at which point
* changes, at which point the entire filter is reset. * the entire filter is reset.
* *
* Without this filter we'd be re-requesting txs from each of our peers, * Without this filter we'd be re-requesting txs from each of our peers,
* increasing bandwidth consumption considerably. For instance, with 100 * increasing bandwidth consumption considerably. For instance, with 100
@ -1816,6 +1816,7 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat
case TxValidationResult::TX_PREMATURE_SPEND: case TxValidationResult::TX_PREMATURE_SPEND:
case TxValidationResult::TX_CONFLICT: case TxValidationResult::TX_CONFLICT:
case TxValidationResult::TX_MEMPOOL_POLICY: case TxValidationResult::TX_MEMPOOL_POLICY:
case TxValidationResult::TX_NO_MEMPOOL:
// moved from BLOCK // moved from BLOCK
case TxValidationResult::TX_BAD_SPECIAL: case TxValidationResult::TX_BAD_SPECIAL:
case TxValidationResult::TX_CONFLICT_LOCK: case TxValidationResult::TX_CONFLICT_LOCK:
@ -3039,7 +3040,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash); const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
if (porphanTx == nullptr) continue; if (porphanTx == nullptr) continue;
const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, porphanTx, false /* bypass_limits */); const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
const TxValidationState& state = result.m_state; const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
@ -3065,7 +3066,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
break; break;
} }
} }
m_mempool.check(m_chainman.ActiveChainstate());
} }
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer, bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer,
@ -4223,7 +4223,7 @@ void PeerManagerImpl::ProcessMessage(
return; return;
} }
const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */); const MempoolAcceptResult result = m_chainman.ProcessTransaction(ptx);
const TxValidationState& state = result.m_state; const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
@ -4234,7 +4234,6 @@ void PeerManagerImpl::ProcessMessage(
m_cj_ctx->dstxman->AddDSTX(dstx); m_cj_ctx->dstxman->AddDSTX(dstx);
} }
m_mempool.check(m_chainman.ActiveChainstate());
RelayTransaction(tx.GetHash()); RelayTransaction(tx.GetHash());
m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set); m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
@ -4306,8 +4305,8 @@ void PeerManagerImpl::ProcessMessage(
} }
// If a tx has been detected by m_recent_rejects, we will have reached // If a tx has been detected by m_recent_rejects, we will have reached
// this point and the tx will have been ignored. Because we haven't run // this point and the tx will have been ignored. Because we haven't
// the tx through AcceptToMemoryPool, we won't have computed a DoS // submitted the tx to our mempool, we won't have computed a DoS
// score for it or determined exactly why we consider it invalid. // score for it or determined exactly why we consider it invalid.
// //
// This means we won't penalize any peer subsequently relaying a DoSy // This means we won't penalize any peer subsequently relaying a DoSy

View File

@ -65,8 +65,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
if (max_tx_fee > 0) { if (max_tx_fee > 0) {
// First, call ATMP with test_accept and check the fee. If ATMP // First, call ATMP with test_accept and check the fee. If ATMP
// fails here, return error immediately. // fails here, return error immediately.
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/true, bypass_limits);
bypass_limits, true /* test_accept */);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string.original); return HandleATMPError(result.m_state, err_string.original);
} else if (result.m_base_fees.value() > max_tx_fee) { } else if (result.m_base_fees.value() > max_tx_fee) {
@ -74,8 +73,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
} }
} }
// Try to submit the transaction to the mempool. // Try to submit the transaction to the mempool.
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/false, bypass_limits);
bypass_limits, false /* test_accept */);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string.original); return HandleATMPError(result.m_state, err_string.original);
} }

View File

@ -134,10 +134,10 @@ static RPCHelpMan coinjoin_start()
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already."); throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already.");
} }
const ChainstateManager& chainman = EnsureChainman(node); ChainstateManager& chainman = EnsureChainman(node);
CTxMemPool& mempool = EnsureMemPool(node); CTxMemPool& mempool = EnsureMemPool(node);
CConnman& connman = EnsureConnman(node); CConnman& connman = EnsureConnman(node);
bool result = cj_clientman->DoAutomaticDenominating(chainman.ActiveChainstate(), connman, mempool); bool result = cj_clientman->DoAutomaticDenominating(chainman, connman, mempool);
return "Mixing " + (result ? "started successfully" : ("start failed: " + cj_clientman->GetStatuses().original + ", will retry")); return "Mixing " + (result ? "started successfully" : ("start failed: " + cj_clientman->GetStatuses().original + ", will retry"));
}, },
}; };

View File

@ -6,6 +6,7 @@
#include <core_io.h> #include <core_io.h>
#include <evo/deterministicmns.h> #include <evo/deterministicmns.h>
#include <governance/classes.h> #include <governance/classes.h>
#include <governance/common.h>
#include <governance/governance.h> #include <governance/governance.h>
#include <governance/validators.h> #include <governance/validators.h>
#include <governance/vote.h> #include <governance/vote.h>
@ -13,13 +14,13 @@
#include <masternode/node.h> #include <masternode/node.h>
#include <masternode/sync.h> #include <masternode/sync.h>
#include <messagesigner.h> #include <messagesigner.h>
#include <node/context.h>
#include <net.h> #include <net.h>
#include <node/context.h>
#include <rpc/blockchain.h> #include <rpc/blockchain.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/server_util.h> #include <rpc/server_util.h>
#include <rpc/util.h> #include <rpc/util.h>
#include <governance/common.h> #include <timedata.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/system.h> #include <util/system.h>
#include <validation.h> #include <validation.h>

View File

@ -5,6 +5,7 @@
#include <rpc/server.h> #include <rpc/server.h>
#include <addrman.h>
#include <banman.h> #include <banman.h>
#include <chainparams.h> #include <chainparams.h>
#include <clientversion.h> #include <clientversion.h>

View File

@ -1246,12 +1246,13 @@ static RPCHelpMan testmempoolaccept()
NodeContext& node = EnsureAnyNodeContext(request.context); NodeContext& node = EnsureAnyNodeContext(request.context);
CTxMemPool& mempool = EnsureMemPool(node); CTxMemPool& mempool = EnsureMemPool(node);
CChainState& chainstate = EnsureChainman(node).ActiveChainstate(); ChainstateManager& chainman = EnsureChainman(node);
CChainState& chainstate = chainman.ActiveChainstate();
const PackageMempoolAcceptResult package_result = [&] { const PackageMempoolAcceptResult package_result = [&] {
LOCK(::cs_main); LOCK(::cs_main);
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true); if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
return PackageMempoolAcceptResult(txns[0]->GetHash(), return PackageMempoolAcceptResult(txns[0]->GetHash(),
AcceptToMemoryPool(chainstate, mempool, txns[0], /* bypass_limits */ false, /* test_accept*/ true)); chainman.ProcessTransaction(txns[0], /*test_accept=*/ true));
}(); }();
UniValue rpc_result(UniValue::VARR); UniValue rpc_result(UniValue::VARR);

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
#include <chainparams.h> #include <chainparams.h>
#include <chainparamsbase.h> #include <chainparamsbase.h>
#include <net.h> #include <net.h>

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
#include <chainparams.h> #include <chainparams.h>
#include <chainparamsbase.h> #include <chainparamsbase.h>
#include <net.h> #include <net.h>

View File

@ -80,7 +80,7 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, const NodeContext& node, CChainState& chainstate) void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, const NodeContext& node, CChainState& chainstate)
{ {
WITH_LOCK(::cs_main, tx_pool.check(chainstate)); WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
{ {
BlockAssembler::Options options; BlockAssembler::Options options;
options.nBlockMaxSize = fuzzed_data_provider.ConsumeIntegralInRange(0U, MaxBlockSize(true)); options.nBlockMaxSize = fuzzed_data_provider.ConsumeIntegralInRange(0U, MaxBlockSize(true));
@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, con
std::vector<uint256> all_txids; std::vector<uint256> all_txids;
tx_pool.queryHashes(all_txids); tx_pool.queryHashes(all_txids);
assert(all_txids.size() < info_all.size()); assert(all_txids.size() < info_all.size());
WITH_LOCK(::cs_main, tx_pool.check(chainstate)); WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
} }
SyncWithValidationInterfaceQueue(); SyncWithValidationInterfaceQueue();
} }

View File

@ -0,0 +1,55 @@
// Copyright (c) 2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <core_io.h>
#include <script/script.h>
#include <util/strencodings.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(script_parse_tests)
BOOST_AUTO_TEST_CASE(parse_script)
{
const std::vector<std::pair<std::string,std::string>> IN_OUT{
// {IN: script string , OUT: hex string }
{"", ""},
{"0", "00"},
{"1", "51"},
{"2", "52"},
{"3", "53"},
{"4", "54"},
{"5", "55"},
{"6", "56"},
{"7", "57"},
{"8", "58"},
{"9", "59"},
{"10", "5a"},
{"11", "5b"},
{"12", "5c"},
{"13", "5d"},
{"14", "5e"},
{"15", "5f"},
{"16", "60"},
{"17", "0111"},
{"-9", "0189"},
{"0x17", "17"},
{"'17'", "023137"},
{"ELSE", "67"},
{"NOP10", "b9"},
{"11111111111111111111", "00"},
};
std::string all_in;
std::string all_out;
for (const auto& [in, out] : IN_OUT) {
BOOST_CHECK_EQUAL(HexStr(ParseScript(in)), out);
all_in += " " + in + " ";
all_out += out;
}
BOOST_CHECK_EQUAL(HexStr(ParseScript(all_in)), all_out);
BOOST_CHECK_EXCEPTION(ParseScript("11111111111"), std::runtime_error, HasReason("script parse error: decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF"));
BOOST_CHECK_EXCEPTION(ParseScript("OP_CHECKSIGADD"), std::runtime_error, HasReason("script parse error: unknown opcode"));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -43,7 +43,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main); LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size(); unsigned int initialPoolSize = m_node.mempool->size();
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx), true /* bypass_limits */); const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID); BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);

View File

@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) { const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main); LOCK(cs_main);
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx), true /* bypass_limits */); const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(tx));
return result.m_result_type == MempoolAcceptResult::ResultType::VALID; return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
}; };

View File

@ -110,12 +110,12 @@ void DashTestSetup(NodeContext& node, const CChainParams& chainparams)
node.dmnman = std::make_unique<CDeterministicMNManager>(chainstate, *node.connman, *node.evodb); node.dmnman = std::make_unique<CDeterministicMNManager>(chainstate, *node.connman, *node.evodb);
node.mempool->ConnectManagers(node.dmnman.get()); node.mempool->ConnectManagers(node.dmnman.get());
node.cj_ctx = std::make_unique<CJContext>(chainstate, *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool, node.cj_ctx = std::make_unique<CJContext>(*node.chainman, *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool,
/* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* relay_txes = */ true); /* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* relay_txes = */ true);
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
node.coinjoin_loader = interfaces::MakeCoinJoinLoader(*node.cj_ctx->walletman); node.coinjoin_loader = interfaces::MakeCoinJoinLoader(*node.cj_ctx->walletman);
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
node.llmq_ctx = std::make_unique<LLMQContext>(chainstate, *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman, *node.mempool, node.llmq_ctx = std::make_unique<LLMQContext>(*node.chainman, *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman, *node.mempool,
/* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* unit_tests = */ true, /* wipe = */ false); /* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* unit_tests = */ true, /* wipe = */ false);
Assert(node.mnhf_manager)->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get()); Assert(node.mnhf_manager)->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get());
node.chain_helper = std::make_unique<CChainstateHelper>(*node.cpoolman, *node.dmnman, *node.mnhf_manager, *node.govman, *(node.llmq_ctx->quorum_block_processor), *node.chainman, node.chain_helper = std::make_unique<CChainstateHelper>(*node.cpoolman, *node.dmnman, *node.mnhf_manager, *node.govman, *(node.llmq_ctx->quorum_block_processor), *node.chainman,
@ -519,7 +519,7 @@ CMutableTransaction TestChainSetup::CreateValidMempoolTransaction(CTransactionRe
// If submit=true, add transaction to the mempool. // If submit=true, add transaction to the mempool.
if (submit) { if (submit) {
LOCK(cs_main); LOCK(cs_main);
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(mempool_txn), /* bypass_limits */ false); const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(mempool_txn));
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID); assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
} }

View File

@ -1318,6 +1318,11 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney)
BOOST_AUTO_TEST_CASE(util_ParseMoney) BOOST_AUTO_TEST_CASE(util_ParseMoney)
{ {
BOOST_CHECK_EQUAL(ParseMoney("0.0").value(), 0); BOOST_CHECK_EQUAL(ParseMoney("0.0").value(), 0);
BOOST_CHECK_EQUAL(ParseMoney(".").value(), 0);
BOOST_CHECK_EQUAL(ParseMoney("0.").value(), 0);
BOOST_CHECK_EQUAL(ParseMoney(".0").value(), 0);
BOOST_CHECK_EQUAL(ParseMoney(".6789").value(), 6789'0000);
BOOST_CHECK_EQUAL(ParseMoney("12345.").value(), COIN * 12345);
BOOST_CHECK_EQUAL(ParseMoney("12345.6789").value(), (COIN/10000)*123456789); BOOST_CHECK_EQUAL(ParseMoney("12345.6789").value(), (COIN/10000)*123456789);
@ -1355,11 +1360,18 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney)
BOOST_CHECK(!ParseMoney(" ")); BOOST_CHECK(!ParseMoney(" "));
// Parsing two numbers should fail // Parsing two numbers should fail
BOOST_CHECK(!ParseMoney(".."));
BOOST_CHECK(!ParseMoney("0..0"));
BOOST_CHECK(!ParseMoney("1 2")); BOOST_CHECK(!ParseMoney("1 2"));
BOOST_CHECK(!ParseMoney(" 1 2 ")); BOOST_CHECK(!ParseMoney(" 1 2 "));
BOOST_CHECK(!ParseMoney(" 1.2 3 ")); BOOST_CHECK(!ParseMoney(" 1.2 3 "));
BOOST_CHECK(!ParseMoney(" 1 2.3 ")); BOOST_CHECK(!ParseMoney(" 1 2.3 "));
// Embedded whitespace should fail
BOOST_CHECK(!ParseMoney(" -1 .2 "));
BOOST_CHECK(!ParseMoney(" 1 .2 "));
BOOST_CHECK(!ParseMoney(" +1 .2 "));
// Attempted 63 bit overflow should fail // Attempted 63 bit overflow should fail
BOOST_CHECK(!ParseMoney("92233720368.54775808")); BOOST_CHECK(!ParseMoney("92233720368.54775808"));
@ -2673,4 +2685,52 @@ BOOST_AUTO_TEST_CASE(remove_prefix)
BOOST_CHECK_EQUAL(RemovePrefix("", ""), ""); BOOST_CHECK_EQUAL(RemovePrefix("", ""), "");
} }
BOOST_AUTO_TEST_CASE(util_ParseByteUnits)
{
auto noop = ByteUnit::NOOP;
// no multiplier
BOOST_CHECK_EQUAL(ParseByteUnits("1", noop).value(), 1);
BOOST_CHECK_EQUAL(ParseByteUnits("0", noop).value(), 0);
BOOST_CHECK_EQUAL(ParseByteUnits("1k", noop).value(), 1000ULL);
BOOST_CHECK_EQUAL(ParseByteUnits("1K", noop).value(), 1ULL << 10);
BOOST_CHECK_EQUAL(ParseByteUnits("2m", noop).value(), 2'000'000ULL);
BOOST_CHECK_EQUAL(ParseByteUnits("2M", noop).value(), 2ULL << 20);
BOOST_CHECK_EQUAL(ParseByteUnits("3g", noop).value(), 3'000'000'000ULL);
BOOST_CHECK_EQUAL(ParseByteUnits("3G", noop).value(), 3ULL << 30);
BOOST_CHECK_EQUAL(ParseByteUnits("4t", noop).value(), 4'000'000'000'000ULL);
BOOST_CHECK_EQUAL(ParseByteUnits("4T", noop).value(), 4ULL << 40);
// check default multiplier
BOOST_CHECK_EQUAL(ParseByteUnits("5", ByteUnit::K).value(), 5ULL << 10);
// NaN
BOOST_CHECK(!ParseByteUnits("", noop));
BOOST_CHECK(!ParseByteUnits("foo", noop));
// whitespace
BOOST_CHECK(!ParseByteUnits("123m ", noop));
BOOST_CHECK(!ParseByteUnits(" 123m", noop));
// no +-
BOOST_CHECK(!ParseByteUnits("-123m", noop));
BOOST_CHECK(!ParseByteUnits("+123m", noop));
// zero padding
BOOST_CHECK_EQUAL(ParseByteUnits("020M", noop).value(), 20ULL << 20);
// fractions not allowed
BOOST_CHECK(!ParseByteUnits("0.5T", noop));
// overflow
BOOST_CHECK(!ParseByteUnits("18446744073709551615g", noop));
// invalid unit
BOOST_CHECK(!ParseByteUnits("1x", noop));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{ {
LOCK(cs_main); LOCK(cs_main);
for (const auto& tx : txs) { for (const auto& tx : txs) {
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */); const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(tx);
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID); BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
} }
} }

View File

@ -5,6 +5,7 @@
#include <txdb.h> #include <txdb.h>
#include <chain.h>
#include <node/ui_interface.h> #include <node/ui_interface.h>
#include <pow.h> #include <pow.h>
#include <random.h> #include <random.h>
@ -31,6 +32,28 @@ static constexpr uint8_t DB_FLAG{'F'};
static constexpr uint8_t DB_REINDEX_FLAG{'R'}; static constexpr uint8_t DB_REINDEX_FLAG{'R'};
static constexpr uint8_t DB_LAST_BLOCK{'l'}; static constexpr uint8_t DB_LAST_BLOCK{'l'};
// Keys used in previous version that might still be found in the DB:
static constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
// uint8_t DB_TXINDEX{'t'}
std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db)
{
CBlockLocator ignored{};
if (block_tree_db.Read(DB_TXINDEX_BLOCK, ignored)) {
return _("The -txindex upgrade started by a previous version can not be completed. Restart with the previous version or run a full -reindex.");
}
bool txindex_legacy_flag{false};
block_tree_db.ReadFlag("txindex", txindex_legacy_flag);
if (txindex_legacy_flag) {
// Disable legacy txindex and warn once about occupied disk space
if (!block_tree_db.WriteFlag("txindex", false)) {
return Untranslated("Failed to write block index db flag 'txindex'='0'");
}
return _("The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.");
}
return std::nullopt;
}
namespace { namespace {
struct CoinEntry { struct CoinEntry {

View File

@ -8,19 +8,22 @@
#include <coins.h> #include <coins.h>
#include <dbwrapper.h> #include <dbwrapper.h>
#include <chain.h>
#include <primitives/block.h>
#include <spentindex.h> #include <spentindex.h>
#include <timestampindex.h> #include <timestampindex.h>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
class CBlockFileInfo;
class CBlockIndex; class CBlockIndex;
class CCoinsViewDBCursor;
class uint256; class uint256;
namespace Consensus {
struct Params;
};
struct bilingual_str;
//! -dbcache default (MiB) //! -dbcache default (MiB)
static const int64_t nDefaultDbCache = 300; static const int64_t nDefaultDbCache = 300;
@ -108,4 +111,6 @@ public:
EXCLUSIVE_LOCKS_REQUIRED(::cs_main); EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
}; };
std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db);
#endif // BITCOIN_TXDB_H #endif // BITCOIN_TXDB_H

View File

@ -5,6 +5,8 @@
#include <txmempool.h> #include <txmempool.h>
#include <chain.h>
#include <coins.h>
#include <consensus/consensus.h> #include <consensus/consensus.h>
#include <consensus/tx_verify.h> #include <consensus/tx_verify.h>
#include <consensus/validation.h> #include <consensus/validation.h>
@ -17,7 +19,6 @@
#include <util/moneystr.h> #include <util/moneystr.h>
#include <util/system.h> #include <util/system.h>
#include <util/time.h> #include <util/time.h>
#include <validation.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <evo/specialtx.h> #include <evo/specialtx.h>
@ -29,6 +30,65 @@
#include <cmath> #include <cmath>
#include <optional> #include <optional>
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
struct update_descendant_state
{
update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
{}
void operator() (CTxMemPoolEntry &e)
{ e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
private:
int64_t modifySize;
CAmount modifyFee;
int64_t modifyCount;
};
struct update_ancestor_state
{
update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
{}
void operator() (CTxMemPoolEntry &e)
{ e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
private:
int64_t modifySize;
CAmount modifyFee;
int64_t modifyCount;
int64_t modifySigOpsCost;
};
struct update_fee_delta
{
explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
private:
int64_t feeDelta;
};
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
{
AssertLockHeld(cs_main);
// If there are relative lock times then the maxInputBlock will be set
// If there are no relative lock times, the LockPoints don't depend on the chain
if (lp.maxInputBlock) {
// Check whether active_chain is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
if (!active_chain.Contains(lp.maxInputBlock)) {
return false;
}
}
// LockPoints still valid
return true;
}
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
int64_t time, unsigned int entry_height, int64_t time, unsigned int entry_height,
bool spends_coinbase, int64_t sigops_count, LockPoints lp) bool spends_coinbase, int64_t sigops_count, LockPoints lp)
@ -284,7 +344,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors) void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
{ {
CTxMemPoolEntry::Parents parents = it->GetMemPoolParents(); const CTxMemPoolEntry::Parents& parents = it->GetMemPoolParentsConst();
// add or remove this tx as a child of each parent // add or remove this tx as a child of each parent
for (const CTxMemPoolEntry& parent : parents) { for (const CTxMemPoolEntry& parent : parents) {
UpdateChild(mapTx.iterator_to(parent), it, add); UpdateChild(mapTx.iterator_to(parent), it, add);
@ -806,44 +866,24 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
RemoveStaged(setAllRemoves, false, reason); RemoveStaged(setAllRemoves, false, reason);
} }
void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main) void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{ {
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions // Remove transactions spending a coinbase which are now immature and no-longer-final transactions
AssertLockHeld(cs); AssertLockHeld(cs);
AssertLockHeld(::cs_main);
setEntries txToRemove; setEntries txToRemove;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx(); if (check_final_and_mature(it)) txToRemove.insert(it);
LockPoints lp = it->GetLockPoints();
bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
|| !CheckSequenceLocks(active_chainstate.m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
txToRemove.insert(it);
} else if (it->GetSpendsCoinbase()) {
for (const CTxIn& txin : tx.vin) {
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
if (it2 != mapTx.end())
continue;
const Coin &coin = active_chainstate.CoinsTip().AccessCoin(txin.prevout);
if (m_check_ratio != 0) assert(!coin.IsSpent());
unsigned int nMemPoolHeight = active_chainstate.m_chain.Tip()->nHeight + 1;
if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
txToRemove.insert(it);
break;
}
}
}
if (!validLP) {
mapTx.modify(it, update_lock_points(lp));
}
} }
setEntries setAllRemoves; setEntries setAllRemoves;
for (txiter it : txToRemove) { for (txiter it : txToRemove) {
CalculateDescendants(it, setAllRemoves); CalculateDescendants(it, setAllRemoves);
} }
RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG); RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG);
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
assert(TestLockPointValidity(chain, it->GetLockPoints()));
}
} }
void CTxMemPool::removeConflicts(const CTransaction &tx) void CTxMemPool::removeConflicts(const CTransaction &tx)
@ -1101,16 +1141,7 @@ void CTxMemPool::clear()
_clear(); _clear();
} }
static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight) void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
{
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
CAmount txfee = 0;
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee);
assert(fCheckResult);
UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
}
void CTxMemPool::check(CChainState& active_chainstate) const
{ {
if (m_check_ratio == 0) return; if (m_check_ratio == 0) return;
@ -1123,20 +1154,16 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t checkTotal = 0; uint64_t checkTotal = 0;
CAmount check_total_fee{0}; CAmount check_total_fee{0};
uint64_t innerUsage = 0; uint64_t innerUsage = 0;
uint64_t prev_ancestor_count{0};
CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip)); CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
std::list<const CTxMemPoolEntry*> waitingOnDependants; for (const auto& it : GetSortedDepthAndScore()) {
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
unsigned int i = 0;
checkTotal += it->GetTxSize(); checkTotal += it->GetTxSize();
check_total_fee += it->GetFee(); check_total_fee += it->GetFee();
innerUsage += it->DynamicMemoryUsage(); innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx(); const CTransaction& tx = it->GetTx();
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst()); innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
bool fDependsWait = false;
CTxMemPoolEntry::Parents setParentCheck; CTxMemPoolEntry::Parents setParentCheck;
for (const CTxIn &txin : tx.vin) { for (const CTxIn &txin : tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
@ -1144,17 +1171,17 @@ void CTxMemPool::check(CChainState& active_chainstate) const
if (it2 != mapTx.end()) { if (it2 != mapTx.end()) {
const CTransaction& tx2 = it2->GetTx(); const CTransaction& tx2 = it2->GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
fDependsWait = true;
setParentCheck.insert(*it2); setParentCheck.insert(*it2);
} else {
assert(active_coins_tip.HaveCoin(txin.prevout));
} }
// We are iterating through the mempool entries sorted in order by ancestor count.
// All parents must have been checked before their children and their coins added to
// the mempoolDuplicate coins cache.
assert(mempoolDuplicate.HaveCoin(txin.prevout));
// Check whether its inputs are marked in mapNextTx. // Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout); auto it3 = mapNextTx.find(txin.prevout);
assert(it3 != mapNextTx.end()); assert(it3 != mapNextTx.end());
assert(it3->first == &txin.prevout); assert(it3->first == &txin.prevout);
assert(it3->second == &tx); assert(it3->second == &tx);
i++;
} }
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool { auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
return a.GetTx().GetHash() == b.GetTx().GetHash(); return a.GetTx().GetHash() == b.GetTx().GetHash();
@ -1181,6 +1208,9 @@ void CTxMemPool::check(CChainState& active_chainstate) const
assert(it->GetSizeWithAncestors() == nSizeCheck); assert(it->GetSizeWithAncestors() == nSizeCheck);
assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); assert(it->GetSigOpCountWithAncestors() == nSigOpCheck);
assert(it->GetModFeesWithAncestors() == nFeesCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck);
// Sanity check: we are walking in ascending ancestor count order.
assert(prev_ancestor_count <= it->GetCountWithAncestors());
prev_ancestor_count = it->GetCountWithAncestors();
// Check children against mapNextTx // Check children against mapNextTx
CTxMemPoolEntry::Children setChildrenCheck; CTxMemPoolEntry::Children setChildrenCheck;
@ -1199,24 +1229,12 @@ void CTxMemPool::check(CChainState& active_chainstate) const
// just a sanity check, not definitive that this calc is correct... // just a sanity check, not definitive that this calc is correct...
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
if (fDependsWait) TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
waitingOnDependants.push_back(&(*it)); CAmount txfee = 0;
else { assert(!tx.IsCoinBase());
CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
} for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
} AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
unsigned int stepsSinceLastRemove = 0;
while (!waitingOnDependants.empty()) {
const CTxMemPoolEntry* entry = waitingOnDependants.front();
waitingOnDependants.pop_front();
if (!mempoolDuplicate.HaveInputs(entry->GetTx())) {
waitingOnDependants.push_back(entry);
stepsSinceLastRemove++;
assert(stepsSinceLastRemove < waitingOnDependants.size());
} else {
CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
stepsSinceLastRemove = 0;
}
} }
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
uint256 hash = it->second->GetHash(); uint256 hash = it->second->GetHash();

View File

@ -30,12 +30,13 @@
#include <util/epochguard.h> #include <util/epochguard.h>
#include <util/hasher.h> #include <util/hasher.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp> #include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
class CBlockIndex; class CBlockIndex;
class CChain;
class CChainState; class CChainState;
extern RecursiveMutex cs_main; extern RecursiveMutex cs_main;
@ -59,6 +60,11 @@ struct LockPoints {
CBlockIndex* maxInputBlock{nullptr}; CBlockIndex* maxInputBlock{nullptr};
}; };
/**
* Test whether the LockPoints height and time are still valid on the current chain
*/
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
struct CompareIteratorByHash { struct CompareIteratorByHash {
// SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T> // SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T>
// (e.g. a wrapped CTxMemPoolEntry&) // (e.g. a wrapped CTxMemPoolEntry&)
@ -171,58 +177,6 @@ public:
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
}; };
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
struct update_descendant_state
{
update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
{}
void operator() (CTxMemPoolEntry &e)
{ e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
private:
int64_t modifySize;
CAmount modifyFee;
int64_t modifyCount;
};
struct update_ancestor_state
{
update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int _modifySigOps) :
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOps(_modifySigOps)
{}
void operator() (CTxMemPoolEntry &e)
{ e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOps); }
private:
int64_t modifySize;
CAmount modifyFee;
int64_t modifyCount;
int modifySigOps;
};
struct update_fee_delta
{
explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
private:
int64_t feeDelta;
};
struct update_lock_points
{
explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
private:
const LockPoints& lp;
};
// extracts a transaction hash from CTxMemPoolEntry or CTransactionRef // extracts a transaction hash from CTxMemPoolEntry or CTransactionRef
struct mempoolentry_txid struct mempoolentry_txid
{ {
@ -354,6 +308,16 @@ public:
} }
}; };
struct update_lock_points
{
explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
private:
const LockPoints& lp;
};
// Multi_index tag names // Multi_index tag names
struct descendant_score {}; struct descendant_score {};
struct entry_time {}; struct entry_time {};
@ -645,7 +609,7 @@ public:
* all inputs are in the mapNextTx array). If sanity-checking is turned off, * all inputs are in the mapNextTx array). If sanity-checking is turned off,
* check does nothing. * check does nothing.
*/ */
void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); void check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
// addUnchecked must updated state for all ancestors of a given transaction, // addUnchecked must updated state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of // to track size/count of descendant transactions. First version of
@ -667,7 +631,10 @@ public:
bool removeSpentIndex(const uint256 txhash); bool removeSpentIndex(const uint256 txhash);
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main); /** After reorg, check if mempool entries are now non-final, premature coinbase spends, or have
* invalid lockpoints. Update lockpoints and remove entries (and descendants of entries) that
* are no longer valid. */
void removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);

View File

@ -20,9 +20,9 @@ bilingual_str TransactionErrorString(const TransactionError err)
case TransactionError::P2P_DISABLED: case TransactionError::P2P_DISABLED:
return Untranslated("Peer-to-peer functionality missing or disabled"); return Untranslated("Peer-to-peer functionality missing or disabled");
case TransactionError::MEMPOOL_REJECTED: case TransactionError::MEMPOOL_REJECTED:
return Untranslated("Transaction rejected by AcceptToMemoryPool"); return Untranslated("Transaction rejected by mempool");
case TransactionError::MEMPOOL_ERROR: case TransactionError::MEMPOOL_ERROR:
return Untranslated("AcceptToMemoryPool failed"); return Untranslated("Mempool internal error");
case TransactionError::INVALID_PSBT: case TransactionError::INVALID_PSBT:
return Untranslated("PSBT is not well-formed"); return Untranslated("PSBT is not well-formed");
case TransactionError::PSBT_MISMATCH: case TransactionError::PSBT_MISMATCH:

View File

@ -33,10 +33,6 @@ public:
SaltedOutpointHasher(bool deterministic = false); SaltedOutpointHasher(bool deterministic = false);
/** /**
* This *must* return size_t. With Boost 1.46 on 32-bit systems the
* unordered_map will behave unpredictably if the custom hasher returns a
* uint64_t, resulting in failures when syncing the chain (#4634).
*
* Having the hash noexcept allows libstdc++'s unordered_map to recalculate * Having the hash noexcept allows libstdc++'s unordered_map to recalculate
* the hash during rehash, so it does not have to cache the value. This * the hash during rehash, so it does not have to cache the value. This
* reduces node's memory by sizeof(size_t). The required recalculation has * reduces node's memory by sizeof(size_t). The required recalculation has

View File

@ -11,6 +11,7 @@
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <limits>
#include <optional> #include <optional>
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@ -501,20 +502,6 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
return true; return true;
} }
std::string HexStr(const Span<const uint8_t> s)
{
std::string rv(s.size() * 2, '\0');
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
auto it = rv.begin();
for (uint8_t v : s) {
*it++ = hexmap[v >> 4];
*it++ = hexmap[v & 15];
}
assert(it == rv.end());
return rv;
}
std::string ToLower(const std::string& str) std::string ToLower(const std::string& str)
{ {
std::string r; std::string r;
@ -535,3 +522,62 @@ std::string Capitalize(std::string str)
str[0] = ToUpper(str.front()); str[0] = ToUpper(str.front());
return str; return str;
} }
std::string HexStr(const Span<const uint8_t> s)
{
std::string rv(s.size() * 2, '\0');
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
auto it = rv.begin();
for (uint8_t v : s) {
*it++ = hexmap[v >> 4];
*it++ = hexmap[v & 15];
}
assert(it == rv.end());
return rv;
}
std::optional<uint64_t> ParseByteUnits(const std::string& str, ByteUnit default_multiplier)
{
if (str.empty()) {
return std::nullopt;
}
auto multiplier = default_multiplier;
char unit = str.back();
switch (unit) {
case 'k':
multiplier = ByteUnit::k;
break;
case 'K':
multiplier = ByteUnit::K;
break;
case 'm':
multiplier = ByteUnit::m;
break;
case 'M':
multiplier = ByteUnit::M;
break;
case 'g':
multiplier = ByteUnit::g;
break;
case 'G':
multiplier = ByteUnit::G;
break;
case 't':
multiplier = ByteUnit::t;
break;
case 'T':
multiplier = ByteUnit::T;
break;
default:
unit = 0;
break;
}
uint64_t unit_amount = static_cast<uint64_t>(multiplier);
auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
return std::nullopt;
}
return *parsed_num * unit_amount;
}

View File

@ -28,6 +28,23 @@ enum SafeChars
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986) SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
}; };
/**
* Used by ParseByteUnits()
* Lowercase base 1000
* Uppercase base 1024
*/
enum class ByteUnit : uint64_t {
NOOP = 1ULL,
k = 1000ULL,
K = 1024ULL,
m = 1'000'000ULL,
M = 1ULL << 20,
g = 1'000'000'000ULL,
G = 1ULL << 30,
t = 1'000'000'000'000ULL,
T = 1ULL << 40,
};
/** /**
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email * Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email
* addresses, but avoid anything even possibly remotely dangerous like & or > * addresses, but avoid anything even possibly remotely dangerous like & or >
@ -312,7 +329,17 @@ std::string ToUpper(const std::string& str);
*/ */
std::string Capitalize(std::string str); std::string Capitalize(std::string str);
/** Parse an HD keypaths like "m/7/0'/2000". */ /**
[[nodiscard]] bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath); * Parse a string with suffix unit [k|K|m|M|g|G|t|T].
* Must be a whole integer, fractions not allowed (0.5t), no whitespace or +-
* Lowercase units are 1000 base. Uppercase units are 1024 base.
* Examples: 2m,27M,19g,41T
*
* @param[in] str the string to convert into bytes
* @param[in] default_multiplier if no unit is found in str use this unit
* @returns optional uint64_t bytes from str or nullopt
* if ToIntegral is false, str is empty, trailing whitespace or overflow
*/
std::optional<uint64_t> ParseByteUnits(const std::string& str, ByteUnit default_multiplier);
#endif // BITCOIN_UTIL_STRENCODINGS_H #endif // BITCOIN_UTIL_STRENCODINGS_H

View File

@ -154,7 +154,7 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
// CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate // CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
// nLockTime because when IsFinalTx() is called within // nLockTime because when IsFinalTx() is called within
// CBlock::AcceptBlock(), the height of the block *being* // AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a // evaluated is what is used. Thus if we want to know if a
// transaction can be part of the *next* block, we need to call // transaction can be part of the *next* block, we need to call
// IsFinalTx() with one more than active_chain_tip.Height(). // IsFinalTx() with one more than active_chain_tip.Height().
@ -172,24 +172,6 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
return IsFinalTx(tx, nBlockHeight, nBlockTime); return IsFinalTx(tx, nBlockHeight, nBlockTime);
} }
bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
{
AssertLockHeld(cs_main);
assert(lp);
// If there are relative lock times then the maxInputBlock will be set
// If there are no relative lock times, the LockPoints don't depend on the chain
if (lp->maxInputBlock) {
// Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
if (!active_chain.Contains(lp->maxInputBlock)) {
return false;
}
}
// LockPoints still valid
return true;
}
bool CheckSequenceLocks(CBlockIndex* tip, bool CheckSequenceLocks(CBlockIndex* tip,
const CCoinsView& coins_view, const CCoinsView& coins_view,
const CTransaction& tx, const CTransaction& tx,
@ -389,8 +371,41 @@ void CChainState::MaybeUpdateMempoolForReorg(
// the disconnectpool that were added back and cleans up the mempool state. // the disconnectpool that were added back and cleans up the mempool state.
m_mempool->UpdateTransactionsFromBlock(vHashUpdate); m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
const auto check_final_and_mature = [this, flags=STANDARD_LOCKTIME_VERIFY_FLAGS](CTxMemPool::txiter it)
EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) {
bool should_remove = false;
AssertLockHeld(m_mempool->cs);
AssertLockHeld(::cs_main);
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
const bool validLP{TestLockPointValidity(m_chain, lp)};
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
if (!CheckFinalTx(m_chain.Tip(), tx, flags)
|| !CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
should_remove = true;
} else if (it->GetSpendsCoinbase()) {
for (const CTxIn& txin : tx.vin) {
auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
if (it2 != m_mempool->mapTx.end())
continue;
const Coin &coin = CoinsTip().AccessCoin(txin.prevout);
assert(!coin.IsSpent());
const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
if (coin.IsSpent() || (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY)) {
should_remove = true;
break;
}
}
}
// CheckSequenceLocks updates lp. Update the mempool entry LockPoints.
if (!validLP) m_mempool->mapTx.modify(it, update_lock_points(lp));
return should_remove;
};
// We also need to remove any now-immature transactions // We also need to remove any now-immature transactions
m_mempool->removeForReorg(*this, STANDARD_LOCKTIME_VERIFY_FLAGS); m_mempool->removeForReorg(m_chain, check_final_and_mature);
// Re-limit mempool size, in case we added any transactions // Re-limit mempool size, in case we added any transactions
LimitMempoolSize( LimitMempoolSize(
*m_mempool, *m_mempool,
@ -1386,12 +1401,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
AddCoins(inputs, tx, nHeight); AddCoins(inputs, tx, nHeight);
} }
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
{
CTxUndo txundo;
UpdateCoins(tx, inputs, txundo, nHeight);
}
bool CScriptCheck::operator()() { bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
PrecomputedTransactionData txdata(*ptxTo); PrecomputedTransactionData txdata(*ptxTo);
@ -1948,8 +1957,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// can be duplicated to remove the ability to spend the first instance -- even after // can be duplicated to remove the ability to spend the first instance -- even after
// being sent to another address. // being sent to another address.
// See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information. // See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information.
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
// already refuses previously-known transaction ids entirely.
// This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC. // This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC.
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
// two in the chain that violate it. This prevents exploiting the issue against nodes during their // two in the chain that violate it. This prevents exploiting the issue against nodes during their
@ -2939,7 +2946,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
// any disconnected transactions back to the mempool. // any disconnected transactions back to the mempool.
MaybeUpdateMempoolForReorg(disconnectpool, true); MaybeUpdateMempoolForReorg(disconnectpool, true);
} }
if (m_mempool) m_mempool->check(*this); if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1);
CheckForkWarningConditions(); CheckForkWarningConditions();
@ -3986,6 +3993,19 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
return true; return true;
} }
MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef& tx, bool test_accept, bool bypass_limits)
{
CChainState& active_chainstate = ActiveChainstate();
if (!active_chainstate.m_mempool) {
TxValidationState state;
state.Invalid(TxValidationResult::TX_NO_MEMPOOL, "no-mempool");
return MempoolAcceptResult::Failure(state);
}
auto result = AcceptToMemoryPool(active_chainstate, *active_chainstate.m_mempool, tx, bypass_limits, test_accept);
active_chainstate.m_mempool->check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
return result;
}
bool TestBlockValidity(BlockValidationState& state, bool TestBlockValidity(BlockValidationState& state,
llmq::CChainLocksHandler& clhandler, llmq::CChainLocksHandler& clhandler,
CEvoDB& evoDb, CEvoDB& evoDb,

View File

@ -12,6 +12,7 @@
#endif #endif
#include <amount.h> #include <amount.h>
#include <arith_uint256.h>
#include <attributes.h> #include <attributes.h>
#include <chain.h> #include <chain.h>
#include <fs.h> #include <fs.h>
@ -19,10 +20,11 @@
#include <policy/feerate.h> #include <policy/feerate.h>
#include <policy/packages.h> #include <policy/packages.h>
#include <script/script_error.h> #include <script/script_error.h>
#include <serialize.h>
#include <sync.h> #include <sync.h>
#include <txdb.h> #include <txdb.h>
#include <txmempool.h> // For CTxMemPool::cs #include <txmempool.h> // For CTxMemPool::cs
#include <serialize.h> #include <uint256.h>
#include <util/check.h> #include <util/check.h>
#include <util/hasher.h> #include <util/hasher.h>
#include <util/translation.h> #include <util/translation.h>
@ -38,17 +40,10 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace llmq {
class CChainLocksHandler;
class CInstantSendManager;
} // namespace llmq
class CEvoDB;
class CChainState; class CChainState;
class CBlockIndex;
class CBlockTreeDB; class CBlockTreeDB;
class CChainParams; class CChainParams;
class CEvoDB;
class CMNHFManager; class CMNHFManager;
class CTxMemPool; class CTxMemPool;
class TxValidationState; class TxValidationState;
@ -61,6 +56,11 @@ struct DisconnectedBlockTransactions;
struct LockPoints; struct LockPoints;
struct AssumeutxoData; struct AssumeutxoData;
namespace llmq {
class CChainLocksHandler;
class CInstantSendManager;
} // namespace llmq
/** Default for -minrelaytxfee, minimum relay fee for transactions */ /** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000; static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
/** Default for -limitancestorcount, max number of in-mempool ancestors */ /** Default for -limitancestorcount, max number of in-mempool ancestors */
@ -240,9 +240,16 @@ struct PackageMempoolAcceptResult
}; };
/** /**
* (Try to) add a transaction to the memory pool. * Try to add a transaction to the mempool. This is an internal function and is
* @param[in] bypass_limits When true, don't enforce mempool fee limits. * exposed only for testing. Client code should use ChainstateManager::ProcessTransaction()
* @param[in] test_accept When true, run validation checks but don't submit to mempool. *
* @param[in] active_chainstate Reference to the active chainstate.
* @param[in] pool Reference to the node's mempool.
* @param[in] tx The transaction to submit for mempool acceptance.
* @param[in] bypass_limits When true, don't enforce mempool fee and capacity limits.
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
*
* @returns a MempoolAcceptResult indicating whether the transaction was accepted/rejected with reason.
*/ */
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx, MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -266,9 +273,6 @@ bool GetUTXOCoin(CChainState& active_chainstate, const COutPoint& outpoint, Coin
int GetUTXOHeight(CChainState& active_chainstate, const COutPoint& outpoint); int GetUTXOHeight(CChainState& active_chainstate, const COutPoint& outpoint);
int GetUTXOConfirmations(CChainState& active_chainstate, const COutPoint& outpoint); int GetUTXOConfirmations(CChainState& active_chainstate, const COutPoint& outpoint);
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
/** Transaction validation functions */ /** Transaction validation functions */
/** /**
@ -280,11 +284,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
*/ */
bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Test whether the LockPoints height and time are still valid on the current chain
*/
bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** /**
* Check if transaction will be BIP68 final in the next block to be created on top of tip. * Check if transaction will be BIP68 final in the next block to be created on top of tip.
* @param[in] tip Chain tip to check tx sequence locks against. For example, * @param[in] tip Chain tip to check tx sequence locks against. For example,
@ -1024,6 +1023,16 @@ public:
*/ */
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main); bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
/**
* Try to add a transaction to the memory pool.
*
* @param[in] tx The transaction to submit for mempool acceptance.
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
* @param[in] bypass_limits When true, don't enforce mempool fee and capacity limits.
*/
[[nodiscard]] MempoolAcceptResult ProcessTransaction(const CTransactionRef& tx, bool test_accept=false, bool bypass_limits=false)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex //! Load the block tree and coins database from disk, initializing state if we're running with -reindex
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);

View File

@ -15,14 +15,12 @@ becomes valid.
import copy import copy
import time import time
from test_framework.blocktools import create_block, create_coinbase, create_tx_with_script from test_framework.blocktools import MAX_FUTURE_BLOCK_TIME, create_block, create_coinbase, create_tx_with_script
from test_framework.messages import COIN from test_framework.messages import COIN
from test_framework.p2p import P2PDataStore from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal from test_framework.util import assert_equal
MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
class InvalidBlockRequestTest(BitcoinTestFramework): class InvalidBlockRequestTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):

View File

@ -27,9 +27,10 @@ import subprocess
from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE
from test_framework.blocktools import ( from test_framework.blocktools import (
MAX_FUTURE_BLOCK_TIME,
TIME_GENESIS_BLOCK,
create_block, create_block,
create_coinbase, create_coinbase,
TIME_GENESIS_BLOCK,
) )
from test_framework.governance import EXPECTED_STDERR_NO_GOV_PRUNE from test_framework.governance import EXPECTED_STDERR_NO_GOV_PRUNE
from test_framework.messages import ( from test_framework.messages import (
@ -48,25 +49,27 @@ from test_framework.util import (
assert_is_hex_string, assert_is_hex_string,
assert_is_hash_string, assert_is_hash_string,
get_datadir_path, get_datadir_path,
set_node_times,
) )
from test_framework.wallet import MiniWallet from test_framework.wallet import MiniWallet
HEIGHT = 200 # blocks mined HEIGHT = 200 # blocks mined
TIME_RANGE_STEP = 156 # ten-minute steps TIME_RANGE_STEP = 156 # two and a half minute steps
TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP
TIME_RANGE_TIP = TIME_GENESIS_BLOCK + (HEIGHT - 1) * TIME_RANGE_STEP
TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
class BlockchainTest(BitcoinTestFramework): class BlockchainTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.disable_mocktime = True
self.setup_clean_chain = True self.setup_clean_chain = True
self.num_nodes = 1 self.num_nodes = 1
self.supports_cli = False self.supports_cli = False
def run_test(self): def run_test(self):
self.mine_chain() self.mine_chain()
self._test_max_future_block_time()
self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1', '-txindex=0']) # Set extra args with pruning after rescan is complete self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1', '-txindex=0']) # Set extra args with pruning after rescan is complete
# Actual tests # Actual tests
@ -85,10 +88,23 @@ class BlockchainTest(BitcoinTestFramework):
self.log.info(f"Generate {HEIGHT} blocks after the genesis block in 156 sec") self.log.info(f"Generate {HEIGHT} blocks after the genesis block in 156 sec")
for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP): for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP):
# 156 sec steps from genesis block time # 156 sec steps from genesis block time
set_node_times(self.nodes, t) self.nodes[0].setmocktime(t)
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2SH_OP_TRUE) self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2SH_OP_TRUE)
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200) assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
def _test_max_future_block_time(self):
self.stop_node(0)
self.log.info("A block tip of more than MAX_FUTURE_BLOCK_TIME in the future raises an error")
self.nodes[0].assert_start_raises_init_error(
extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME - 1}"],
expected_msg=": The block database contains a block which appears to be from the future."
" This may be due to your computer's date and time being set incorrectly."
f" Only rebuild the block database if you are sure that your computer's date and time are correct.{os.linesep}"
"Please restart with -reindex or -reindex-chainstate to recover.",
)
self.log.info("A block tip of MAX_FUTURE_BLOCK_TIME in the future is fine")
self.start_node(0, extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME}"])
def _test_getblockchaininfo(self): def _test_getblockchaininfo(self):
self.log.info("Test getblockchaininfo") self.log.info("Test getblockchaininfo")

View File

@ -31,6 +31,8 @@ MAX_BLOCK_SIGOPS = 40000
# Genesis block time (regtest) # Genesis block time (regtest)
TIME_GENESIS_BLOCK = 1417713337 TIME_GENESIS_BLOCK = 1417713337
MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule) # Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
COINBASE_MATURITY = 100 COINBASE_MATURITY = 100

View File

@ -631,6 +631,8 @@ class NetworkThread(threading.Thread):
NetworkThread.listeners = {} NetworkThread.listeners = {}
NetworkThread.protos = {} NetworkThread.protos = {}
if sys.platform == 'win32':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
NetworkThread.network_event_loop = asyncio.new_event_loop() NetworkThread.network_event_loop = asyncio.new_event_loop()
def run(self): def run(self):

View File

@ -18,7 +18,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel" "qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel" "qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel" "qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
"txmempool -> validation -> txmempool"
"wallet/fees -> wallet/wallet -> wallet/fees" "wallet/fees -> wallet/wallet -> wallet/fees"
"wallet/wallet -> wallet/walletdb -> wallet/wallet" "wallet/wallet -> wallet/walletdb -> wallet/wallet"
"node/coinstats -> validation -> node/coinstats" "node/coinstats -> validation -> node/coinstats"
@ -63,12 +62,13 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"llmq/chainlocks -> validation -> llmq/chainlocks" "llmq/chainlocks -> validation -> llmq/chainlocks"
"coinjoin/coinjoin -> llmq/chainlocks -> net -> coinjoin/coinjoin" "coinjoin/coinjoin -> llmq/chainlocks -> net -> coinjoin/coinjoin"
"evo/assetlocktx -> validation -> txmempool -> evo/assetlocktx"
"evo/deterministicmns -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns -> evo/deterministicmns" "evo/deterministicmns -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns -> evo/deterministicmns"
"evo/deterministicmns -> llmq/utils -> net -> evo/deterministicmns" "evo/deterministicmns -> llmq/utils -> net -> evo/deterministicmns"
"evo/deterministicmns -> validation -> txmempool -> evo/deterministicmns"
"policy/policy -> policy/settings -> policy/policy" "policy/policy -> policy/settings -> policy/policy"
"consensus/tx_verify -> evo/assetlocktx -> validation -> consensus/tx_verify" "consensus/tx_verify -> evo/assetlocktx -> validation -> consensus/tx_verify"
"consensus/tx_verify -> evo/assetlocktx -> llmq/signing -> net_processing -> txmempool -> consensus/tx_verify" "consensus/tx_verify -> evo/assetlocktx -> validation -> txmempool -> consensus/tx_verify"
"evo/assetlocktx -> llmq/signing -> net_processing -> txmempool -> evo/assetlocktx"
"evo/simplifiedmns -> llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns" "evo/simplifiedmns -> llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns"
"llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor" "llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor"