mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
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: utACK8c3ff618d3
PastaPastaPasta: utACK8c3ff618d3
Tree-SHA512: ea1eaad7279b6608a07c1175e5c3b44385d42e33afa8ce5160d871fc9b37a014e9981eafca379ac3ad6dc141b5fda6f1e676b4cc9658a6d1775fe929a134ff67
This commit is contained in:
commit
5cba8e29fc
6
doc/release-notes-6296.md
Normal file
6
doc/release-notes-6296.md
Normal 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.
|
@ -150,8 +150,9 @@ BITCOIN_TESTS =\
|
||||
test/script_p2sh_tests.cpp \
|
||||
test/script_p2pk_tests.cpp \
|
||||
test/script_p2pkh_tests.cpp \
|
||||
test/script_tests.cpp \
|
||||
test/script_parse_tests.cpp \
|
||||
test/script_standard_tests.cpp \
|
||||
test/script_tests.cpp \
|
||||
test/scriptnum_tests.cpp \
|
||||
test/serfloat_tests.cpp \
|
||||
test/serialize_tests.cpp \
|
||||
|
@ -39,10 +39,10 @@ static void AssembleBlock(benchmark::Bench& bench)
|
||||
txs.at(b) = MakeTransactionRef(tx);
|
||||
}
|
||||
{
|
||||
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
|
||||
LOCK(::cs_main);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <policy/policy.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <txmempool.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -26,14 +27,8 @@ struct Available {
|
||||
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<CTransactionRef> ordered_coins;
|
||||
// Create some base transactions
|
||||
@ -57,8 +52,10 @@ static void ComplexMemPool(benchmark::Bench& bench)
|
||||
size_t idx = det_rand.randrange(available_coins.size());
|
||||
Available coin = available_coins[idx];
|
||||
uint256 hash = coin.ref->GetHash();
|
||||
// biased towards taking just one ancestor, 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);
|
||||
// biased towards taking min_ancestors parents, but maybe more
|
||||
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) {
|
||||
tx.vin.emplace_back();
|
||||
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));
|
||||
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);
|
||||
CTxMemPool pool;
|
||||
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(MempoolCheck);
|
||||
|
@ -793,7 +793,8 @@ bool CCoinJoinClientManager::CheckAutomaticBackup()
|
||||
//
|
||||
// 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 (nState != POOL_STATE_IDLE) return false;
|
||||
@ -946,7 +947,7 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainst
|
||||
return false;
|
||||
}
|
||||
} 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");
|
||||
if (!CreateCollateralTransaction(txMyCollateral, strReason)) {
|
||||
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason);
|
||||
@ -973,7 +974,8 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainst
|
||||
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 (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return false;
|
||||
@ -1016,7 +1018,7 @@ bool CCoinJoinClientManager::DoAutomaticDenominating(CChainState& active_chainst
|
||||
return false;
|
||||
}
|
||||
|
||||
fResult &= session.DoAutomaticDenominating(active_chainstate, connman, mempool, fDryRun);
|
||||
fResult &= session.DoAutomaticDenominating(chainman, connman, mempool, fDryRun);
|
||||
}
|
||||
|
||||
return fResult;
|
||||
@ -1864,7 +1866,7 @@ void CCoinJoinClientQueueManager::DoMaintenance()
|
||||
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 (m_is_masternode) return; // no client-side mixing on masternodes
|
||||
@ -1878,7 +1880,7 @@ void CCoinJoinClientManager::DoMaintenance(CChainState& active_chainstate, CConn
|
||||
CheckTimeout();
|
||||
ProcessPendingDsaRequest(connman);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1930,7 +1932,7 @@ void CoinJoinWalletManager::DoMaintenance()
|
||||
{
|
||||
LOCK(cs_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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,10 @@ class CCoinJoinClientQueueManager;
|
||||
class CConnman;
|
||||
class CDeterministicMN;
|
||||
class CDeterministicMNManager;
|
||||
class CNode;
|
||||
class ChainstateManager;
|
||||
class CMasternodeMetaMan;
|
||||
class CMasternodeSync;
|
||||
class CNode;
|
||||
class CoinJoinWalletManager;
|
||||
class CTxMemPool;
|
||||
|
||||
@ -74,10 +75,17 @@ public:
|
||||
using wallet_name_cjman_map = std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>>;
|
||||
|
||||
public:
|
||||
CoinJoinWalletManager(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
|
||||
const CMasternodeSync& mn_sync, const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode)
|
||||
: m_chainstate(chainstate), 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(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
|
||||
CMasternodeMetaMan& mn_metaman, const CTxMemPool& mempool, const CMasternodeSync& mn_sync,
|
||||
const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool 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() {
|
||||
@ -112,11 +120,11 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
CChainState& m_chainstate;
|
||||
ChainstateManager& m_chainman;
|
||||
CConnman& m_connman;
|
||||
CDeterministicMNManager& m_dmnman;
|
||||
CMasternodeMetaMan& m_mn_metaman;
|
||||
CTxMemPool& m_mempool;
|
||||
const CTxMemPool& m_mempool;
|
||||
const CMasternodeSync& m_mn_sync;
|
||||
const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
|
||||
|
||||
@ -202,7 +210,8 @@ public:
|
||||
bool GetMixingMasternodeInfo(CDeterministicMNCPtr& ret) const;
|
||||
|
||||
/// 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
|
||||
bool SubmitDenominate(CConnman& connman);
|
||||
@ -310,7 +319,8 @@ public:
|
||||
bool GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||
|
||||
/// 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 MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||
@ -326,7 +336,8 @@ public:
|
||||
|
||||
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);
|
||||
};
|
||||
|
@ -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)
|
||||
// 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);
|
||||
|
||||
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) {
|
||||
/* Fetch fee and fast-fail if ATMP fails regardless */
|
||||
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 */
|
||||
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
|
||||
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.nLockTime != 0) return false;
|
||||
@ -352,7 +353,7 @@ bool CoinJoin::IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mem
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
} else {
|
||||
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);
|
||||
if (!ATMPIfSaneFee(active_chainstate, mempool, MakeTransactionRef(txCollateral), /*test_accept=*/true)) {
|
||||
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n");
|
||||
if (!ATMPIfSaneFee(chainman, MakeTransactionRef(txCollateral), /*test_accept=*/true)) {
|
||||
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- didn't pass ATMPIfSaneFee()\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ class CChainState;
|
||||
class CConnman;
|
||||
class CBLSPublicKey;
|
||||
class CBlockIndex;
|
||||
class ChainstateManager;
|
||||
class CMasternodeSync;
|
||||
class CTxMemPool;
|
||||
class TxValidationState;
|
||||
@ -368,8 +369,7 @@ namespace CoinJoin
|
||||
constexpr CAmount GetMaxPoolAmount() { return COINJOIN_ENTRY_MAX_SIZE * vecStandardDenominations.front(); }
|
||||
|
||||
/// 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
|
||||
@ -402,7 +402,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
bool ATMPIfSaneFee(CChainState& active_chainstate, CTxMemPool& pool,
|
||||
const CTransactionRef &tx, bool test_accept = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept = false)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
#endif // BITCOIN_COINJOIN_COINJOIN_H
|
||||
|
@ -9,20 +9,20 @@
|
||||
#endif // ENABLE_WALLET
|
||||
#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,
|
||||
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
||||
std::unique_ptr<PeerManager>& peerman, bool relay_txes) :
|
||||
dstxman{std::make_unique<CDSTXManager>()},
|
||||
#ifdef ENABLE_WALLET
|
||||
walletman{std::make_unique<CoinJoinWalletManager>(chainstate, connman, dmnman, mn_metaman, mempool, mn_sync,
|
||||
queueman, /* is_masternode = */ mn_activeman != nullptr)},
|
||||
walletman{std::make_unique<CoinJoinWalletManager>(chainman, connman, dmnman, mn_metaman, mempool, mn_sync, queueman,
|
||||
/* is_masternode = */ mn_activeman != nullptr)},
|
||||
queueman{relay_txes
|
||||
? std::make_unique<CCoinJoinClientQueueManager>(connman, peerman, *walletman, dmnman, mn_metaman,
|
||||
mn_sync, /* is_masternode = */ mn_activeman != nullptr)
|
||||
: nullptr},
|
||||
#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)}
|
||||
{}
|
||||
|
||||
|
@ -13,11 +13,11 @@
|
||||
|
||||
class CActiveMasternodeManager;
|
||||
class CBlockPolicyEstimator;
|
||||
class CChainState;
|
||||
class CCoinJoinServer;
|
||||
class CConnman;
|
||||
class CDeterministicMNManager;
|
||||
class CDSTXManager;
|
||||
class ChainstateManager;
|
||||
class CMasternodeMetaMan;
|
||||
class CMasternodeSync;
|
||||
class CTxMemPool;
|
||||
@ -31,7 +31,7 @@ class CoinJoinWalletManager;
|
||||
struct CJContext {
|
||||
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,
|
||||
const CMasternodeSync& mn_sync, std::unique_ptr<PeerManager>& peerman, bool relay_txes);
|
||||
~CJContext();
|
||||
|
@ -331,8 +331,8 @@ void CCoinJoinServer::CommitFinalTransaction()
|
||||
// See if the transaction is valid
|
||||
TRY_LOCK(cs_main, lockMain);
|
||||
mempool.PrioritiseTransaction(hashTx, 0.1 * COIN);
|
||||
if (!lockMain || !ATMPIfSaneFee(m_chainstate, mempool, finalTransaction)) {
|
||||
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n");
|
||||
if (!lockMain || !ATMPIfSaneFee(m_chainman, finalTransaction)) {
|
||||
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- ATMPIfSaneFee() error: Transaction not valid\n");
|
||||
WITH_LOCK(cs_coinjoin, SetNull());
|
||||
// not much we can do in this case, just notify clients
|
||||
RelayCompletedTransaction(ERR_INVALID_TX);
|
||||
@ -463,8 +463,8 @@ void CCoinJoinServer::ChargeRandomFees() const
|
||||
void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (!ATMPIfSaneFee(m_chainstate, mempool, txref, false /* bypass_limits */)) {
|
||||
LogPrint(BCLog::COINJOIN, "%s -- AcceptToMemoryPool failed\n", __func__);
|
||||
if (!ATMPIfSaneFee(m_chainman, txref)) {
|
||||
LogPrint(BCLog::COINJOIN, "%s -- ATMPIfSaneFee failed\n", __func__);
|
||||
} else {
|
||||
Assert(m_peerman)->RelayTransaction(txref->GetHash());
|
||||
LogPrint(BCLog::COINJOIN, "%s -- Collateral was consumed\n", __func__);
|
||||
@ -582,7 +582,7 @@ bool CCoinJoinServer::AddEntry(const CCoinJoinEntry& entry, PoolMessage& nMessag
|
||||
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__);
|
||||
nMessageIDRet = ERR_INVALID_COLLATERAL;
|
||||
return false;
|
||||
@ -616,7 +616,7 @@ bool CCoinJoinServer::AddEntry(const CCoinJoinEntry& entry, PoolMessage& nMessag
|
||||
}
|
||||
|
||||
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);
|
||||
if (fConsumeCollateral) {
|
||||
ConsumeCollateral(entry.txCollateral);
|
||||
@ -693,7 +693,7 @@ bool CCoinJoinServer::IsAcceptableDSA(const CCoinJoinAccept& dsa, PoolMessage& n
|
||||
}
|
||||
|
||||
// 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__);
|
||||
nMessageIDRet = ERR_INVALID_COLLATERAL;
|
||||
return false;
|
||||
|
@ -10,11 +10,11 @@
|
||||
#include <protocol.h>
|
||||
|
||||
class CActiveMasternodeManager;
|
||||
class CChainState;
|
||||
class CCoinJoinServer;
|
||||
class CDataStream;
|
||||
class CDeterministicMNManager;
|
||||
class CDSTXManager;
|
||||
class ChainstateManager;
|
||||
class CMasternodeMetaMan;
|
||||
class CNode;
|
||||
class CTxMemPool;
|
||||
@ -27,7 +27,7 @@ class UniValue;
|
||||
class CCoinJoinServer : public CCoinJoinBaseSession, public CCoinJoinBaseManager
|
||||
{
|
||||
private:
|
||||
CChainState& m_chainstate;
|
||||
ChainstateManager& m_chainman;
|
||||
CConnman& connman;
|
||||
CDeterministicMNManager& m_dmnman;
|
||||
CDSTXManager& m_dstxman;
|
||||
@ -90,11 +90,11 @@ private:
|
||||
void SetNull() override EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
|
||||
|
||||
public:
|
||||
explicit CCoinJoinServer(CChainState& chainstate, CConnman& _connman, CDeterministicMNManager& dmnman,
|
||||
explicit CCoinJoinServer(ChainstateManager& chainman, CConnman& _connman, CDeterministicMNManager& dmnman,
|
||||
CDSTXManager& dstxman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
|
||||
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
||||
std::unique_ptr<PeerManager>& peerman) :
|
||||
m_chainstate(chainstate),
|
||||
m_chainman(chainman),
|
||||
connman(_connman),
|
||||
m_dmnman(dmnman),
|
||||
m_dstxman(dstxman),
|
||||
|
@ -31,6 +31,7 @@ enum class TxValidationResult {
|
||||
TX_CONFLICT,
|
||||
TX_CONFLICT_LOCK, //!< conflicts with InstantSend lock
|
||||
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
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <protocol.h>
|
||||
#include <shutdown.h>
|
||||
#include <spork.h>
|
||||
#include <timedata.h>
|
||||
#include <util/ranges.h>
|
||||
#include <util/time.h>
|
||||
#include <validation.h>
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <masternode/sync.h>
|
||||
#include <messagesigner.h>
|
||||
#include <net_processing.h>
|
||||
#include <timedata.h>
|
||||
#include <util/string.h>
|
||||
#include <util/system.h>
|
||||
#include <validation.h>
|
||||
|
@ -6,13 +6,12 @@
|
||||
#define BITCOIN_INDEX_BASE_H
|
||||
|
||||
#include <dbwrapper.h>
|
||||
#include <primitives/block.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <threadinterrupt.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
class CBlock;
|
||||
class CBlockIndex;
|
||||
class CChainState;
|
||||
|
||||
|
@ -2,18 +2,14 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <index/disktxpos.h>
|
||||
#include <index/txindex.h>
|
||||
|
||||
#include <index/disktxpos.h>
|
||||
#include <node/blockstorage.h>
|
||||
#include <node/ui_interface.h>
|
||||
#include <shutdown.h>
|
||||
#include <util/system.h>
|
||||
#include <util/translation.h>
|
||||
#include <validation.h>
|
||||
|
||||
constexpr uint8_t DB_BEST_BLOCK{'B'};
|
||||
constexpr uint8_t DB_TXINDEX{'t'};
|
||||
constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
|
||||
|
||||
std::unique_ptr<TxIndex> g_txindex;
|
||||
|
||||
@ -30,10 +26,6 @@ public:
|
||||
|
||||
/// Write a batch of transaction positions to the DB.
|
||||
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) :
|
||||
@ -54,163 +46,12 @@ bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_
|
||||
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)
|
||||
: m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
|
||||
{}
|
||||
|
||||
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)
|
||||
{
|
||||
// Exclude genesis block transaction because outputs are not spendable.
|
||||
|
@ -5,9 +5,7 @@
|
||||
#ifndef BITCOIN_INDEX_TXINDEX_H
|
||||
#define BITCOIN_INDEX_TXINDEX_H
|
||||
|
||||
#include <chain.h>
|
||||
#include <index/base.h>
|
||||
#include <txdb.h>
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
protected:
|
||||
/// Override base class init to migrate from old database.
|
||||
bool Init() override;
|
||||
|
||||
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
|
||||
|
||||
BaseIndex::DB& GetDB() const override;
|
||||
|
21
src/init.cpp
21
src/init.cpp
@ -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("-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("-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("-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);
|
||||
@ -1440,6 +1440,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||
{
|
||||
const ArgsManager& args = *Assert(node.args);
|
||||
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
|
||||
if (!CreatePidFile(args)) {
|
||||
// Detailed error printed inside CreatePidFile().
|
||||
@ -1891,7 +1897,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||
node.llmq_ctx->Stop();
|
||||
}
|
||||
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);
|
||||
// Enable CMNHFManager::{Process, Undo}Block
|
||||
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();
|
||||
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. "
|
||||
"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");
|
||||
@ -2153,7 +2159,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||
|
||||
// ********************************************************* 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);
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
@ -2193,6 +2199,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||
|
||||
// ********************************************************* Step 8: start indexers
|
||||
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);
|
||||
if (!g_txindex->Start(chainman.ActiveChainstate())) {
|
||||
return false;
|
||||
@ -2388,7 +2399,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||
connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
|
||||
connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
|
||||
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;
|
||||
|
||||
// Port to bind to if `-bind=addr` is provided without a `:port` suffix.
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <llmq/context.h>
|
||||
|
||||
#include <dbwrapper.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <llmq/blockprocessor.h>
|
||||
#include <llmq/chainlocks.h>
|
||||
@ -17,36 +18,41 @@
|
||||
#include <llmq/signing.h>
|
||||
#include <llmq/signing_shares.h>
|
||||
|
||||
LLMQContext::LLMQContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CEvoDB& evo_db,
|
||||
CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman,
|
||||
LLMQContext::LLMQContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
|
||||
CEvoDB& evo_db, CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman,
|
||||
CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman,
|
||||
const CMasternodeSync& mn_sync, const std::unique_ptr<PeerManager>& peerman, bool unit_tests,
|
||||
bool wipe) :
|
||||
is_masternode{mn_activeman != nullptr},
|
||||
bls_worker{std::make_shared<CBLSWorker>()},
|
||||
dkg_debugman{std::make_unique<llmq::CDKGDebugManager>()},
|
||||
quorum_block_processor{std::make_unique<llmq::CQuorumBlockProcessor>(chainstate, dmnman, evo_db)},
|
||||
qdkgsman{std::make_unique<llmq::CDKGSessionManager>(*bls_worker, chainstate, connman, dmnman, *dkg_debugman,
|
||||
mn_metaman, *quorum_block_processor, mn_activeman, sporkman,
|
||||
peerman, unit_tests, wipe)},
|
||||
qman{std::make_unique<llmq::CQuorumManager>(*bls_worker, chainstate, connman, dmnman, *qdkgsman, evo_db,
|
||||
*quorum_block_processor, mn_activeman, mn_sync, sporkman, unit_tests,
|
||||
wipe)},
|
||||
sigman{std::make_unique<llmq::CSigningManager>(connman, mn_activeman, chainstate, *qman, peerman, unit_tests, wipe)},
|
||||
quorum_block_processor{std::make_unique<llmq::CQuorumBlockProcessor>(chainman.ActiveChainstate(), dmnman, evo_db)},
|
||||
qdkgsman{std::make_unique<llmq::CDKGSessionManager>(*bls_worker, chainman.ActiveChainstate(), connman, dmnman,
|
||||
*dkg_debugman, mn_metaman, *quorum_block_processor,
|
||||
mn_activeman, sporkman, peerman, unit_tests, wipe)},
|
||||
qman{std::make_unique<llmq::CQuorumManager>(*bls_worker, chainman.ActiveChainstate(), connman, dmnman, *qdkgsman,
|
||||
evo_db, *quorum_block_processor, mn_activeman, mn_sync, sporkman,
|
||||
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)},
|
||||
clhandler{[&]() -> llmq::CChainLocksHandler* const {
|
||||
assert(llmq::chainLocksHandler == nullptr);
|
||||
llmq::chainLocksHandler = std::make_unique<llmq::CChainLocksHandler>(chainstate, *qman, *sigman, *shareman,
|
||||
sporkman, mempool, mn_sync, is_masternode);
|
||||
llmq::chainLocksHandler = std::make_unique<llmq::CChainLocksHandler>(chainman.ActiveChainstate(), *qman,
|
||||
*sigman, *shareman, sporkman, mempool,
|
||||
mn_sync, is_masternode);
|
||||
return llmq::chainLocksHandler.get();
|
||||
}()},
|
||||
isman{[&]() -> llmq::CInstantSendManager* const {
|
||||
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();
|
||||
}()},
|
||||
ehfSignalsHandler{
|
||||
std::make_unique<llmq::CEHFSignalsHandler>(chainstate, mnhfman, *sigman, *shareman, mempool, *qman)}
|
||||
ehfSignalsHandler{std::make_unique<llmq::CEHFSignalsHandler>(chainman, mnhfman, *sigman, *shareman, *qman)}
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
class CActiveMasternodeManager;
|
||||
class CBLSWorker;
|
||||
class CChainState;
|
||||
class CConnman;
|
||||
class ChainstateManager;
|
||||
class CDeterministicMNManager;
|
||||
class CDBWrapper;
|
||||
class CEvoDB;
|
||||
@ -40,7 +40,7 @@ private:
|
||||
public:
|
||||
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,
|
||||
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
||||
const std::unique_ptr<PeerManager>& peerman, bool unit_tests, bool wipe);
|
||||
|
@ -15,19 +15,17 @@
|
||||
#include <deploymentstatus.h>
|
||||
#include <index/txindex.h> // g_txindex
|
||||
#include <primitives/transaction.h>
|
||||
#include <txmempool.h>
|
||||
#include <validation.h>
|
||||
|
||||
namespace llmq {
|
||||
|
||||
|
||||
CEHFSignalsHandler::CEHFSignalsHandler(CChainState& chainstate, CMNHFManager& mnhfman, CSigningManager& sigman,
|
||||
CSigSharesManager& shareman, CTxMemPool& mempool, const CQuorumManager& qman) :
|
||||
chainstate(chainstate),
|
||||
CEHFSignalsHandler::CEHFSignalsHandler(ChainstateManager& chainman, CMNHFManager& mnhfman, CSigningManager& sigman,
|
||||
CSigSharesManager& shareman, const CQuorumManager& qman) :
|
||||
m_chainman(chainman),
|
||||
mnhfman(mnhfman),
|
||||
sigman(sigman),
|
||||
shareman(shareman),
|
||||
mempool(mempool),
|
||||
qman(qman)
|
||||
{
|
||||
sigman.RegisterRecoveredSigsListener(this);
|
||||
@ -77,7 +75,7 @@ void CEHFSignalsHandler::trySignEHFSignal(int bit, const CBlockIndex* const pind
|
||||
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) {
|
||||
LogPrintf("CEHFSignalsHandler::trySignEHFSignal no quorum for id=%s\n", requestId.ToString());
|
||||
return;
|
||||
@ -103,7 +101,7 @@ MessageProcessingResult CEHFSignalsHandler::HandleNewRecoveredSig(const CRecover
|
||||
}
|
||||
|
||||
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;
|
||||
for (const auto& deployment : Params().GetConsensus().vDeployments) {
|
||||
// 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));
|
||||
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig Special EHF TX is created hash=%s\n", tx_to_sent->GetHash().ToString());
|
||||
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) {
|
||||
ret.m_transactions.push_back(tx_to_sent->GetHash());
|
||||
} else {
|
||||
|
@ -10,9 +10,8 @@
|
||||
#include <set>
|
||||
|
||||
class CBlockIndex;
|
||||
class CChainState;
|
||||
class ChainstateManager;
|
||||
class CMNHFManager;
|
||||
class CTxMemPool;
|
||||
|
||||
namespace llmq
|
||||
{
|
||||
@ -23,11 +22,10 @@ class CSigningManager;
|
||||
class CEHFSignalsHandler : public CRecoveredSigsListener
|
||||
{
|
||||
private:
|
||||
CChainState& chainstate;
|
||||
ChainstateManager& m_chainman;
|
||||
CMNHFManager& mnhfman;
|
||||
CSigningManager& sigman;
|
||||
CSigSharesManager& shareman;
|
||||
CTxMemPool& mempool;
|
||||
const CQuorumManager& qman;
|
||||
|
||||
/**
|
||||
@ -36,8 +34,8 @@ private:
|
||||
mutable Mutex cs;
|
||||
std::set<uint256> ids GUARDED_BY(cs);
|
||||
public:
|
||||
explicit CEHFSignalsHandler(CChainState& chainstate, CMNHFManager& mnhfman, CSigningManager& sigman,
|
||||
CSigSharesManager& shareman, CTxMemPool& mempool, const CQuorumManager& qman);
|
||||
explicit CEHFSignalsHandler(ChainstateManager& chainman, CMNHFManager& mnhfman, CSigningManager& sigman,
|
||||
CSigSharesManager& shareman, const CQuorumManager& qman);
|
||||
|
||||
~CEHFSignalsHandler();
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <netmessagemaker.h>
|
||||
|
||||
#include <addrdb.h>
|
||||
#include <addrman.h>
|
||||
#include <banman.h>
|
||||
#include <clientversion.h>
|
||||
#include <compat.h>
|
||||
|
@ -6,7 +6,6 @@
|
||||
#ifndef BITCOIN_NET_H
|
||||
#define BITCOIN_NET_H
|
||||
|
||||
#include <addrman.h>
|
||||
#include <bip324.h>
|
||||
#include <bloom.h>
|
||||
#include <chainparams.h>
|
||||
@ -50,14 +49,15 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class AddrMan;
|
||||
class BanMan;
|
||||
class CConnman;
|
||||
class CDeterministicMNList;
|
||||
class CDeterministicMNManager;
|
||||
class CMasternodeMetaMan;
|
||||
class CMasternodeSync;
|
||||
class CScheduler;
|
||||
class CNode;
|
||||
class BanMan;
|
||||
class CScheduler;
|
||||
struct bilingual_str;
|
||||
|
||||
/** Default for -whitelistrelay. */
|
||||
@ -100,7 +100,7 @@ static const bool DEFAULT_LISTEN = true;
|
||||
*/
|
||||
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
|
||||
/** 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*/
|
||||
static const bool DEFAULT_BLOCKSONLY = false;
|
||||
/** -peertimeout default */
|
||||
|
@ -856,9 +856,9 @@ private:
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
|
||||
|
||||
/**
|
||||
* Filter for transactions that were recently rejected by
|
||||
* AcceptToMemoryPool. These are not rerequested until the chain tip
|
||||
* changes, at which point the entire filter is reset.
|
||||
* Filter for transactions that were recently rejected by the mempool.
|
||||
* These are not rerequested until the chain tip changes, at which point
|
||||
* the entire filter is reset.
|
||||
*
|
||||
* Without this filter we'd be re-requesting txs from each of our peers,
|
||||
* 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_CONFLICT:
|
||||
case TxValidationResult::TX_MEMPOOL_POLICY:
|
||||
case TxValidationResult::TX_NO_MEMPOOL:
|
||||
// moved from BLOCK
|
||||
case TxValidationResult::TX_BAD_SPECIAL:
|
||||
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);
|
||||
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;
|
||||
|
||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
@ -3065,7 +3066,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_mempool.check(m_chainman.ActiveChainstate());
|
||||
}
|
||||
|
||||
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer,
|
||||
@ -4223,7 +4223,7 @@ void PeerManagerImpl::ProcessMessage(
|
||||
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;
|
||||
|
||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
@ -4234,7 +4234,6 @@ void PeerManagerImpl::ProcessMessage(
|
||||
m_cj_ctx->dstxman->AddDSTX(dstx);
|
||||
}
|
||||
|
||||
m_mempool.check(m_chainman.ActiveChainstate());
|
||||
RelayTransaction(tx.GetHash());
|
||||
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
|
||||
// this point and the tx will have been ignored. Because we haven't run
|
||||
// the tx through AcceptToMemoryPool, we won't have computed a DoS
|
||||
// this point and the tx will have been ignored. Because we haven't
|
||||
// submitted the tx to our mempool, we won't have computed a DoS
|
||||
// score for it or determined exactly why we consider it invalid.
|
||||
//
|
||||
// This means we won't penalize any peer subsequently relaying a DoSy
|
||||
|
@ -65,8 +65,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
||||
if (max_tx_fee > 0) {
|
||||
// First, call ATMP with test_accept and check the fee. If ATMP
|
||||
// fails here, return error immediately.
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx,
|
||||
bypass_limits, true /* test_accept */);
|
||||
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/true, bypass_limits);
|
||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||
return HandleATMPError(result.m_state, err_string.original);
|
||||
} 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.
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx,
|
||||
bypass_limits, false /* test_accept */);
|
||||
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/false, bypass_limits);
|
||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||
return HandleATMPError(result.m_state, err_string.original);
|
||||
}
|
||||
|
@ -134,10 +134,10 @@ static RPCHelpMan coinjoin_start()
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already.");
|
||||
}
|
||||
|
||||
const ChainstateManager& chainman = EnsureChainman(node);
|
||||
ChainstateManager& chainman = EnsureChainman(node);
|
||||
CTxMemPool& mempool = EnsureMemPool(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"));
|
||||
},
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <core_io.h>
|
||||
#include <evo/deterministicmns.h>
|
||||
#include <governance/classes.h>
|
||||
#include <governance/common.h>
|
||||
#include <governance/governance.h>
|
||||
#include <governance/validators.h>
|
||||
#include <governance/vote.h>
|
||||
@ -13,13 +14,13 @@
|
||||
#include <masternode/node.h>
|
||||
#include <masternode/sync.h>
|
||||
#include <messagesigner.h>
|
||||
#include <node/context.h>
|
||||
#include <net.h>
|
||||
#include <node/context.h>
|
||||
#include <rpc/blockchain.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/server_util.h>
|
||||
#include <rpc/util.h>
|
||||
#include <governance/common.h>
|
||||
#include <timedata.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
#include <validation.h>
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <rpc/server.h>
|
||||
|
||||
#include <addrman.h>
|
||||
#include <banman.h>
|
||||
#include <chainparams.h>
|
||||
#include <clientversion.h>
|
||||
|
@ -1246,12 +1246,13 @@ static RPCHelpMan testmempoolaccept()
|
||||
|
||||
NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||
CTxMemPool& mempool = EnsureMemPool(node);
|
||||
CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
|
||||
ChainstateManager& chainman = EnsureChainman(node);
|
||||
CChainState& chainstate = chainman.ActiveChainstate();
|
||||
const PackageMempoolAcceptResult package_result = [&] {
|
||||
LOCK(::cs_main);
|
||||
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
|
||||
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);
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <addrman.h>
|
||||
#include <chainparams.h>
|
||||
#include <chainparamsbase.h>
|
||||
#include <net.h>
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <addrman.h>
|
||||
#include <chainparams.h>
|
||||
#include <chainparamsbase.h>
|
||||
#include <net.h>
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
tx_pool.queryHashes(all_txids);
|
||||
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();
|
||||
}
|
||||
|
55
src/test/script_parse_tests.cpp
Normal file
55
src/test/script_parse_tests.cpp
Normal 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()
|
@ -43,7 +43,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
|
||||
LOCK(cs_main);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
|
||||
const auto ToMemPool = [this](const CMutableTransaction& tx) {
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -110,12 +110,12 @@ void DashTestSetup(NodeContext& node, const CChainParams& chainparams)
|
||||
node.dmnman = std::make_unique<CDeterministicMNManager>(chainstate, *node.connman, *node.evodb);
|
||||
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);
|
||||
#ifdef ENABLE_WALLET
|
||||
node.coinjoin_loader = interfaces::MakeCoinJoinLoader(*node.cj_ctx->walletman);
|
||||
#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);
|
||||
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,
|
||||
@ -519,7 +519,7 @@ CMutableTransaction TestChainSetup::CreateValidMempoolTransaction(CTransactionRe
|
||||
// If submit=true, add transaction to the mempool.
|
||||
if (submit) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1318,6 +1318,11 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney)
|
||||
BOOST_AUTO_TEST_CASE(util_ParseMoney)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -1355,11 +1360,18 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney)
|
||||
BOOST_CHECK(!ParseMoney(" "));
|
||||
|
||||
// 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 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
|
||||
BOOST_CHECK(!ParseMoney("92233720368.54775808"));
|
||||
|
||||
@ -2673,4 +2685,52 @@ BOOST_AUTO_TEST_CASE(remove_prefix)
|
||||
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()
|
||||
|
@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
23
src/txdb.cpp
23
src/txdb.cpp
@ -5,6 +5,7 @@
|
||||
|
||||
#include <txdb.h>
|
||||
|
||||
#include <chain.h>
|
||||
#include <node/ui_interface.h>
|
||||
#include <pow.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_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 {
|
||||
|
||||
struct CoinEntry {
|
||||
|
11
src/txdb.h
11
src/txdb.h
@ -8,19 +8,22 @@
|
||||
|
||||
#include <coins.h>
|
||||
#include <dbwrapper.h>
|
||||
#include <chain.h>
|
||||
#include <primitives/block.h>
|
||||
#include <spentindex.h>
|
||||
#include <timestampindex.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class CBlockFileInfo;
|
||||
class CBlockIndex;
|
||||
class CCoinsViewDBCursor;
|
||||
class uint256;
|
||||
namespace Consensus {
|
||||
struct Params;
|
||||
};
|
||||
struct bilingual_str;
|
||||
|
||||
//! -dbcache default (MiB)
|
||||
static const int64_t nDefaultDbCache = 300;
|
||||
@ -108,4 +111,6 @@ public:
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
};
|
||||
|
||||
std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db);
|
||||
|
||||
#endif // BITCOIN_TXDB_H
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include <txmempool.h>
|
||||
|
||||
#include <chain.h>
|
||||
#include <coins.h>
|
||||
#include <consensus/consensus.h>
|
||||
#include <consensus/tx_verify.h>
|
||||
#include <consensus/validation.h>
|
||||
@ -17,7 +19,6 @@
|
||||
#include <util/moneystr.h>
|
||||
#include <util/system.h>
|
||||
#include <util/time.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
#include <evo/specialtx.h>
|
||||
@ -29,6 +30,65 @@
|
||||
#include <cmath>
|
||||
#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,
|
||||
int64_t time, unsigned int entry_height,
|
||||
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)
|
||||
{
|
||||
CTxMemPoolEntry::Parents parents = it->GetMemPoolParents();
|
||||
const CTxMemPoolEntry::Parents& parents = it->GetMemPoolParentsConst();
|
||||
// add or remove this tx as a child of each parent
|
||||
for (const CTxMemPoolEntry& parent : parents) {
|
||||
UpdateChild(mapTx.iterator_to(parent), it, add);
|
||||
@ -806,44 +866,24 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
|
||||
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
|
||||
AssertLockHeld(cs);
|
||||
AssertLockHeld(::cs_main);
|
||||
|
||||
setEntries txToRemove;
|
||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||
const CTransaction& tx = it->GetTx();
|
||||
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));
|
||||
}
|
||||
if (check_final_and_mature(it)) txToRemove.insert(it);
|
||||
}
|
||||
setEntries setAllRemoves;
|
||||
for (txiter it : txToRemove) {
|
||||
CalculateDescendants(it, setAllRemoves);
|
||||
}
|
||||
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)
|
||||
@ -1101,16 +1141,7 @@ void CTxMemPool::clear()
|
||||
_clear();
|
||||
}
|
||||
|
||||
static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
|
||||
{
|
||||
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
|
||||
void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
|
||||
{
|
||||
if (m_check_ratio == 0) return;
|
||||
|
||||
@ -1123,20 +1154,16 @@ void CTxMemPool::check(CChainState& active_chainstate) const
|
||||
uint64_t checkTotal = 0;
|
||||
CAmount check_total_fee{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));
|
||||
const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
|
||||
|
||||
std::list<const CTxMemPoolEntry*> waitingOnDependants;
|
||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||
unsigned int i = 0;
|
||||
for (const auto& it : GetSortedDepthAndScore()) {
|
||||
checkTotal += it->GetTxSize();
|
||||
check_total_fee += it->GetFee();
|
||||
innerUsage += it->DynamicMemoryUsage();
|
||||
const CTransaction& tx = it->GetTx();
|
||||
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
|
||||
bool fDependsWait = false;
|
||||
CTxMemPoolEntry::Parents setParentCheck;
|
||||
for (const CTxIn &txin : tx.vin) {
|
||||
// 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()) {
|
||||
const CTransaction& tx2 = it2->GetTx();
|
||||
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
|
||||
fDependsWait = true;
|
||||
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.
|
||||
auto it3 = mapNextTx.find(txin.prevout);
|
||||
assert(it3 != mapNextTx.end());
|
||||
assert(it3->first == &txin.prevout);
|
||||
assert(it3->second == &tx);
|
||||
i++;
|
||||
}
|
||||
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
|
||||
return a.GetTx().GetHash() == b.GetTx().GetHash();
|
||||
@ -1181,6 +1208,9 @@ void CTxMemPool::check(CChainState& active_chainstate) const
|
||||
assert(it->GetSizeWithAncestors() == nSizeCheck);
|
||||
assert(it->GetSigOpCountWithAncestors() == nSigOpCheck);
|
||||
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
|
||||
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...
|
||||
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
|
||||
|
||||
if (fDependsWait)
|
||||
waitingOnDependants.push_back(&(*it));
|
||||
else {
|
||||
CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
||||
CAmount txfee = 0;
|
||||
assert(!tx.IsCoinBase());
|
||||
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());
|
||||
}
|
||||
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
|
||||
uint256 hash = it->second->GetHash();
|
||||
|
@ -30,12 +30,13 @@
|
||||
#include <util/epochguard.h>
|
||||
#include <util/hasher.h>
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/sequenced_index.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
|
||||
class CBlockIndex;
|
||||
class CChain;
|
||||
class CChainState;
|
||||
extern RecursiveMutex cs_main;
|
||||
|
||||
@ -59,6 +60,11 @@ struct LockPoints {
|
||||
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 {
|
||||
// SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T>
|
||||
// (e.g. a wrapped CTxMemPoolEntry&)
|
||||
@ -171,58 +177,6 @@ public:
|
||||
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
|
||||
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
|
||||
struct descendant_score {};
|
||||
struct entry_time {};
|
||||
@ -645,7 +609,7 @@ public:
|
||||
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
|
||||
* 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,
|
||||
// to track size/count of descendant transactions. First version of
|
||||
@ -667,7 +631,10 @@ public:
|
||||
bool removeSpentIndex(const uint256 txhash);
|
||||
|
||||
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 removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
@ -20,9 +20,9 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
||||
case TransactionError::P2P_DISABLED:
|
||||
return Untranslated("Peer-to-peer functionality missing or disabled");
|
||||
case TransactionError::MEMPOOL_REJECTED:
|
||||
return Untranslated("Transaction rejected by AcceptToMemoryPool");
|
||||
return Untranslated("Transaction rejected by mempool");
|
||||
case TransactionError::MEMPOOL_ERROR:
|
||||
return Untranslated("AcceptToMemoryPool failed");
|
||||
return Untranslated("Mempool internal error");
|
||||
case TransactionError::INVALID_PSBT:
|
||||
return Untranslated("PSBT is not well-formed");
|
||||
case TransactionError::PSBT_MISMATCH:
|
||||
|
@ -33,10 +33,6 @@ public:
|
||||
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
|
||||
* 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
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 r;
|
||||
@ -535,3 +522,62 @@ std::string Capitalize(std::string str)
|
||||
str[0] = ToUpper(str.front());
|
||||
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;
|
||||
}
|
||||
|
@ -28,6 +28,23 @@ enum SafeChars
|
||||
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
|
||||
* 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);
|
||||
|
||||
/** 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
|
||||
|
@ -154,7 +154,7 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
|
||||
|
||||
// CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
|
||||
// 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
|
||||
// transaction can be part of the *next* block, we need to call
|
||||
// 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);
|
||||
}
|
||||
|
||||
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,
|
||||
const CCoinsView& coins_view,
|
||||
const CTransaction& tx,
|
||||
@ -389,8 +371,41 @@ void CChainState::MaybeUpdateMempoolForReorg(
|
||||
// the disconnectpool that were added back and cleans up the mempool state.
|
||||
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
|
||||
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
|
||||
LimitMempoolSize(
|
||||
*m_mempool,
|
||||
@ -1386,12 +1401,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
||||
AddCoins(inputs, tx, nHeight);
|
||||
}
|
||||
|
||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
||||
{
|
||||
CTxUndo txundo;
|
||||
UpdateCoins(tx, inputs, txundo, nHeight);
|
||||
}
|
||||
|
||||
bool CScriptCheck::operator()() {
|
||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||
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
|
||||
// being sent to another address.
|
||||
// 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.
|
||||
// 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
|
||||
@ -2939,7 +2946,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
|
||||
// any disconnected transactions back to the mempool.
|
||||
MaybeUpdateMempoolForReorg(disconnectpool, true);
|
||||
}
|
||||
if (m_mempool) m_mempool->check(*this);
|
||||
if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1);
|
||||
|
||||
CheckForkWarningConditions();
|
||||
|
||||
@ -3986,6 +3993,19 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
|
||||
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,
|
||||
llmq::CChainLocksHandler& clhandler,
|
||||
CEvoDB& evoDb,
|
||||
|
@ -12,6 +12,7 @@
|
||||
#endif
|
||||
|
||||
#include <amount.h>
|
||||
#include <arith_uint256.h>
|
||||
#include <attributes.h>
|
||||
#include <chain.h>
|
||||
#include <fs.h>
|
||||
@ -19,10 +20,11 @@
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/packages.h>
|
||||
#include <script/script_error.h>
|
||||
#include <serialize.h>
|
||||
#include <sync.h>
|
||||
#include <txdb.h>
|
||||
#include <txmempool.h> // For CTxMemPool::cs
|
||||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
#include <util/check.h>
|
||||
#include <util/hasher.h>
|
||||
#include <util/translation.h>
|
||||
@ -38,17 +40,10 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace llmq {
|
||||
class CChainLocksHandler;
|
||||
class CInstantSendManager;
|
||||
} // namespace llmq
|
||||
|
||||
class CEvoDB;
|
||||
|
||||
class CChainState;
|
||||
class CBlockIndex;
|
||||
class CBlockTreeDB;
|
||||
class CChainParams;
|
||||
class CEvoDB;
|
||||
class CMNHFManager;
|
||||
class CTxMemPool;
|
||||
class TxValidationState;
|
||||
@ -61,6 +56,11 @@ struct DisconnectedBlockTransactions;
|
||||
struct LockPoints;
|
||||
struct AssumeutxoData;
|
||||
|
||||
namespace llmq {
|
||||
class CChainLocksHandler;
|
||||
class CInstantSendManager;
|
||||
} // namespace llmq
|
||||
|
||||
/** Default for -minrelaytxfee, minimum relay fee for transactions */
|
||||
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
|
||||
/** Default for -limitancestorcount, max number of in-mempool ancestors */
|
||||
@ -240,9 +240,16 @@ struct PackageMempoolAcceptResult
|
||||
};
|
||||
|
||||
/**
|
||||
* (Try to) add a transaction to the memory pool.
|
||||
* @param[in] bypass_limits When true, don't enforce mempool fee limits.
|
||||
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
|
||||
* Try to add a transaction to the mempool. This is an internal function and is
|
||||
* exposed only for testing. Client code should use ChainstateManager::ProcessTransaction()
|
||||
*
|
||||
* @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,
|
||||
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 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 */
|
||||
|
||||
/**
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
|
@ -15,14 +15,12 @@ becomes valid.
|
||||
import copy
|
||||
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.p2p import P2PDataStore
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
|
||||
|
||||
|
||||
class InvalidBlockRequestTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
|
@ -27,9 +27,10 @@ import subprocess
|
||||
|
||||
from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE
|
||||
from test_framework.blocktools import (
|
||||
MAX_FUTURE_BLOCK_TIME,
|
||||
TIME_GENESIS_BLOCK,
|
||||
create_block,
|
||||
create_coinbase,
|
||||
TIME_GENESIS_BLOCK,
|
||||
)
|
||||
from test_framework.governance import EXPECTED_STDERR_NO_GOV_PRUNE
|
||||
from test_framework.messages import (
|
||||
@ -48,25 +49,27 @@ from test_framework.util import (
|
||||
assert_is_hex_string,
|
||||
assert_is_hash_string,
|
||||
get_datadir_path,
|
||||
set_node_times,
|
||||
)
|
||||
from test_framework.wallet import MiniWallet
|
||||
|
||||
|
||||
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_TIP = TIME_GENESIS_BLOCK + (HEIGHT - 1) * TIME_RANGE_STEP
|
||||
TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
|
||||
|
||||
|
||||
class BlockchainTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.disable_mocktime = True
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
self.supports_cli = False
|
||||
|
||||
def run_test(self):
|
||||
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
|
||||
|
||||
# Actual tests
|
||||
@ -85,10 +88,23 @@ class BlockchainTest(BitcoinTestFramework):
|
||||
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):
|
||||
# 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)
|
||||
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):
|
||||
self.log.info("Test getblockchaininfo")
|
||||
|
||||
|
@ -31,6 +31,8 @@ MAX_BLOCK_SIGOPS = 40000
|
||||
# Genesis block time (regtest)
|
||||
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_MATURITY = 100
|
||||
|
||||
|
@ -631,6 +631,8 @@ class NetworkThread(threading.Thread):
|
||||
|
||||
NetworkThread.listeners = {}
|
||||
NetworkThread.protos = {}
|
||||
if sys.platform == 'win32':
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
NetworkThread.network_event_loop = asyncio.new_event_loop()
|
||||
|
||||
def run(self):
|
||||
|
@ -18,7 +18,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
||||
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
|
||||
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
|
||||
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
|
||||
"txmempool -> validation -> txmempool"
|
||||
"wallet/fees -> wallet/wallet -> wallet/fees"
|
||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
||||
"node/coinstats -> validation -> node/coinstats"
|
||||
@ -63,12 +62,13 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
||||
|
||||
"llmq/chainlocks -> validation -> llmq/chainlocks"
|
||||
"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 -> net -> evo/deterministicmns"
|
||||
"evo/deterministicmns -> validation -> txmempool -> evo/deterministicmns"
|
||||
"policy/policy -> policy/settings -> policy/policy"
|
||||
"consensus/tx_verify -> evo/assetlocktx -> validation -> consensus/tx_verify"
|
||||
"consensus/tx_verify -> evo/assetlocktx -> llmq/signing -> net_processing -> txmempool -> consensus/tx_verify"
|
||||
"evo/assetlocktx -> llmq/signing -> net_processing -> txmempool -> evo/assetlocktx"
|
||||
"consensus/tx_verify -> evo/assetlocktx -> validation -> txmempool -> consensus/tx_verify"
|
||||
|
||||
"evo/simplifiedmns -> llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns"
|
||||
"llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor"
|
||||
|
Loading…
Reference in New Issue
Block a user