mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +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_p2sh_tests.cpp \
|
||||||
test/script_p2pk_tests.cpp \
|
test/script_p2pk_tests.cpp \
|
||||||
test/script_p2pkh_tests.cpp \
|
test/script_p2pkh_tests.cpp \
|
||||||
test/script_tests.cpp \
|
test/script_parse_tests.cpp \
|
||||||
test/script_standard_tests.cpp \
|
test/script_standard_tests.cpp \
|
||||||
|
test/script_tests.cpp \
|
||||||
test/scriptnum_tests.cpp \
|
test/scriptnum_tests.cpp \
|
||||||
test/serfloat_tests.cpp \
|
test/serfloat_tests.cpp \
|
||||||
test/serialize_tests.cpp \
|
test/serialize_tests.cpp \
|
||||||
|
@ -39,10 +39,10 @@ static void AssembleBlock(benchmark::Bench& bench)
|
|||||||
txs.at(b) = MakeTransactionRef(tx);
|
txs.at(b) = MakeTransactionRef(tx);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
|
LOCK(::cs_main);
|
||||||
|
|
||||||
for (const auto& txr : txs) {
|
for (const auto& txr : txs) {
|
||||||
const MempoolAcceptResult res = ::AcceptToMemoryPool(test_setup->m_node.chainman->ActiveChainstate(), *test_setup->m_node.mempool, txr, false /* bypass_limits */);
|
const MempoolAcceptResult res = test_setup->m_node.chainman->ProcessTransaction(txr);
|
||||||
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -26,14 +27,8 @@ struct Available {
|
|||||||
Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}
|
Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ComplexMemPool(benchmark::Bench& bench)
|
static std::vector<CTransactionRef> CreateOrderedCoins(FastRandomContext& det_rand, int childTxs, int min_ancestors)
|
||||||
{
|
{
|
||||||
int childTxs = 800;
|
|
||||||
if (bench.complexityN() > 1) {
|
|
||||||
childTxs = static_cast<int>(bench.complexityN());
|
|
||||||
}
|
|
||||||
|
|
||||||
FastRandomContext det_rand{true};
|
|
||||||
std::vector<Available> available_coins;
|
std::vector<Available> available_coins;
|
||||||
std::vector<CTransactionRef> ordered_coins;
|
std::vector<CTransactionRef> ordered_coins;
|
||||||
// Create some base transactions
|
// Create some base transactions
|
||||||
@ -57,8 +52,10 @@ static void ComplexMemPool(benchmark::Bench& bench)
|
|||||||
size_t idx = det_rand.randrange(available_coins.size());
|
size_t idx = det_rand.randrange(available_coins.size());
|
||||||
Available coin = available_coins[idx];
|
Available coin = available_coins[idx];
|
||||||
uint256 hash = coin.ref->GetHash();
|
uint256 hash = coin.ref->GetHash();
|
||||||
// biased towards taking just one ancestor, but maybe more
|
// biased towards taking min_ancestors parents, but maybe more
|
||||||
size_t n_to_take = det_rand.randrange(2) == 0 ? 1 : 1+det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
|
size_t n_to_take = det_rand.randrange(2) == 0 ?
|
||||||
|
min_ancestors :
|
||||||
|
min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
|
||||||
for (size_t i = 0; i < n_to_take; ++i) {
|
for (size_t i = 0; i < n_to_take; ++i) {
|
||||||
tx.vin.emplace_back();
|
tx.vin.emplace_back();
|
||||||
tx.vin.back().prevout = COutPoint(hash, coin.vin_left++);
|
tx.vin.back().prevout = COutPoint(hash, coin.vin_left++);
|
||||||
@ -77,6 +74,17 @@ static void ComplexMemPool(benchmark::Bench& bench)
|
|||||||
ordered_coins.emplace_back(MakeTransactionRef(tx));
|
ordered_coins.emplace_back(MakeTransactionRef(tx));
|
||||||
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
|
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
|
||||||
}
|
}
|
||||||
|
return ordered_coins;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ComplexMemPool(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
FastRandomContext det_rand{true};
|
||||||
|
int childTxs = 800;
|
||||||
|
if (bench.complexityN() > 1) {
|
||||||
|
childTxs = static_cast<int>(bench.complexityN());
|
||||||
|
}
|
||||||
|
std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 1);
|
||||||
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
|
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
|
||||||
CTxMemPool pool;
|
CTxMemPool pool;
|
||||||
LOCK2(cs_main, pool.cs);
|
LOCK2(cs_main, pool.cs);
|
||||||
@ -89,4 +97,21 @@ static void ComplexMemPool(benchmark::Bench& bench)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void MempoolCheck(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
FastRandomContext det_rand{true};
|
||||||
|
const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000;
|
||||||
|
const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 5);
|
||||||
|
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN, {"-checkmempool=1"});
|
||||||
|
CTxMemPool pool;
|
||||||
|
LOCK2(cs_main, pool.cs);
|
||||||
|
const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();
|
||||||
|
for (auto& tx : ordered_coins) AddTx(tx, pool);
|
||||||
|
|
||||||
|
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
|
||||||
|
pool.check(coins_tip, /* spendheight */ 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
BENCHMARK(ComplexMemPool);
|
BENCHMARK(ComplexMemPool);
|
||||||
|
BENCHMARK(MempoolCheck);
|
||||||
|
@ -793,7 +793,8 @@ bool CCoinJoinClientManager::CheckAutomaticBackup()
|
|||||||
//
|
//
|
||||||
// Passively run mixing in the background to mix funds based on the given configuration.
|
// Passively run mixing in the background to mix funds based on the given configuration.
|
||||||
//
|
//
|
||||||
bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun)
|
bool CCoinJoinClientSession::DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman,
|
||||||
|
const CTxMemPool& mempool, bool fDryRun)
|
||||||
{
|
{
|
||||||
if (m_is_masternode) return false; // no client-side mixing on masternodes
|
if (m_is_masternode) return false; // no client-side mixing on masternodes
|
||||||
if (nState != POOL_STATE_IDLE) return false;
|
if (nState != POOL_STATE_IDLE) return false;
|
||||||
@ -946,7 +947,7 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainst
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!CoinJoin::IsCollateralValid(active_chainstate, mempool, CTransaction(txMyCollateral))) {
|
if (!CoinJoin::IsCollateralValid(chainman, mempool, CTransaction(txMyCollateral))) {
|
||||||
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n");
|
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n");
|
||||||
if (!CreateCollateralTransaction(txMyCollateral, strReason)) {
|
if (!CreateCollateralTransaction(txMyCollateral, strReason)) {
|
||||||
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason);
|
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason);
|
||||||
@ -973,7 +974,8 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(CChainState& active_chainst
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinJoinClientManager::DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun)
|
bool CCoinJoinClientManager::DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman,
|
||||||
|
const CTxMemPool& mempool, bool fDryRun)
|
||||||
{
|
{
|
||||||
if (m_is_masternode) return false; // no client-side mixing on masternodes
|
if (m_is_masternode) return false; // no client-side mixing on masternodes
|
||||||
if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return false;
|
if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return false;
|
||||||
@ -1016,7 +1018,7 @@ bool CCoinJoinClientManager::DoAutomaticDenominating(CChainState& active_chainst
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fResult &= session.DoAutomaticDenominating(active_chainstate, connman, mempool, fDryRun);
|
fResult &= session.DoAutomaticDenominating(chainman, connman, mempool, fDryRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fResult;
|
return fResult;
|
||||||
@ -1864,7 +1866,7 @@ void CCoinJoinClientQueueManager::DoMaintenance()
|
|||||||
CheckQueue();
|
CheckQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCoinJoinClientManager::DoMaintenance(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool)
|
void CCoinJoinClientManager::DoMaintenance(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool)
|
||||||
{
|
{
|
||||||
if (!CCoinJoinClientOptions::IsEnabled()) return;
|
if (!CCoinJoinClientOptions::IsEnabled()) return;
|
||||||
if (m_is_masternode) return; // no client-side mixing on masternodes
|
if (m_is_masternode) return; // no client-side mixing on masternodes
|
||||||
@ -1878,7 +1880,7 @@ void CCoinJoinClientManager::DoMaintenance(CChainState& active_chainstate, CConn
|
|||||||
CheckTimeout();
|
CheckTimeout();
|
||||||
ProcessPendingDsaRequest(connman);
|
ProcessPendingDsaRequest(connman);
|
||||||
if (nDoAutoNextRun == nTick) {
|
if (nDoAutoNextRun == nTick) {
|
||||||
DoAutomaticDenominating(active_chainstate, connman, mempool);
|
DoAutomaticDenominating(chainman, connman, mempool);
|
||||||
nDoAutoNextRun = nTick + COINJOIN_AUTO_TIMEOUT_MIN + GetRandInt(COINJOIN_AUTO_TIMEOUT_MAX - COINJOIN_AUTO_TIMEOUT_MIN);
|
nDoAutoNextRun = nTick + COINJOIN_AUTO_TIMEOUT_MIN + GetRandInt(COINJOIN_AUTO_TIMEOUT_MAX - COINJOIN_AUTO_TIMEOUT_MIN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1930,7 +1932,7 @@ void CoinJoinWalletManager::DoMaintenance()
|
|||||||
{
|
{
|
||||||
LOCK(cs_wallet_manager_map);
|
LOCK(cs_wallet_manager_map);
|
||||||
for (auto& [_, clientman] : m_wallet_manager_map) {
|
for (auto& [_, clientman] : m_wallet_manager_map) {
|
||||||
clientman->DoMaintenance(m_chainstate, m_connman, m_mempool);
|
clientman->DoMaintenance(m_chainman, m_connman, m_mempool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,10 @@ class CCoinJoinClientQueueManager;
|
|||||||
class CConnman;
|
class CConnman;
|
||||||
class CDeterministicMN;
|
class CDeterministicMN;
|
||||||
class CDeterministicMNManager;
|
class CDeterministicMNManager;
|
||||||
class CNode;
|
class ChainstateManager;
|
||||||
class CMasternodeMetaMan;
|
class CMasternodeMetaMan;
|
||||||
class CMasternodeSync;
|
class CMasternodeSync;
|
||||||
|
class CNode;
|
||||||
class CoinJoinWalletManager;
|
class CoinJoinWalletManager;
|
||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
|
|
||||||
@ -74,10 +75,17 @@ public:
|
|||||||
using wallet_name_cjman_map = std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>>;
|
using wallet_name_cjman_map = std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CoinJoinWalletManager(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
|
CoinJoinWalletManager(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
|
||||||
const CMasternodeSync& mn_sync, const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode)
|
CMasternodeMetaMan& mn_metaman, const CTxMemPool& mempool, const CMasternodeSync& mn_sync,
|
||||||
: m_chainstate(chainstate), m_connman(connman), m_dmnman(dmnman), m_mn_metaman(mn_metaman), m_mempool(mempool), m_mn_sync(mn_sync),
|
const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode) :
|
||||||
m_queueman(queueman), m_is_masternode{is_masternode}
|
m_chainman(chainman),
|
||||||
|
m_connman(connman),
|
||||||
|
m_dmnman(dmnman),
|
||||||
|
m_mn_metaman(mn_metaman),
|
||||||
|
m_mempool(mempool),
|
||||||
|
m_mn_sync(mn_sync),
|
||||||
|
m_queueman(queueman),
|
||||||
|
m_is_masternode{is_masternode}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~CoinJoinWalletManager() {
|
~CoinJoinWalletManager() {
|
||||||
@ -112,11 +120,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CChainState& m_chainstate;
|
ChainstateManager& m_chainman;
|
||||||
CConnman& m_connman;
|
CConnman& m_connman;
|
||||||
CDeterministicMNManager& m_dmnman;
|
CDeterministicMNManager& m_dmnman;
|
||||||
CMasternodeMetaMan& m_mn_metaman;
|
CMasternodeMetaMan& m_mn_metaman;
|
||||||
CTxMemPool& m_mempool;
|
const CTxMemPool& m_mempool;
|
||||||
const CMasternodeSync& m_mn_sync;
|
const CMasternodeSync& m_mn_sync;
|
||||||
const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
|
const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
|
||||||
|
|
||||||
@ -202,7 +210,8 @@ public:
|
|||||||
bool GetMixingMasternodeInfo(CDeterministicMNCPtr& ret) const;
|
bool GetMixingMasternodeInfo(CDeterministicMNCPtr& ret) const;
|
||||||
|
|
||||||
/// Passively run mixing in the background according to the configuration in settings
|
/// Passively run mixing in the background according to the configuration in settings
|
||||||
bool DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin);
|
bool DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool,
|
||||||
|
bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin);
|
||||||
|
|
||||||
/// As a client, submit part of a future mixing transaction to a Masternode to start the process
|
/// As a client, submit part of a future mixing transaction to a Masternode to start the process
|
||||||
bool SubmitDenominate(CConnman& connman);
|
bool SubmitDenominate(CConnman& connman);
|
||||||
@ -310,7 +319,8 @@ public:
|
|||||||
bool GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
bool GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||||
|
|
||||||
/// Passively run mixing in the background according to the configuration in settings
|
/// Passively run mixing in the background according to the configuration in settings
|
||||||
bool DoAutomaticDenominating(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool, bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
bool DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool,
|
||||||
|
bool fDryRun = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||||
|
|
||||||
bool TrySubmitDenominate(const CService& mnAddr, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
bool TrySubmitDenominate(const CService& mnAddr, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||||
bool MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
bool MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||||
@ -326,7 +336,8 @@ public:
|
|||||||
|
|
||||||
void UpdatedBlockTip(const CBlockIndex* pindex);
|
void UpdatedBlockTip(const CBlockIndex* pindex);
|
||||||
|
|
||||||
void DoMaintenance(CChainState& active_chainstate, CConnman& connman, CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
void DoMaintenance(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||||
|
|
||||||
void GetJsonInfo(UniValue& obj) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
void GetJsonInfo(UniValue& obj) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions);
|
||||||
};
|
};
|
||||||
|
@ -308,10 +308,11 @@ bool CCoinJoinBaseSession::IsValidInOuts(CChainState& active_chainstate, const C
|
|||||||
|
|
||||||
// Responsibility for checking fee sanity is moved from the mempool to the client (BroadcastTransaction)
|
// Responsibility for checking fee sanity is moved from the mempool to the client (BroadcastTransaction)
|
||||||
// but CoinJoin still requires ATMP with fee sanity checks so we need to implement them separately
|
// but CoinJoin still requires ATMP with fee sanity checks so we need to implement them separately
|
||||||
bool ATMPIfSaneFee(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef &tx, bool test_accept) {
|
bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept)
|
||||||
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
|
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(active_chainstate, pool, tx, /* bypass_limits */ false, /* test_accept */ true);
|
const MempoolAcceptResult result = chainman.ProcessTransaction(tx, /*test_accept=*/true);
|
||||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||||
/* Fetch fee and fast-fail if ATMP fails regardless */
|
/* Fetch fee and fast-fail if ATMP fails regardless */
|
||||||
return false;
|
return false;
|
||||||
@ -322,11 +323,11 @@ bool ATMPIfSaneFee(CChainState& active_chainstate, CTxMemPool& pool, const CTran
|
|||||||
/* Don't re-run ATMP if only doing test run */
|
/* Don't re-run ATMP if only doing test run */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return AcceptToMemoryPool(active_chainstate, pool, tx, /* bypass_limits */ false, test_accept).m_result_type == MempoolAcceptResult::ResultType::VALID;
|
return chainman.ProcessTransaction(tx, test_accept).m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to make sure the collateral provided by the client is valid
|
// check to make sure the collateral provided by the client is valid
|
||||||
bool CoinJoin::IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mempool, const CTransaction& txCollateral)
|
bool CoinJoin::IsCollateralValid(ChainstateManager& chainman, const CTxMemPool& mempool, const CTransaction& txCollateral)
|
||||||
{
|
{
|
||||||
if (txCollateral.vout.empty()) return false;
|
if (txCollateral.vout.empty()) return false;
|
||||||
if (txCollateral.nLockTime != 0) return false;
|
if (txCollateral.nLockTime != 0) return false;
|
||||||
@ -352,7 +353,7 @@ bool CoinJoin::IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
nValueIn += mempoolTx->vout[txin.prevout.n].nValue;
|
nValueIn += mempoolTx->vout[txin.prevout.n].nValue;
|
||||||
} else if (GetUTXOCoin(active_chainstate, txin.prevout, coin)) {
|
} else if (GetUTXOCoin(chainman.ActiveChainstate(), txin.prevout, coin)) {
|
||||||
nValueIn += coin.out.nValue;
|
nValueIn += coin.out.nValue;
|
||||||
} else {
|
} else {
|
||||||
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); /* Continued */
|
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); /* Continued */
|
||||||
@ -370,8 +371,8 @@ bool CoinJoin::IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mem
|
|||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
if (!ATMPIfSaneFee(active_chainstate, mempool, MakeTransactionRef(txCollateral), /*test_accept=*/true)) {
|
if (!ATMPIfSaneFee(chainman, MakeTransactionRef(txCollateral), /*test_accept=*/true)) {
|
||||||
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n");
|
LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- didn't pass ATMPIfSaneFee()\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ class CChainState;
|
|||||||
class CConnman;
|
class CConnman;
|
||||||
class CBLSPublicKey;
|
class CBLSPublicKey;
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
|
class ChainstateManager;
|
||||||
class CMasternodeSync;
|
class CMasternodeSync;
|
||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
class TxValidationState;
|
class TxValidationState;
|
||||||
@ -368,8 +369,7 @@ namespace CoinJoin
|
|||||||
constexpr CAmount GetMaxPoolAmount() { return COINJOIN_ENTRY_MAX_SIZE * vecStandardDenominations.front(); }
|
constexpr CAmount GetMaxPoolAmount() { return COINJOIN_ENTRY_MAX_SIZE * vecStandardDenominations.front(); }
|
||||||
|
|
||||||
/// If the collateral is valid given by a client
|
/// If the collateral is valid given by a client
|
||||||
bool IsCollateralValid(CChainState& active_chainstate, CTxMemPool& mempool, const CTransaction& txCollateral);
|
bool IsCollateralValid(ChainstateManager& chainman, const CTxMemPool& mempool, const CTransaction& txCollateral);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CDSTXManager
|
class CDSTXManager
|
||||||
@ -402,7 +402,7 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ATMPIfSaneFee(CChainState& active_chainstate, CTxMemPool& pool,
|
bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept = false)
|
||||||
const CTransactionRef &tx, bool test_accept = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
#endif // BITCOIN_COINJOIN_COINJOIN_H
|
#endif // BITCOIN_COINJOIN_COINJOIN_H
|
||||||
|
@ -9,20 +9,20 @@
|
|||||||
#endif // ENABLE_WALLET
|
#endif // ENABLE_WALLET
|
||||||
#include <coinjoin/server.h>
|
#include <coinjoin/server.h>
|
||||||
|
|
||||||
CJContext::CJContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman,
|
CJContext::CJContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
|
||||||
CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
|
CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
|
||||||
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
||||||
std::unique_ptr<PeerManager>& peerman, bool relay_txes) :
|
std::unique_ptr<PeerManager>& peerman, bool relay_txes) :
|
||||||
dstxman{std::make_unique<CDSTXManager>()},
|
dstxman{std::make_unique<CDSTXManager>()},
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
walletman{std::make_unique<CoinJoinWalletManager>(chainstate, connman, dmnman, mn_metaman, mempool, mn_sync,
|
walletman{std::make_unique<CoinJoinWalletManager>(chainman, connman, dmnman, mn_metaman, mempool, mn_sync, queueman,
|
||||||
queueman, /* is_masternode = */ mn_activeman != nullptr)},
|
/* is_masternode = */ mn_activeman != nullptr)},
|
||||||
queueman{relay_txes
|
queueman{relay_txes
|
||||||
? std::make_unique<CCoinJoinClientQueueManager>(connman, peerman, *walletman, dmnman, mn_metaman,
|
? std::make_unique<CCoinJoinClientQueueManager>(connman, peerman, *walletman, dmnman, mn_metaman,
|
||||||
mn_sync, /* is_masternode = */ mn_activeman != nullptr)
|
mn_sync, /* is_masternode = */ mn_activeman != nullptr)
|
||||||
: nullptr},
|
: nullptr},
|
||||||
#endif // ENABLE_WALLET
|
#endif // ENABLE_WALLET
|
||||||
server{std::make_unique<CCoinJoinServer>(chainstate, connman, dmnman, *dstxman, mn_metaman, mempool, mn_activeman,
|
server{std::make_unique<CCoinJoinServer>(chainman, connman, dmnman, *dstxman, mn_metaman, mempool, mn_activeman,
|
||||||
mn_sync, peerman)}
|
mn_sync, peerman)}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@
|
|||||||
|
|
||||||
class CActiveMasternodeManager;
|
class CActiveMasternodeManager;
|
||||||
class CBlockPolicyEstimator;
|
class CBlockPolicyEstimator;
|
||||||
class CChainState;
|
|
||||||
class CCoinJoinServer;
|
class CCoinJoinServer;
|
||||||
class CConnman;
|
class CConnman;
|
||||||
class CDeterministicMNManager;
|
class CDeterministicMNManager;
|
||||||
class CDSTXManager;
|
class CDSTXManager;
|
||||||
|
class ChainstateManager;
|
||||||
class CMasternodeMetaMan;
|
class CMasternodeMetaMan;
|
||||||
class CMasternodeSync;
|
class CMasternodeSync;
|
||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
@ -31,7 +31,7 @@ class CoinJoinWalletManager;
|
|||||||
struct CJContext {
|
struct CJContext {
|
||||||
CJContext() = delete;
|
CJContext() = delete;
|
||||||
CJContext(const CJContext&) = delete;
|
CJContext(const CJContext&) = delete;
|
||||||
CJContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman,
|
CJContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
|
||||||
CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman,
|
CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman,
|
||||||
const CMasternodeSync& mn_sync, std::unique_ptr<PeerManager>& peerman, bool relay_txes);
|
const CMasternodeSync& mn_sync, std::unique_ptr<PeerManager>& peerman, bool relay_txes);
|
||||||
~CJContext();
|
~CJContext();
|
||||||
|
@ -331,8 +331,8 @@ void CCoinJoinServer::CommitFinalTransaction()
|
|||||||
// See if the transaction is valid
|
// See if the transaction is valid
|
||||||
TRY_LOCK(cs_main, lockMain);
|
TRY_LOCK(cs_main, lockMain);
|
||||||
mempool.PrioritiseTransaction(hashTx, 0.1 * COIN);
|
mempool.PrioritiseTransaction(hashTx, 0.1 * COIN);
|
||||||
if (!lockMain || !ATMPIfSaneFee(m_chainstate, mempool, finalTransaction)) {
|
if (!lockMain || !ATMPIfSaneFee(m_chainman, finalTransaction)) {
|
||||||
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n");
|
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- ATMPIfSaneFee() error: Transaction not valid\n");
|
||||||
WITH_LOCK(cs_coinjoin, SetNull());
|
WITH_LOCK(cs_coinjoin, SetNull());
|
||||||
// not much we can do in this case, just notify clients
|
// not much we can do in this case, just notify clients
|
||||||
RelayCompletedTransaction(ERR_INVALID_TX);
|
RelayCompletedTransaction(ERR_INVALID_TX);
|
||||||
@ -463,8 +463,8 @@ void CCoinJoinServer::ChargeRandomFees() const
|
|||||||
void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const
|
void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
if (!ATMPIfSaneFee(m_chainstate, mempool, txref, false /* bypass_limits */)) {
|
if (!ATMPIfSaneFee(m_chainman, txref)) {
|
||||||
LogPrint(BCLog::COINJOIN, "%s -- AcceptToMemoryPool failed\n", __func__);
|
LogPrint(BCLog::COINJOIN, "%s -- ATMPIfSaneFee failed\n", __func__);
|
||||||
} else {
|
} else {
|
||||||
Assert(m_peerman)->RelayTransaction(txref->GetHash());
|
Assert(m_peerman)->RelayTransaction(txref->GetHash());
|
||||||
LogPrint(BCLog::COINJOIN, "%s -- Collateral was consumed\n", __func__);
|
LogPrint(BCLog::COINJOIN, "%s -- Collateral was consumed\n", __func__);
|
||||||
@ -582,7 +582,7 @@ bool CCoinJoinServer::AddEntry(const CCoinJoinEntry& entry, PoolMessage& nMessag
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CoinJoin::IsCollateralValid(m_chainstate, mempool, *entry.txCollateral)) {
|
if (!CoinJoin::IsCollateralValid(m_chainman, mempool, *entry.txCollateral)) {
|
||||||
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR: collateral not valid!\n", __func__);
|
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR: collateral not valid!\n", __func__);
|
||||||
nMessageIDRet = ERR_INVALID_COLLATERAL;
|
nMessageIDRet = ERR_INVALID_COLLATERAL;
|
||||||
return false;
|
return false;
|
||||||
@ -616,7 +616,7 @@ bool CCoinJoinServer::AddEntry(const CCoinJoinEntry& entry, PoolMessage& nMessag
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool fConsumeCollateral{false};
|
bool fConsumeCollateral{false};
|
||||||
if (!IsValidInOuts(m_chainstate, mempool, vin, entry.vecTxOut, nMessageIDRet, &fConsumeCollateral)) {
|
if (!IsValidInOuts(m_chainman.ActiveChainstate(), mempool, vin, entry.vecTxOut, nMessageIDRet, &fConsumeCollateral)) {
|
||||||
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR! IsValidInOuts() failed: %s\n", __func__, CoinJoin::GetMessageByID(nMessageIDRet).translated);
|
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- ERROR! IsValidInOuts() failed: %s\n", __func__, CoinJoin::GetMessageByID(nMessageIDRet).translated);
|
||||||
if (fConsumeCollateral) {
|
if (fConsumeCollateral) {
|
||||||
ConsumeCollateral(entry.txCollateral);
|
ConsumeCollateral(entry.txCollateral);
|
||||||
@ -693,7 +693,7 @@ bool CCoinJoinServer::IsAcceptableDSA(const CCoinJoinAccept& dsa, PoolMessage& n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check collateral
|
// check collateral
|
||||||
if (!fUnitTest && !CoinJoin::IsCollateralValid(m_chainstate, mempool, CTransaction(dsa.txCollateral))) {
|
if (!fUnitTest && !CoinJoin::IsCollateralValid(m_chainman, mempool, CTransaction(dsa.txCollateral))) {
|
||||||
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- collateral not valid!\n", __func__);
|
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- collateral not valid!\n", __func__);
|
||||||
nMessageIDRet = ERR_INVALID_COLLATERAL;
|
nMessageIDRet = ERR_INVALID_COLLATERAL;
|
||||||
return false;
|
return false;
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
#include <protocol.h>
|
#include <protocol.h>
|
||||||
|
|
||||||
class CActiveMasternodeManager;
|
class CActiveMasternodeManager;
|
||||||
class CChainState;
|
|
||||||
class CCoinJoinServer;
|
class CCoinJoinServer;
|
||||||
class CDataStream;
|
class CDataStream;
|
||||||
class CDeterministicMNManager;
|
class CDeterministicMNManager;
|
||||||
class CDSTXManager;
|
class CDSTXManager;
|
||||||
|
class ChainstateManager;
|
||||||
class CMasternodeMetaMan;
|
class CMasternodeMetaMan;
|
||||||
class CNode;
|
class CNode;
|
||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
@ -27,7 +27,7 @@ class UniValue;
|
|||||||
class CCoinJoinServer : public CCoinJoinBaseSession, public CCoinJoinBaseManager
|
class CCoinJoinServer : public CCoinJoinBaseSession, public CCoinJoinBaseManager
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
CChainState& m_chainstate;
|
ChainstateManager& m_chainman;
|
||||||
CConnman& connman;
|
CConnman& connman;
|
||||||
CDeterministicMNManager& m_dmnman;
|
CDeterministicMNManager& m_dmnman;
|
||||||
CDSTXManager& m_dstxman;
|
CDSTXManager& m_dstxman;
|
||||||
@ -90,11 +90,11 @@ private:
|
|||||||
void SetNull() override EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
|
void SetNull() override EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CCoinJoinServer(CChainState& chainstate, CConnman& _connman, CDeterministicMNManager& dmnman,
|
explicit CCoinJoinServer(ChainstateManager& chainman, CConnman& _connman, CDeterministicMNManager& dmnman,
|
||||||
CDSTXManager& dstxman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
|
CDSTXManager& dstxman, CMasternodeMetaMan& mn_metaman, CTxMemPool& mempool,
|
||||||
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
||||||
std::unique_ptr<PeerManager>& peerman) :
|
std::unique_ptr<PeerManager>& peerman) :
|
||||||
m_chainstate(chainstate),
|
m_chainman(chainman),
|
||||||
connman(_connman),
|
connman(_connman),
|
||||||
m_dmnman(dmnman),
|
m_dmnman(dmnman),
|
||||||
m_dstxman(dstxman),
|
m_dstxman(dstxman),
|
||||||
|
@ -31,6 +31,7 @@ enum class TxValidationResult {
|
|||||||
TX_CONFLICT,
|
TX_CONFLICT,
|
||||||
TX_CONFLICT_LOCK, //!< conflicts with InstantSend lock
|
TX_CONFLICT_LOCK, //!< conflicts with InstantSend lock
|
||||||
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/etc limits
|
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/etc limits
|
||||||
|
TX_NO_MEMPOOL, //!< this node does not have a mempool so can't validate the transaction
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A "reason" why a block was invalid, suitable for determining whether the
|
/** A "reason" why a block was invalid, suitable for determining whether the
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <protocol.h>
|
#include <protocol.h>
|
||||||
#include <shutdown.h>
|
#include <shutdown.h>
|
||||||
#include <spork.h>
|
#include <spork.h>
|
||||||
|
#include <timedata.h>
|
||||||
#include <util/ranges.h>
|
#include <util/ranges.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <masternode/sync.h>
|
#include <masternode/sync.h>
|
||||||
#include <messagesigner.h>
|
#include <messagesigner.h>
|
||||||
#include <net_processing.h>
|
#include <net_processing.h>
|
||||||
|
#include <timedata.h>
|
||||||
#include <util/string.h>
|
#include <util/string.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
@ -6,13 +6,12 @@
|
|||||||
#define BITCOIN_INDEX_BASE_H
|
#define BITCOIN_INDEX_BASE_H
|
||||||
|
|
||||||
#include <dbwrapper.h>
|
#include <dbwrapper.h>
|
||||||
#include <primitives/block.h>
|
|
||||||
#include <primitives/transaction.h>
|
|
||||||
#include <threadinterrupt.h>
|
#include <threadinterrupt.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
class CBlock;
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
class CChainState;
|
class CChainState;
|
||||||
|
|
||||||
|
@ -2,18 +2,14 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <index/disktxpos.h>
|
|
||||||
#include <index/txindex.h>
|
#include <index/txindex.h>
|
||||||
|
|
||||||
|
#include <index/disktxpos.h>
|
||||||
#include <node/blockstorage.h>
|
#include <node/blockstorage.h>
|
||||||
#include <node/ui_interface.h>
|
|
||||||
#include <shutdown.h>
|
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/translation.h>
|
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
|
||||||
constexpr uint8_t DB_BEST_BLOCK{'B'};
|
|
||||||
constexpr uint8_t DB_TXINDEX{'t'};
|
constexpr uint8_t DB_TXINDEX{'t'};
|
||||||
constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
|
|
||||||
|
|
||||||
std::unique_ptr<TxIndex> g_txindex;
|
std::unique_ptr<TxIndex> g_txindex;
|
||||||
|
|
||||||
@ -30,10 +26,6 @@ public:
|
|||||||
|
|
||||||
/// Write a batch of transaction positions to the DB.
|
/// Write a batch of transaction positions to the DB.
|
||||||
bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos);
|
bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos);
|
||||||
|
|
||||||
/// Migrate txindex data from the block tree DB, where it may be for older nodes that have not
|
|
||||||
/// been upgraded yet to the new database.
|
|
||||||
bool MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
|
TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
|
||||||
@ -54,163 +46,12 @@ bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_
|
|||||||
return WriteBatch(batch);
|
return WriteBatch(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Safely persist a transfer of data from the old txindex database to the new one, and compact the
|
|
||||||
* range of keys updated. This is used internally by MigrateData.
|
|
||||||
*/
|
|
||||||
static void WriteTxIndexMigrationBatches(CDBWrapper& newdb, CDBWrapper& olddb,
|
|
||||||
CDBBatch& batch_newdb, CDBBatch& batch_olddb,
|
|
||||||
const std::pair<uint8_t, uint256>& begin_key,
|
|
||||||
const std::pair<uint8_t, uint256>& end_key)
|
|
||||||
{
|
|
||||||
// Sync new DB changes to disk before deleting from old DB.
|
|
||||||
newdb.WriteBatch(batch_newdb, /*fSync=*/ true);
|
|
||||||
olddb.WriteBatch(batch_olddb);
|
|
||||||
olddb.CompactRange(begin_key, end_key);
|
|
||||||
|
|
||||||
batch_newdb.Clear();
|
|
||||||
batch_olddb.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator)
|
|
||||||
{
|
|
||||||
// The prior implementation of txindex was always in sync with block index
|
|
||||||
// and presence was indicated with a boolean DB flag. If the flag is set,
|
|
||||||
// this means the txindex from a previous version is valid and in sync with
|
|
||||||
// the chain tip. The first step of the migration is to unset the flag and
|
|
||||||
// write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the
|
|
||||||
// index entries are copied over in batches to the new database. Finally,
|
|
||||||
// DB_TXINDEX_BLOCK is erased from the old database and the block hash is
|
|
||||||
// written to the new database.
|
|
||||||
//
|
|
||||||
// Unsetting the boolean flag ensures that if the node is downgraded to a
|
|
||||||
// previous version, it will not see a corrupted, partially migrated index
|
|
||||||
// -- it will see that the txindex is disabled. When the node is upgraded
|
|
||||||
// again, the migration will pick up where it left off and sync to the block
|
|
||||||
// with hash DB_TXINDEX_BLOCK.
|
|
||||||
bool f_legacy_flag = false;
|
|
||||||
block_tree_db.ReadFlag("txindex", f_legacy_flag);
|
|
||||||
if (f_legacy_flag) {
|
|
||||||
if (!block_tree_db.Write(DB_TXINDEX_BLOCK, best_locator)) {
|
|
||||||
return error("%s: cannot write block indicator", __func__);
|
|
||||||
}
|
|
||||||
if (!block_tree_db.WriteFlag("txindex", false)) {
|
|
||||||
return error("%s: cannot write block index db flag", __func__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CBlockLocator locator;
|
|
||||||
if (!block_tree_db.Read(DB_TXINDEX_BLOCK, locator)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t count = 0;
|
|
||||||
LogPrintf("Upgrading txindex database... [0%%]\n");
|
|
||||||
uiInterface.ShowProgress(_("Upgrading txindex database").translated, 0, true);
|
|
||||||
int report_done = 0;
|
|
||||||
const size_t batch_size = 1 << 24; // 16 MiB
|
|
||||||
|
|
||||||
CDBBatch batch_newdb(*this);
|
|
||||||
CDBBatch batch_olddb(block_tree_db);
|
|
||||||
|
|
||||||
std::pair<uint8_t, uint256> key;
|
|
||||||
std::pair<uint8_t, uint256> begin_key{DB_TXINDEX, uint256()};
|
|
||||||
std::pair<uint8_t, uint256> prev_key = begin_key;
|
|
||||||
|
|
||||||
bool interrupted = false;
|
|
||||||
std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
|
|
||||||
for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) {
|
|
||||||
if (ShutdownRequested()) {
|
|
||||||
interrupted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cursor->GetKey(key)) {
|
|
||||||
return error("%s: cannot get key from valid cursor", __func__);
|
|
||||||
}
|
|
||||||
if (key.first != DB_TXINDEX) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log progress every 10%.
|
|
||||||
if (++count % 256 == 0) {
|
|
||||||
// Since txids are uniformly random and traversed in increasing order, the high 16 bits
|
|
||||||
// of the hash can be used to estimate the current progress.
|
|
||||||
const uint256& txid = key.second;
|
|
||||||
uint32_t high_nibble =
|
|
||||||
(static_cast<uint32_t>(*(txid.begin() + 0)) << 8) +
|
|
||||||
(static_cast<uint32_t>(*(txid.begin() + 1)) << 0);
|
|
||||||
int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5);
|
|
||||||
|
|
||||||
uiInterface.ShowProgress(_("Upgrading txindex database").translated, percentage_done, true);
|
|
||||||
if (report_done < percentage_done/10) {
|
|
||||||
LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done);
|
|
||||||
report_done = percentage_done/10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CDiskTxPos value;
|
|
||||||
if (!cursor->GetValue(value)) {
|
|
||||||
return error("%s: cannot parse txindex record", __func__);
|
|
||||||
}
|
|
||||||
batch_newdb.Write(key, value);
|
|
||||||
batch_olddb.Erase(key);
|
|
||||||
|
|
||||||
if (batch_newdb.SizeEstimate() > batch_size || batch_olddb.SizeEstimate() > batch_size) {
|
|
||||||
// NOTE: it's OK to delete the key pointed at by the current DB cursor while iterating
|
|
||||||
// because LevelDB iterators are guaranteed to provide a consistent view of the
|
|
||||||
// underlying data, like a lightweight snapshot.
|
|
||||||
WriteTxIndexMigrationBatches(*this, block_tree_db,
|
|
||||||
batch_newdb, batch_olddb,
|
|
||||||
prev_key, key);
|
|
||||||
prev_key = key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If these final DB batches complete the migration, write the best block
|
|
||||||
// hash marker to the new database and delete from the old one. This signals
|
|
||||||
// that the former is fully caught up to that point in the blockchain and
|
|
||||||
// that all txindex entries have been removed from the latter.
|
|
||||||
if (!interrupted) {
|
|
||||||
batch_olddb.Erase(DB_TXINDEX_BLOCK);
|
|
||||||
batch_newdb.Write(DB_BEST_BLOCK, locator);
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteTxIndexMigrationBatches(*this, block_tree_db,
|
|
||||||
batch_newdb, batch_olddb,
|
|
||||||
begin_key, key);
|
|
||||||
|
|
||||||
if (interrupted) {
|
|
||||||
LogPrintf("[CANCELLED].\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uiInterface.ShowProgress("", 100, false);
|
|
||||||
|
|
||||||
LogPrintf("[DONE].\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
|
TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
|
||||||
: m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
|
: m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
TxIndex::~TxIndex() {}
|
TxIndex::~TxIndex() {}
|
||||||
|
|
||||||
bool TxIndex::Init()
|
|
||||||
{
|
|
||||||
LOCK(cs_main);
|
|
||||||
|
|
||||||
// Attempt to migrate txindex from the old database to the new one. Even if
|
|
||||||
// chain_tip is null, the node could be reindexing and we still want to
|
|
||||||
// delete txindex records in the old database.
|
|
||||||
if (!m_db->MigrateData(*m_chainstate->m_blockman.m_block_tree_db, m_chainstate->m_chain.GetLocator())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BaseIndex::Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
||||||
{
|
{
|
||||||
// Exclude genesis block transaction because outputs are not spendable.
|
// Exclude genesis block transaction because outputs are not spendable.
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
#ifndef BITCOIN_INDEX_TXINDEX_H
|
#ifndef BITCOIN_INDEX_TXINDEX_H
|
||||||
#define BITCOIN_INDEX_TXINDEX_H
|
#define BITCOIN_INDEX_TXINDEX_H
|
||||||
|
|
||||||
#include <chain.h>
|
|
||||||
#include <index/base.h>
|
#include <index/base.h>
|
||||||
#include <txdb.h>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TxIndex is used to look up transactions included in the blockchain by hash.
|
* TxIndex is used to look up transactions included in the blockchain by hash.
|
||||||
@ -23,9 +21,6 @@ private:
|
|||||||
const std::unique_ptr<DB> m_db;
|
const std::unique_ptr<DB> m_db;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Override base class init to migrate from old database.
|
|
||||||
bool Init() override;
|
|
||||||
|
|
||||||
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
|
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
|
||||||
|
|
||||||
BaseIndex::DB& GetDB() const override;
|
BaseIndex::DB& GetDB() const override;
|
||||||
|
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("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection memory usage for the send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection memory usage for the send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target per 24h. Limit does not apply to peers with 'download' permission or blocks created within past week. 0 = no limit (default: %s). Optional suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 base while uppercase is 1024 base", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
@ -1440,6 +1440,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
{
|
{
|
||||||
const ArgsManager& args = *Assert(node.args);
|
const ArgsManager& args = *Assert(node.args);
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
|
|
||||||
|
auto opt_max_upload = ParseByteUnits(args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET), ByteUnit::M);
|
||||||
|
if (!opt_max_upload) {
|
||||||
|
return InitError(strprintf(_("Unable to parse -maxuploadtarget: '%s' (possible integer overflow?)"), args.GetArg("-maxuploadtarget", "")));
|
||||||
|
}
|
||||||
|
|
||||||
// ********************************************************* Step 4a: application initialization
|
// ********************************************************* Step 4a: application initialization
|
||||||
if (!CreatePidFile(args)) {
|
if (!CreatePidFile(args)) {
|
||||||
// Detailed error printed inside CreatePidFile().
|
// Detailed error printed inside CreatePidFile().
|
||||||
@ -1891,7 +1897,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
node.llmq_ctx->Stop();
|
node.llmq_ctx->Stop();
|
||||||
}
|
}
|
||||||
node.llmq_ctx.reset();
|
node.llmq_ctx.reset();
|
||||||
node.llmq_ctx = std::make_unique<LLMQContext>(chainman.ActiveChainstate(), *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman,
|
node.llmq_ctx = std::make_unique<LLMQContext>(chainman, *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman,
|
||||||
*node.mempool, node.mn_activeman.get(), *node.mn_sync, node.peerman, /* unit_tests = */ false, /* wipe = */ fReset || fReindexChainState);
|
*node.mempool, node.mn_activeman.get(), *node.mn_sync, node.peerman, /* unit_tests = */ false, /* wipe = */ fReset || fReindexChainState);
|
||||||
// Enable CMNHFManager::{Process, Undo}Block
|
// Enable CMNHFManager::{Process, Undo}Block
|
||||||
node.mnhf_manager->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get());
|
node.mnhf_manager->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get());
|
||||||
@ -2050,7 +2056,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
|
|
||||||
const CBlockIndex* tip = chainstate->m_chain.Tip();
|
const CBlockIndex* tip = chainstate->m_chain.Tip();
|
||||||
RPCNotifyBlockChange(tip);
|
RPCNotifyBlockChange(tip);
|
||||||
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
|
if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
|
||||||
strLoadError = _("The block database contains a block which appears to be from the future. "
|
strLoadError = _("The block database contains a block which appears to be from the future. "
|
||||||
"This may be due to your computer's date and time being set incorrectly. "
|
"This may be due to your computer's date and time being set incorrectly. "
|
||||||
"Only rebuild the block database if you are sure that your computer's date and time are correct");
|
"Only rebuild the block database if you are sure that your computer's date and time are correct");
|
||||||
@ -2153,7 +2159,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
|
|
||||||
// ********************************************************* Step 7c: Setup CoinJoin
|
// ********************************************************* Step 7c: Setup CoinJoin
|
||||||
|
|
||||||
node.cj_ctx = std::make_unique<CJContext>(chainman.ActiveChainstate(), *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool,
|
node.cj_ctx = std::make_unique<CJContext>(chainman, *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool,
|
||||||
node.mn_activeman.get(), *node.mn_sync, node.peerman, !ignores_incoming_txs);
|
node.mn_activeman.get(), *node.mn_sync, node.peerman, !ignores_incoming_txs);
|
||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
@ -2193,6 +2199,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
|
|
||||||
// ********************************************************* Step 8: start indexers
|
// ********************************************************* Step 8: start indexers
|
||||||
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
||||||
|
LOCK(::cs_main);
|
||||||
|
if (const auto error{CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db))}) {
|
||||||
|
return InitError(*error);
|
||||||
|
}
|
||||||
|
|
||||||
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
|
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
|
||||||
if (!g_txindex->Start(chainman.ActiveChainstate())) {
|
if (!g_txindex->Start(chainman.ActiveChainstate())) {
|
||||||
return false;
|
return false;
|
||||||
@ -2388,7 +2399,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
|
connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
|
||||||
connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
|
connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
|
||||||
connOptions.m_added_nodes = args.GetArgs("-addnode");
|
connOptions.m_added_nodes = args.GetArgs("-addnode");
|
||||||
connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
|
connOptions.nMaxOutboundLimit = *opt_max_upload;
|
||||||
connOptions.m_peer_connect_timeout = peer_connect_timeout;
|
connOptions.m_peer_connect_timeout = peer_connect_timeout;
|
||||||
|
|
||||||
// Port to bind to if `-bind=addr` is provided without a `:port` suffix.
|
// Port to bind to if `-bind=addr` is provided without a `:port` suffix.
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <llmq/context.h>
|
#include <llmq/context.h>
|
||||||
|
|
||||||
#include <dbwrapper.h>
|
#include <dbwrapper.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
#include <llmq/blockprocessor.h>
|
#include <llmq/blockprocessor.h>
|
||||||
#include <llmq/chainlocks.h>
|
#include <llmq/chainlocks.h>
|
||||||
@ -17,36 +18,41 @@
|
|||||||
#include <llmq/signing.h>
|
#include <llmq/signing.h>
|
||||||
#include <llmq/signing_shares.h>
|
#include <llmq/signing_shares.h>
|
||||||
|
|
||||||
LLMQContext::LLMQContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CEvoDB& evo_db,
|
LLMQContext::LLMQContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman,
|
||||||
CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman,
|
CEvoDB& evo_db, CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman,
|
||||||
CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman,
|
CTxMemPool& mempool, const CActiveMasternodeManager* const mn_activeman,
|
||||||
const CMasternodeSync& mn_sync, const std::unique_ptr<PeerManager>& peerman, bool unit_tests,
|
const CMasternodeSync& mn_sync, const std::unique_ptr<PeerManager>& peerman, bool unit_tests,
|
||||||
bool wipe) :
|
bool wipe) :
|
||||||
is_masternode{mn_activeman != nullptr},
|
is_masternode{mn_activeman != nullptr},
|
||||||
bls_worker{std::make_shared<CBLSWorker>()},
|
bls_worker{std::make_shared<CBLSWorker>()},
|
||||||
dkg_debugman{std::make_unique<llmq::CDKGDebugManager>()},
|
dkg_debugman{std::make_unique<llmq::CDKGDebugManager>()},
|
||||||
quorum_block_processor{std::make_unique<llmq::CQuorumBlockProcessor>(chainstate, dmnman, evo_db)},
|
quorum_block_processor{std::make_unique<llmq::CQuorumBlockProcessor>(chainman.ActiveChainstate(), dmnman, evo_db)},
|
||||||
qdkgsman{std::make_unique<llmq::CDKGSessionManager>(*bls_worker, chainstate, connman, dmnman, *dkg_debugman,
|
qdkgsman{std::make_unique<llmq::CDKGSessionManager>(*bls_worker, chainman.ActiveChainstate(), connman, dmnman,
|
||||||
mn_metaman, *quorum_block_processor, mn_activeman, sporkman,
|
*dkg_debugman, mn_metaman, *quorum_block_processor,
|
||||||
peerman, unit_tests, wipe)},
|
mn_activeman, sporkman, peerman, unit_tests, wipe)},
|
||||||
qman{std::make_unique<llmq::CQuorumManager>(*bls_worker, chainstate, connman, dmnman, *qdkgsman, evo_db,
|
qman{std::make_unique<llmq::CQuorumManager>(*bls_worker, chainman.ActiveChainstate(), connman, dmnman, *qdkgsman,
|
||||||
*quorum_block_processor, mn_activeman, mn_sync, sporkman, unit_tests,
|
evo_db, *quorum_block_processor, mn_activeman, mn_sync, sporkman,
|
||||||
wipe)},
|
unit_tests, wipe)},
|
||||||
sigman{std::make_unique<llmq::CSigningManager>(connman, mn_activeman, chainstate, *qman, peerman, unit_tests, wipe)},
|
sigman{std::make_unique<llmq::CSigningManager>(connman, mn_activeman, chainman.ActiveChainstate(), *qman, peerman,
|
||||||
|
unit_tests, wipe)},
|
||||||
shareman{std::make_unique<llmq::CSigSharesManager>(connman, *sigman, mn_activeman, *qman, sporkman, peerman)},
|
shareman{std::make_unique<llmq::CSigSharesManager>(connman, *sigman, mn_activeman, *qman, sporkman, peerman)},
|
||||||
clhandler{[&]() -> llmq::CChainLocksHandler* const {
|
clhandler{[&]() -> llmq::CChainLocksHandler* const {
|
||||||
assert(llmq::chainLocksHandler == nullptr);
|
assert(llmq::chainLocksHandler == nullptr);
|
||||||
llmq::chainLocksHandler = std::make_unique<llmq::CChainLocksHandler>(chainstate, *qman, *sigman, *shareman,
|
llmq::chainLocksHandler = std::make_unique<llmq::CChainLocksHandler>(chainman.ActiveChainstate(), *qman,
|
||||||
sporkman, mempool, mn_sync, is_masternode);
|
*sigman, *shareman, sporkman, mempool,
|
||||||
|
mn_sync, is_masternode);
|
||||||
return llmq::chainLocksHandler.get();
|
return llmq::chainLocksHandler.get();
|
||||||
}()},
|
}()},
|
||||||
isman{[&]() -> llmq::CInstantSendManager* const {
|
isman{[&]() -> llmq::CInstantSendManager* const {
|
||||||
assert(llmq::quorumInstantSendManager == nullptr);
|
assert(llmq::quorumInstantSendManager == nullptr);
|
||||||
llmq::quorumInstantSendManager = std::make_unique<llmq::CInstantSendManager>(*llmq::chainLocksHandler, chainstate, connman, *qman, *sigman, *shareman, sporkman, mempool, mn_sync, peerman, is_masternode, unit_tests, wipe);
|
llmq::quorumInstantSendManager = std::make_unique<llmq::CInstantSendManager>(*llmq::chainLocksHandler,
|
||||||
|
chainman.ActiveChainstate(),
|
||||||
|
connman, *qman, *sigman, *shareman,
|
||||||
|
sporkman, mempool, mn_sync, peerman,
|
||||||
|
is_masternode, unit_tests, wipe);
|
||||||
return llmq::quorumInstantSendManager.get();
|
return llmq::quorumInstantSendManager.get();
|
||||||
}()},
|
}()},
|
||||||
ehfSignalsHandler{
|
ehfSignalsHandler{std::make_unique<llmq::CEHFSignalsHandler>(chainman, mnhfman, *sigman, *shareman, *qman)}
|
||||||
std::make_unique<llmq::CEHFSignalsHandler>(chainstate, mnhfman, *sigman, *shareman, mempool, *qman)}
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
class CActiveMasternodeManager;
|
class CActiveMasternodeManager;
|
||||||
class CBLSWorker;
|
class CBLSWorker;
|
||||||
class CChainState;
|
|
||||||
class CConnman;
|
class CConnman;
|
||||||
|
class ChainstateManager;
|
||||||
class CDeterministicMNManager;
|
class CDeterministicMNManager;
|
||||||
class CDBWrapper;
|
class CDBWrapper;
|
||||||
class CEvoDB;
|
class CEvoDB;
|
||||||
@ -40,7 +40,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
LLMQContext() = delete;
|
LLMQContext() = delete;
|
||||||
LLMQContext(const LLMQContext&) = delete;
|
LLMQContext(const LLMQContext&) = delete;
|
||||||
LLMQContext(CChainState& chainstate, CConnman& connman, CDeterministicMNManager& dmnman, CEvoDB& evo_db,
|
LLMQContext(ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman, CEvoDB& evo_db,
|
||||||
CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman, CTxMemPool& mempool,
|
CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman, CTxMemPool& mempool,
|
||||||
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
|
||||||
const std::unique_ptr<PeerManager>& peerman, bool unit_tests, bool wipe);
|
const std::unique_ptr<PeerManager>& peerman, bool unit_tests, bool wipe);
|
||||||
|
@ -15,19 +15,17 @@
|
|||||||
#include <deploymentstatus.h>
|
#include <deploymentstatus.h>
|
||||||
#include <index/txindex.h> // g_txindex
|
#include <index/txindex.h> // g_txindex
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <txmempool.h>
|
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
|
||||||
namespace llmq {
|
namespace llmq {
|
||||||
|
|
||||||
|
|
||||||
CEHFSignalsHandler::CEHFSignalsHandler(CChainState& chainstate, CMNHFManager& mnhfman, CSigningManager& sigman,
|
CEHFSignalsHandler::CEHFSignalsHandler(ChainstateManager& chainman, CMNHFManager& mnhfman, CSigningManager& sigman,
|
||||||
CSigSharesManager& shareman, CTxMemPool& mempool, const CQuorumManager& qman) :
|
CSigSharesManager& shareman, const CQuorumManager& qman) :
|
||||||
chainstate(chainstate),
|
m_chainman(chainman),
|
||||||
mnhfman(mnhfman),
|
mnhfman(mnhfman),
|
||||||
sigman(sigman),
|
sigman(sigman),
|
||||||
shareman(shareman),
|
shareman(shareman),
|
||||||
mempool(mempool),
|
|
||||||
qman(qman)
|
qman(qman)
|
||||||
{
|
{
|
||||||
sigman.RegisterRecoveredSigsListener(this);
|
sigman.RegisterRecoveredSigsListener(this);
|
||||||
@ -77,7 +75,7 @@ void CEHFSignalsHandler::trySignEHFSignal(int bit, const CBlockIndex* const pind
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto quorum = llmq::SelectQuorumForSigning(llmq_params_opt.value(), chainstate.m_chain, qman, requestId);
|
const auto quorum = llmq::SelectQuorumForSigning(llmq_params_opt.value(), m_chainman.ActiveChain(), qman, requestId);
|
||||||
if (!quorum) {
|
if (!quorum) {
|
||||||
LogPrintf("CEHFSignalsHandler::trySignEHFSignal no quorum for id=%s\n", requestId.ToString());
|
LogPrintf("CEHFSignalsHandler::trySignEHFSignal no quorum for id=%s\n", requestId.ToString());
|
||||||
return;
|
return;
|
||||||
@ -103,7 +101,7 @@ MessageProcessingResult CEHFSignalsHandler::HandleNewRecoveredSig(const CRecover
|
|||||||
}
|
}
|
||||||
|
|
||||||
MessageProcessingResult ret;
|
MessageProcessingResult ret;
|
||||||
const auto ehfSignals = mnhfman.GetSignalsStage(WITH_LOCK(cs_main, return chainstate.m_chain.Tip()));
|
const auto ehfSignals = mnhfman.GetSignalsStage(WITH_LOCK(cs_main, return m_chainman.ActiveTip()));
|
||||||
MNHFTxPayload mnhfPayload;
|
MNHFTxPayload mnhfPayload;
|
||||||
for (const auto& deployment : Params().GetConsensus().vDeployments) {
|
for (const auto& deployment : Params().GetConsensus().vDeployments) {
|
||||||
// skip deployments that do not use dip0023 or that have already been mined
|
// skip deployments that do not use dip0023 or that have already been mined
|
||||||
@ -126,7 +124,7 @@ MessageProcessingResult CEHFSignalsHandler::HandleNewRecoveredSig(const CRecover
|
|||||||
CTransactionRef tx_to_sent = MakeTransactionRef(std::move(tx));
|
CTransactionRef tx_to_sent = MakeTransactionRef(std::move(tx));
|
||||||
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig Special EHF TX is created hash=%s\n", tx_to_sent->GetHash().ToString());
|
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig Special EHF TX is created hash=%s\n", tx_to_sent->GetHash().ToString());
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(chainstate, mempool, tx_to_sent, /* bypass_limits */ false);
|
const MempoolAcceptResult result = m_chainman.ProcessTransaction(tx_to_sent);
|
||||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||||
ret.m_transactions.push_back(tx_to_sent->GetHash());
|
ret.m_transactions.push_back(tx_to_sent->GetHash());
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,9 +10,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
class CChainState;
|
class ChainstateManager;
|
||||||
class CMNHFManager;
|
class CMNHFManager;
|
||||||
class CTxMemPool;
|
|
||||||
|
|
||||||
namespace llmq
|
namespace llmq
|
||||||
{
|
{
|
||||||
@ -23,11 +22,10 @@ class CSigningManager;
|
|||||||
class CEHFSignalsHandler : public CRecoveredSigsListener
|
class CEHFSignalsHandler : public CRecoveredSigsListener
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
CChainState& chainstate;
|
ChainstateManager& m_chainman;
|
||||||
CMNHFManager& mnhfman;
|
CMNHFManager& mnhfman;
|
||||||
CSigningManager& sigman;
|
CSigningManager& sigman;
|
||||||
CSigSharesManager& shareman;
|
CSigSharesManager& shareman;
|
||||||
CTxMemPool& mempool;
|
|
||||||
const CQuorumManager& qman;
|
const CQuorumManager& qman;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,8 +34,8 @@ private:
|
|||||||
mutable Mutex cs;
|
mutable Mutex cs;
|
||||||
std::set<uint256> ids GUARDED_BY(cs);
|
std::set<uint256> ids GUARDED_BY(cs);
|
||||||
public:
|
public:
|
||||||
explicit CEHFSignalsHandler(CChainState& chainstate, CMNHFManager& mnhfman, CSigningManager& sigman,
|
explicit CEHFSignalsHandler(ChainstateManager& chainman, CMNHFManager& mnhfman, CSigningManager& sigman,
|
||||||
CSigSharesManager& shareman, CTxMemPool& mempool, const CQuorumManager& qman);
|
CSigSharesManager& shareman, const CQuorumManager& qman);
|
||||||
|
|
||||||
~CEHFSignalsHandler();
|
~CEHFSignalsHandler();
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <netmessagemaker.h>
|
#include <netmessagemaker.h>
|
||||||
|
|
||||||
#include <addrdb.h>
|
#include <addrdb.h>
|
||||||
|
#include <addrman.h>
|
||||||
#include <banman.h>
|
#include <banman.h>
|
||||||
#include <clientversion.h>
|
#include <clientversion.h>
|
||||||
#include <compat.h>
|
#include <compat.h>
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#ifndef BITCOIN_NET_H
|
#ifndef BITCOIN_NET_H
|
||||||
#define BITCOIN_NET_H
|
#define BITCOIN_NET_H
|
||||||
|
|
||||||
#include <addrman.h>
|
|
||||||
#include <bip324.h>
|
#include <bip324.h>
|
||||||
#include <bloom.h>
|
#include <bloom.h>
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
@ -50,14 +49,15 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class AddrMan;
|
||||||
|
class BanMan;
|
||||||
class CConnman;
|
class CConnman;
|
||||||
class CDeterministicMNList;
|
class CDeterministicMNList;
|
||||||
class CDeterministicMNManager;
|
class CDeterministicMNManager;
|
||||||
class CMasternodeMetaMan;
|
class CMasternodeMetaMan;
|
||||||
class CMasternodeSync;
|
class CMasternodeSync;
|
||||||
class CScheduler;
|
|
||||||
class CNode;
|
class CNode;
|
||||||
class BanMan;
|
class CScheduler;
|
||||||
struct bilingual_str;
|
struct bilingual_str;
|
||||||
|
|
||||||
/** Default for -whitelistrelay. */
|
/** Default for -whitelistrelay. */
|
||||||
@ -100,7 +100,7 @@ static const bool DEFAULT_LISTEN = true;
|
|||||||
*/
|
*/
|
||||||
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
|
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
|
||||||
/** The default for -maxuploadtarget. 0 = Unlimited */
|
/** The default for -maxuploadtarget. 0 = Unlimited */
|
||||||
static constexpr uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
|
static const std::string DEFAULT_MAX_UPLOAD_TARGET{"0M"};
|
||||||
/** Default for blocks only*/
|
/** Default for blocks only*/
|
||||||
static const bool DEFAULT_BLOCKSONLY = false;
|
static const bool DEFAULT_BLOCKSONLY = false;
|
||||||
/** -peertimeout default */
|
/** -peertimeout default */
|
||||||
|
@ -856,9 +856,9 @@ private:
|
|||||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter for transactions that were recently rejected by
|
* Filter for transactions that were recently rejected by the mempool.
|
||||||
* AcceptToMemoryPool. These are not rerequested until the chain tip
|
* These are not rerequested until the chain tip changes, at which point
|
||||||
* changes, at which point the entire filter is reset.
|
* the entire filter is reset.
|
||||||
*
|
*
|
||||||
* Without this filter we'd be re-requesting txs from each of our peers,
|
* Without this filter we'd be re-requesting txs from each of our peers,
|
||||||
* increasing bandwidth consumption considerably. For instance, with 100
|
* increasing bandwidth consumption considerably. For instance, with 100
|
||||||
@ -1816,6 +1816,7 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat
|
|||||||
case TxValidationResult::TX_PREMATURE_SPEND:
|
case TxValidationResult::TX_PREMATURE_SPEND:
|
||||||
case TxValidationResult::TX_CONFLICT:
|
case TxValidationResult::TX_CONFLICT:
|
||||||
case TxValidationResult::TX_MEMPOOL_POLICY:
|
case TxValidationResult::TX_MEMPOOL_POLICY:
|
||||||
|
case TxValidationResult::TX_NO_MEMPOOL:
|
||||||
// moved from BLOCK
|
// moved from BLOCK
|
||||||
case TxValidationResult::TX_BAD_SPECIAL:
|
case TxValidationResult::TX_BAD_SPECIAL:
|
||||||
case TxValidationResult::TX_CONFLICT_LOCK:
|
case TxValidationResult::TX_CONFLICT_LOCK:
|
||||||
@ -3039,7 +3040,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
|||||||
const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
|
const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
|
||||||
if (porphanTx == nullptr) continue;
|
if (porphanTx == nullptr) continue;
|
||||||
|
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, porphanTx, false /* bypass_limits */);
|
const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
|
||||||
const TxValidationState& state = result.m_state;
|
const TxValidationState& state = result.m_state;
|
||||||
|
|
||||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||||
@ -3065,7 +3066,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_mempool.check(m_chainman.ActiveChainstate());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer,
|
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer,
|
||||||
@ -4223,7 +4223,7 @@ void PeerManagerImpl::ProcessMessage(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */);
|
const MempoolAcceptResult result = m_chainman.ProcessTransaction(ptx);
|
||||||
const TxValidationState& state = result.m_state;
|
const TxValidationState& state = result.m_state;
|
||||||
|
|
||||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||||
@ -4234,7 +4234,6 @@ void PeerManagerImpl::ProcessMessage(
|
|||||||
m_cj_ctx->dstxman->AddDSTX(dstx);
|
m_cj_ctx->dstxman->AddDSTX(dstx);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mempool.check(m_chainman.ActiveChainstate());
|
|
||||||
RelayTransaction(tx.GetHash());
|
RelayTransaction(tx.GetHash());
|
||||||
m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
|
m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
|
||||||
|
|
||||||
@ -4306,8 +4305,8 @@ void PeerManagerImpl::ProcessMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If a tx has been detected by m_recent_rejects, we will have reached
|
// If a tx has been detected by m_recent_rejects, we will have reached
|
||||||
// this point and the tx will have been ignored. Because we haven't run
|
// this point and the tx will have been ignored. Because we haven't
|
||||||
// the tx through AcceptToMemoryPool, we won't have computed a DoS
|
// submitted the tx to our mempool, we won't have computed a DoS
|
||||||
// score for it or determined exactly why we consider it invalid.
|
// score for it or determined exactly why we consider it invalid.
|
||||||
//
|
//
|
||||||
// This means we won't penalize any peer subsequently relaying a DoSy
|
// This means we won't penalize any peer subsequently relaying a DoSy
|
||||||
|
@ -65,8 +65,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|||||||
if (max_tx_fee > 0) {
|
if (max_tx_fee > 0) {
|
||||||
// First, call ATMP with test_accept and check the fee. If ATMP
|
// First, call ATMP with test_accept and check the fee. If ATMP
|
||||||
// fails here, return error immediately.
|
// fails here, return error immediately.
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx,
|
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/true, bypass_limits);
|
||||||
bypass_limits, true /* test_accept */);
|
|
||||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||||
return HandleATMPError(result.m_state, err_string.original);
|
return HandleATMPError(result.m_state, err_string.original);
|
||||||
} else if (result.m_base_fees.value() > max_tx_fee) {
|
} else if (result.m_base_fees.value() > max_tx_fee) {
|
||||||
@ -74,8 +73,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Try to submit the transaction to the mempool.
|
// Try to submit the transaction to the mempool.
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx,
|
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/false, bypass_limits);
|
||||||
bypass_limits, false /* test_accept */);
|
|
||||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||||
return HandleATMPError(result.m_state, err_string.original);
|
return HandleATMPError(result.m_state, err_string.original);
|
||||||
}
|
}
|
||||||
|
@ -134,10 +134,10 @@ static RPCHelpMan coinjoin_start()
|
|||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already.");
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChainstateManager& chainman = EnsureChainman(node);
|
ChainstateManager& chainman = EnsureChainman(node);
|
||||||
CTxMemPool& mempool = EnsureMemPool(node);
|
CTxMemPool& mempool = EnsureMemPool(node);
|
||||||
CConnman& connman = EnsureConnman(node);
|
CConnman& connman = EnsureConnman(node);
|
||||||
bool result = cj_clientman->DoAutomaticDenominating(chainman.ActiveChainstate(), connman, mempool);
|
bool result = cj_clientman->DoAutomaticDenominating(chainman, connman, mempool);
|
||||||
return "Mixing " + (result ? "started successfully" : ("start failed: " + cj_clientman->GetStatuses().original + ", will retry"));
|
return "Mixing " + (result ? "started successfully" : ("start failed: " + cj_clientman->GetStatuses().original + ", will retry"));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <core_io.h>
|
#include <core_io.h>
|
||||||
#include <evo/deterministicmns.h>
|
#include <evo/deterministicmns.h>
|
||||||
#include <governance/classes.h>
|
#include <governance/classes.h>
|
||||||
|
#include <governance/common.h>
|
||||||
#include <governance/governance.h>
|
#include <governance/governance.h>
|
||||||
#include <governance/validators.h>
|
#include <governance/validators.h>
|
||||||
#include <governance/vote.h>
|
#include <governance/vote.h>
|
||||||
@ -13,13 +14,13 @@
|
|||||||
#include <masternode/node.h>
|
#include <masternode/node.h>
|
||||||
#include <masternode/sync.h>
|
#include <masternode/sync.h>
|
||||||
#include <messagesigner.h>
|
#include <messagesigner.h>
|
||||||
#include <node/context.h>
|
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
#include <node/context.h>
|
||||||
#include <rpc/blockchain.h>
|
#include <rpc/blockchain.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <rpc/server_util.h>
|
#include <rpc/server_util.h>
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <governance/common.h>
|
#include <timedata.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
|
|
||||||
|
#include <addrman.h>
|
||||||
#include <banman.h>
|
#include <banman.h>
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
#include <clientversion.h>
|
#include <clientversion.h>
|
||||||
|
@ -1246,12 +1246,13 @@ static RPCHelpMan testmempoolaccept()
|
|||||||
|
|
||||||
NodeContext& node = EnsureAnyNodeContext(request.context);
|
NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||||
CTxMemPool& mempool = EnsureMemPool(node);
|
CTxMemPool& mempool = EnsureMemPool(node);
|
||||||
CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
|
ChainstateManager& chainman = EnsureChainman(node);
|
||||||
|
CChainState& chainstate = chainman.ActiveChainstate();
|
||||||
const PackageMempoolAcceptResult package_result = [&] {
|
const PackageMempoolAcceptResult package_result = [&] {
|
||||||
LOCK(::cs_main);
|
LOCK(::cs_main);
|
||||||
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
|
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
|
||||||
return PackageMempoolAcceptResult(txns[0]->GetHash(),
|
return PackageMempoolAcceptResult(txns[0]->GetHash(),
|
||||||
AcceptToMemoryPool(chainstate, mempool, txns[0], /* bypass_limits */ false, /* test_accept*/ true));
|
chainman.ProcessTransaction(txns[0], /*test_accept=*/ true));
|
||||||
}();
|
}();
|
||||||
|
|
||||||
UniValue rpc_result(UniValue::VARR);
|
UniValue rpc_result(UniValue::VARR);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <addrman.h>
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
#include <chainparamsbase.h>
|
#include <chainparamsbase.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <addrman.h>
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
#include <chainparamsbase.h>
|
#include <chainparamsbase.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
@ -80,7 +80,7 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
|
|||||||
|
|
||||||
void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, const NodeContext& node, CChainState& chainstate)
|
void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, const NodeContext& node, CChainState& chainstate)
|
||||||
{
|
{
|
||||||
WITH_LOCK(::cs_main, tx_pool.check(chainstate));
|
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
|
||||||
{
|
{
|
||||||
BlockAssembler::Options options;
|
BlockAssembler::Options options;
|
||||||
options.nBlockMaxSize = fuzzed_data_provider.ConsumeIntegralInRange(0U, MaxBlockSize(true));
|
options.nBlockMaxSize = fuzzed_data_provider.ConsumeIntegralInRange(0U, MaxBlockSize(true));
|
||||||
@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, con
|
|||||||
std::vector<uint256> all_txids;
|
std::vector<uint256> all_txids;
|
||||||
tx_pool.queryHashes(all_txids);
|
tx_pool.queryHashes(all_txids);
|
||||||
assert(all_txids.size() < info_all.size());
|
assert(all_txids.size() < info_all.size());
|
||||||
WITH_LOCK(::cs_main, tx_pool.check(chainstate));
|
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
|
||||||
}
|
}
|
||||||
SyncWithValidationInterfaceQueue();
|
SyncWithValidationInterfaceQueue();
|
||||||
}
|
}
|
||||||
|
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);
|
LOCK(cs_main);
|
||||||
|
|
||||||
unsigned int initialPoolSize = m_node.mempool->size();
|
unsigned int initialPoolSize = m_node.mempool->size();
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx), true /* bypass_limits */);
|
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
|
||||||
|
|
||||||
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
|
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
|
|||||||
const auto ToMemPool = [this](const CMutableTransaction& tx) {
|
const auto ToMemPool = [this](const CMutableTransaction& tx) {
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx), true /* bypass_limits */);
|
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(tx));
|
||||||
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,12 +110,12 @@ void DashTestSetup(NodeContext& node, const CChainParams& chainparams)
|
|||||||
node.dmnman = std::make_unique<CDeterministicMNManager>(chainstate, *node.connman, *node.evodb);
|
node.dmnman = std::make_unique<CDeterministicMNManager>(chainstate, *node.connman, *node.evodb);
|
||||||
node.mempool->ConnectManagers(node.dmnman.get());
|
node.mempool->ConnectManagers(node.dmnman.get());
|
||||||
|
|
||||||
node.cj_ctx = std::make_unique<CJContext>(chainstate, *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool,
|
node.cj_ctx = std::make_unique<CJContext>(*node.chainman, *node.connman, *node.dmnman, *node.mn_metaman, *node.mempool,
|
||||||
/* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* relay_txes = */ true);
|
/* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* relay_txes = */ true);
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
node.coinjoin_loader = interfaces::MakeCoinJoinLoader(*node.cj_ctx->walletman);
|
node.coinjoin_loader = interfaces::MakeCoinJoinLoader(*node.cj_ctx->walletman);
|
||||||
#endif // ENABLE_WALLET
|
#endif // ENABLE_WALLET
|
||||||
node.llmq_ctx = std::make_unique<LLMQContext>(chainstate, *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman, *node.mempool,
|
node.llmq_ctx = std::make_unique<LLMQContext>(*node.chainman, *node.connman, *node.dmnman, *node.evodb, *node.mn_metaman, *node.mnhf_manager, *node.sporkman, *node.mempool,
|
||||||
/* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* unit_tests = */ true, /* wipe = */ false);
|
/* mn_activeman = */ nullptr, *node.mn_sync, node.peerman, /* unit_tests = */ true, /* wipe = */ false);
|
||||||
Assert(node.mnhf_manager)->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get());
|
Assert(node.mnhf_manager)->ConnectManagers(node.chainman.get(), node.llmq_ctx->qman.get());
|
||||||
node.chain_helper = std::make_unique<CChainstateHelper>(*node.cpoolman, *node.dmnman, *node.mnhf_manager, *node.govman, *(node.llmq_ctx->quorum_block_processor), *node.chainman,
|
node.chain_helper = std::make_unique<CChainstateHelper>(*node.cpoolman, *node.dmnman, *node.mnhf_manager, *node.govman, *(node.llmq_ctx->quorum_block_processor), *node.chainman,
|
||||||
@ -519,7 +519,7 @@ CMutableTransaction TestChainSetup::CreateValidMempoolTransaction(CTransactionRe
|
|||||||
// If submit=true, add transaction to the mempool.
|
// If submit=true, add transaction to the mempool.
|
||||||
if (submit) {
|
if (submit) {
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
|
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(mempool_txn));
|
||||||
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1318,6 +1318,11 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney)
|
|||||||
BOOST_AUTO_TEST_CASE(util_ParseMoney)
|
BOOST_AUTO_TEST_CASE(util_ParseMoney)
|
||||||
{
|
{
|
||||||
BOOST_CHECK_EQUAL(ParseMoney("0.0").value(), 0);
|
BOOST_CHECK_EQUAL(ParseMoney("0.0").value(), 0);
|
||||||
|
BOOST_CHECK_EQUAL(ParseMoney(".").value(), 0);
|
||||||
|
BOOST_CHECK_EQUAL(ParseMoney("0.").value(), 0);
|
||||||
|
BOOST_CHECK_EQUAL(ParseMoney(".0").value(), 0);
|
||||||
|
BOOST_CHECK_EQUAL(ParseMoney(".6789").value(), 6789'0000);
|
||||||
|
BOOST_CHECK_EQUAL(ParseMoney("12345.").value(), COIN * 12345);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(ParseMoney("12345.6789").value(), (COIN/10000)*123456789);
|
BOOST_CHECK_EQUAL(ParseMoney("12345.6789").value(), (COIN/10000)*123456789);
|
||||||
|
|
||||||
@ -1355,11 +1360,18 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney)
|
|||||||
BOOST_CHECK(!ParseMoney(" "));
|
BOOST_CHECK(!ParseMoney(" "));
|
||||||
|
|
||||||
// Parsing two numbers should fail
|
// Parsing two numbers should fail
|
||||||
|
BOOST_CHECK(!ParseMoney(".."));
|
||||||
|
BOOST_CHECK(!ParseMoney("0..0"));
|
||||||
BOOST_CHECK(!ParseMoney("1 2"));
|
BOOST_CHECK(!ParseMoney("1 2"));
|
||||||
BOOST_CHECK(!ParseMoney(" 1 2 "));
|
BOOST_CHECK(!ParseMoney(" 1 2 "));
|
||||||
BOOST_CHECK(!ParseMoney(" 1.2 3 "));
|
BOOST_CHECK(!ParseMoney(" 1.2 3 "));
|
||||||
BOOST_CHECK(!ParseMoney(" 1 2.3 "));
|
BOOST_CHECK(!ParseMoney(" 1 2.3 "));
|
||||||
|
|
||||||
|
// Embedded whitespace should fail
|
||||||
|
BOOST_CHECK(!ParseMoney(" -1 .2 "));
|
||||||
|
BOOST_CHECK(!ParseMoney(" 1 .2 "));
|
||||||
|
BOOST_CHECK(!ParseMoney(" +1 .2 "));
|
||||||
|
|
||||||
// Attempted 63 bit overflow should fail
|
// Attempted 63 bit overflow should fail
|
||||||
BOOST_CHECK(!ParseMoney("92233720368.54775808"));
|
BOOST_CHECK(!ParseMoney("92233720368.54775808"));
|
||||||
|
|
||||||
@ -2673,4 +2685,52 @@ BOOST_AUTO_TEST_CASE(remove_prefix)
|
|||||||
BOOST_CHECK_EQUAL(RemovePrefix("", ""), "");
|
BOOST_CHECK_EQUAL(RemovePrefix("", ""), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(util_ParseByteUnits)
|
||||||
|
{
|
||||||
|
auto noop = ByteUnit::NOOP;
|
||||||
|
|
||||||
|
// no multiplier
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("1", noop).value(), 1);
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("0", noop).value(), 0);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("1k", noop).value(), 1000ULL);
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("1K", noop).value(), 1ULL << 10);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("2m", noop).value(), 2'000'000ULL);
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("2M", noop).value(), 2ULL << 20);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("3g", noop).value(), 3'000'000'000ULL);
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("3G", noop).value(), 3ULL << 30);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("4t", noop).value(), 4'000'000'000'000ULL);
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("4T", noop).value(), 4ULL << 40);
|
||||||
|
|
||||||
|
// check default multiplier
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("5", ByteUnit::K).value(), 5ULL << 10);
|
||||||
|
|
||||||
|
// NaN
|
||||||
|
BOOST_CHECK(!ParseByteUnits("", noop));
|
||||||
|
BOOST_CHECK(!ParseByteUnits("foo", noop));
|
||||||
|
|
||||||
|
// whitespace
|
||||||
|
BOOST_CHECK(!ParseByteUnits("123m ", noop));
|
||||||
|
BOOST_CHECK(!ParseByteUnits(" 123m", noop));
|
||||||
|
|
||||||
|
// no +-
|
||||||
|
BOOST_CHECK(!ParseByteUnits("-123m", noop));
|
||||||
|
BOOST_CHECK(!ParseByteUnits("+123m", noop));
|
||||||
|
|
||||||
|
// zero padding
|
||||||
|
BOOST_CHECK_EQUAL(ParseByteUnits("020M", noop).value(), 20ULL << 20);
|
||||||
|
|
||||||
|
// fractions not allowed
|
||||||
|
BOOST_CHECK(!ParseByteUnits("0.5T", noop));
|
||||||
|
|
||||||
|
// overflow
|
||||||
|
BOOST_CHECK(!ParseByteUnits("18446744073709551615g", noop));
|
||||||
|
|
||||||
|
// invalid unit
|
||||||
|
BOOST_CHECK(!ParseByteUnits("1x", noop));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
|
|||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
for (const auto& tx : txs) {
|
for (const auto& tx : txs) {
|
||||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */);
|
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(tx);
|
||||||
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/txdb.cpp
23
src/txdb.cpp
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <txdb.h>
|
#include <txdb.h>
|
||||||
|
|
||||||
|
#include <chain.h>
|
||||||
#include <node/ui_interface.h>
|
#include <node/ui_interface.h>
|
||||||
#include <pow.h>
|
#include <pow.h>
|
||||||
#include <random.h>
|
#include <random.h>
|
||||||
@ -31,6 +32,28 @@ static constexpr uint8_t DB_FLAG{'F'};
|
|||||||
static constexpr uint8_t DB_REINDEX_FLAG{'R'};
|
static constexpr uint8_t DB_REINDEX_FLAG{'R'};
|
||||||
static constexpr uint8_t DB_LAST_BLOCK{'l'};
|
static constexpr uint8_t DB_LAST_BLOCK{'l'};
|
||||||
|
|
||||||
|
// Keys used in previous version that might still be found in the DB:
|
||||||
|
static constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
|
||||||
|
// uint8_t DB_TXINDEX{'t'}
|
||||||
|
|
||||||
|
std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db)
|
||||||
|
{
|
||||||
|
CBlockLocator ignored{};
|
||||||
|
if (block_tree_db.Read(DB_TXINDEX_BLOCK, ignored)) {
|
||||||
|
return _("The -txindex upgrade started by a previous version can not be completed. Restart with the previous version or run a full -reindex.");
|
||||||
|
}
|
||||||
|
bool txindex_legacy_flag{false};
|
||||||
|
block_tree_db.ReadFlag("txindex", txindex_legacy_flag);
|
||||||
|
if (txindex_legacy_flag) {
|
||||||
|
// Disable legacy txindex and warn once about occupied disk space
|
||||||
|
if (!block_tree_db.WriteFlag("txindex", false)) {
|
||||||
|
return Untranslated("Failed to write block index db flag 'txindex'='0'");
|
||||||
|
}
|
||||||
|
return _("The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.");
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct CoinEntry {
|
struct CoinEntry {
|
||||||
|
11
src/txdb.h
11
src/txdb.h
@ -8,19 +8,22 @@
|
|||||||
|
|
||||||
#include <coins.h>
|
#include <coins.h>
|
||||||
#include <dbwrapper.h>
|
#include <dbwrapper.h>
|
||||||
#include <chain.h>
|
|
||||||
#include <primitives/block.h>
|
|
||||||
#include <spentindex.h>
|
#include <spentindex.h>
|
||||||
#include <timestampindex.h>
|
#include <timestampindex.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class CBlockFileInfo;
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
class CCoinsViewDBCursor;
|
|
||||||
class uint256;
|
class uint256;
|
||||||
|
namespace Consensus {
|
||||||
|
struct Params;
|
||||||
|
};
|
||||||
|
struct bilingual_str;
|
||||||
|
|
||||||
//! -dbcache default (MiB)
|
//! -dbcache default (MiB)
|
||||||
static const int64_t nDefaultDbCache = 300;
|
static const int64_t nDefaultDbCache = 300;
|
||||||
@ -108,4 +111,6 @@ public:
|
|||||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db);
|
||||||
|
|
||||||
#endif // BITCOIN_TXDB_H
|
#endif // BITCOIN_TXDB_H
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
|
||||||
|
#include <chain.h>
|
||||||
|
#include <coins.h>
|
||||||
#include <consensus/consensus.h>
|
#include <consensus/consensus.h>
|
||||||
#include <consensus/tx_verify.h>
|
#include <consensus/tx_verify.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
@ -17,7 +19,6 @@
|
|||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
#include <validation.h>
|
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
|
|
||||||
#include <evo/specialtx.h>
|
#include <evo/specialtx.h>
|
||||||
@ -29,6 +30,65 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
|
||||||
|
struct update_descendant_state
|
||||||
|
{
|
||||||
|
update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
|
||||||
|
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator() (CTxMemPoolEntry &e)
|
||||||
|
{ e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int64_t modifySize;
|
||||||
|
CAmount modifyFee;
|
||||||
|
int64_t modifyCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct update_ancestor_state
|
||||||
|
{
|
||||||
|
update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
|
||||||
|
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator() (CTxMemPoolEntry &e)
|
||||||
|
{ e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int64_t modifySize;
|
||||||
|
CAmount modifyFee;
|
||||||
|
int64_t modifyCount;
|
||||||
|
int64_t modifySigOpsCost;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct update_fee_delta
|
||||||
|
{
|
||||||
|
explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
|
||||||
|
|
||||||
|
void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int64_t feeDelta;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
|
||||||
|
{
|
||||||
|
AssertLockHeld(cs_main);
|
||||||
|
// If there are relative lock times then the maxInputBlock will be set
|
||||||
|
// If there are no relative lock times, the LockPoints don't depend on the chain
|
||||||
|
if (lp.maxInputBlock) {
|
||||||
|
// Check whether active_chain is an extension of the block at which the LockPoints
|
||||||
|
// calculation was valid. If not LockPoints are no longer valid
|
||||||
|
if (!active_chain.Contains(lp.maxInputBlock)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LockPoints still valid
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
|
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
|
||||||
int64_t time, unsigned int entry_height,
|
int64_t time, unsigned int entry_height,
|
||||||
bool spends_coinbase, int64_t sigops_count, LockPoints lp)
|
bool spends_coinbase, int64_t sigops_count, LockPoints lp)
|
||||||
@ -284,7 +344,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
|
|||||||
|
|
||||||
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
|
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
|
||||||
{
|
{
|
||||||
CTxMemPoolEntry::Parents parents = it->GetMemPoolParents();
|
const CTxMemPoolEntry::Parents& parents = it->GetMemPoolParentsConst();
|
||||||
// add or remove this tx as a child of each parent
|
// add or remove this tx as a child of each parent
|
||||||
for (const CTxMemPoolEntry& parent : parents) {
|
for (const CTxMemPoolEntry& parent : parents) {
|
||||||
UpdateChild(mapTx.iterator_to(parent), it, add);
|
UpdateChild(mapTx.iterator_to(parent), it, add);
|
||||||
@ -806,44 +866,24 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
|
|||||||
RemoveStaged(setAllRemoves, false, reason);
|
RemoveStaged(setAllRemoves, false, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
|
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
|
||||||
AssertLockHeld(cs);
|
AssertLockHeld(cs);
|
||||||
|
AssertLockHeld(::cs_main);
|
||||||
|
|
||||||
setEntries txToRemove;
|
setEntries txToRemove;
|
||||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||||
const CTransaction& tx = it->GetTx();
|
if (check_final_and_mature(it)) txToRemove.insert(it);
|
||||||
LockPoints lp = it->GetLockPoints();
|
|
||||||
bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
|
|
||||||
CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
|
|
||||||
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
|
|
||||||
|| !CheckSequenceLocks(active_chainstate.m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
|
|
||||||
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
|
|
||||||
// So it's critical that we remove the tx and not depend on the LockPoints.
|
|
||||||
txToRemove.insert(it);
|
|
||||||
} else if (it->GetSpendsCoinbase()) {
|
|
||||||
for (const CTxIn& txin : tx.vin) {
|
|
||||||
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
|
|
||||||
if (it2 != mapTx.end())
|
|
||||||
continue;
|
|
||||||
const Coin &coin = active_chainstate.CoinsTip().AccessCoin(txin.prevout);
|
|
||||||
if (m_check_ratio != 0) assert(!coin.IsSpent());
|
|
||||||
unsigned int nMemPoolHeight = active_chainstate.m_chain.Tip()->nHeight + 1;
|
|
||||||
if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
|
|
||||||
txToRemove.insert(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!validLP) {
|
|
||||||
mapTx.modify(it, update_lock_points(lp));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setEntries setAllRemoves;
|
setEntries setAllRemoves;
|
||||||
for (txiter it : txToRemove) {
|
for (txiter it : txToRemove) {
|
||||||
CalculateDescendants(it, setAllRemoves);
|
CalculateDescendants(it, setAllRemoves);
|
||||||
}
|
}
|
||||||
RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG);
|
RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG);
|
||||||
|
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||||
|
assert(TestLockPointValidity(chain, it->GetLockPoints()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::removeConflicts(const CTransaction &tx)
|
void CTxMemPool::removeConflicts(const CTransaction &tx)
|
||||||
@ -1101,16 +1141,7 @@ void CTxMemPool::clear()
|
|||||||
_clear();
|
_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
|
void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
|
||||||
{
|
|
||||||
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
|
||||||
CAmount txfee = 0;
|
|
||||||
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee);
|
|
||||||
assert(fCheckResult);
|
|
||||||
UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTxMemPool::check(CChainState& active_chainstate) const
|
|
||||||
{
|
{
|
||||||
if (m_check_ratio == 0) return;
|
if (m_check_ratio == 0) return;
|
||||||
|
|
||||||
@ -1123,20 +1154,16 @@ void CTxMemPool::check(CChainState& active_chainstate) const
|
|||||||
uint64_t checkTotal = 0;
|
uint64_t checkTotal = 0;
|
||||||
CAmount check_total_fee{0};
|
CAmount check_total_fee{0};
|
||||||
uint64_t innerUsage = 0;
|
uint64_t innerUsage = 0;
|
||||||
|
uint64_t prev_ancestor_count{0};
|
||||||
|
|
||||||
CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
|
|
||||||
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
|
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
|
||||||
const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
|
|
||||||
|
|
||||||
std::list<const CTxMemPoolEntry*> waitingOnDependants;
|
for (const auto& it : GetSortedDepthAndScore()) {
|
||||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
|
||||||
unsigned int i = 0;
|
|
||||||
checkTotal += it->GetTxSize();
|
checkTotal += it->GetTxSize();
|
||||||
check_total_fee += it->GetFee();
|
check_total_fee += it->GetFee();
|
||||||
innerUsage += it->DynamicMemoryUsage();
|
innerUsage += it->DynamicMemoryUsage();
|
||||||
const CTransaction& tx = it->GetTx();
|
const CTransaction& tx = it->GetTx();
|
||||||
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
|
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
|
||||||
bool fDependsWait = false;
|
|
||||||
CTxMemPoolEntry::Parents setParentCheck;
|
CTxMemPoolEntry::Parents setParentCheck;
|
||||||
for (const CTxIn &txin : tx.vin) {
|
for (const CTxIn &txin : tx.vin) {
|
||||||
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
|
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
|
||||||
@ -1144,17 +1171,17 @@ void CTxMemPool::check(CChainState& active_chainstate) const
|
|||||||
if (it2 != mapTx.end()) {
|
if (it2 != mapTx.end()) {
|
||||||
const CTransaction& tx2 = it2->GetTx();
|
const CTransaction& tx2 = it2->GetTx();
|
||||||
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
|
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
|
||||||
fDependsWait = true;
|
|
||||||
setParentCheck.insert(*it2);
|
setParentCheck.insert(*it2);
|
||||||
} else {
|
|
||||||
assert(active_coins_tip.HaveCoin(txin.prevout));
|
|
||||||
}
|
}
|
||||||
|
// We are iterating through the mempool entries sorted in order by ancestor count.
|
||||||
|
// All parents must have been checked before their children and their coins added to
|
||||||
|
// the mempoolDuplicate coins cache.
|
||||||
|
assert(mempoolDuplicate.HaveCoin(txin.prevout));
|
||||||
// Check whether its inputs are marked in mapNextTx.
|
// Check whether its inputs are marked in mapNextTx.
|
||||||
auto it3 = mapNextTx.find(txin.prevout);
|
auto it3 = mapNextTx.find(txin.prevout);
|
||||||
assert(it3 != mapNextTx.end());
|
assert(it3 != mapNextTx.end());
|
||||||
assert(it3->first == &txin.prevout);
|
assert(it3->first == &txin.prevout);
|
||||||
assert(it3->second == &tx);
|
assert(it3->second == &tx);
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
|
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
|
||||||
return a.GetTx().GetHash() == b.GetTx().GetHash();
|
return a.GetTx().GetHash() == b.GetTx().GetHash();
|
||||||
@ -1181,6 +1208,9 @@ void CTxMemPool::check(CChainState& active_chainstate) const
|
|||||||
assert(it->GetSizeWithAncestors() == nSizeCheck);
|
assert(it->GetSizeWithAncestors() == nSizeCheck);
|
||||||
assert(it->GetSigOpCountWithAncestors() == nSigOpCheck);
|
assert(it->GetSigOpCountWithAncestors() == nSigOpCheck);
|
||||||
assert(it->GetModFeesWithAncestors() == nFeesCheck);
|
assert(it->GetModFeesWithAncestors() == nFeesCheck);
|
||||||
|
// Sanity check: we are walking in ascending ancestor count order.
|
||||||
|
assert(prev_ancestor_count <= it->GetCountWithAncestors());
|
||||||
|
prev_ancestor_count = it->GetCountWithAncestors();
|
||||||
|
|
||||||
// Check children against mapNextTx
|
// Check children against mapNextTx
|
||||||
CTxMemPoolEntry::Children setChildrenCheck;
|
CTxMemPoolEntry::Children setChildrenCheck;
|
||||||
@ -1199,24 +1229,12 @@ void CTxMemPool::check(CChainState& active_chainstate) const
|
|||||||
// just a sanity check, not definitive that this calc is correct...
|
// just a sanity check, not definitive that this calc is correct...
|
||||||
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
|
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
|
||||||
|
|
||||||
if (fDependsWait)
|
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
||||||
waitingOnDependants.push_back(&(*it));
|
CAmount txfee = 0;
|
||||||
else {
|
assert(!tx.IsCoinBase());
|
||||||
CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
|
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
|
||||||
}
|
for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
|
||||||
}
|
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
|
||||||
unsigned int stepsSinceLastRemove = 0;
|
|
||||||
while (!waitingOnDependants.empty()) {
|
|
||||||
const CTxMemPoolEntry* entry = waitingOnDependants.front();
|
|
||||||
waitingOnDependants.pop_front();
|
|
||||||
if (!mempoolDuplicate.HaveInputs(entry->GetTx())) {
|
|
||||||
waitingOnDependants.push_back(entry);
|
|
||||||
stepsSinceLastRemove++;
|
|
||||||
assert(stepsSinceLastRemove < waitingOnDependants.size());
|
|
||||||
} else {
|
|
||||||
CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
|
|
||||||
stepsSinceLastRemove = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
|
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
|
||||||
uint256 hash = it->second->GetHash();
|
uint256 hash = it->second->GetHash();
|
||||||
|
@ -30,12 +30,13 @@
|
|||||||
#include <util/epochguard.h>
|
#include <util/epochguard.h>
|
||||||
#include <util/hasher.h>
|
#include <util/hasher.h>
|
||||||
|
|
||||||
#include <boost/multi_index_container.hpp>
|
|
||||||
#include <boost/multi_index/hashed_index.hpp>
|
#include <boost/multi_index/hashed_index.hpp>
|
||||||
#include <boost/multi_index/ordered_index.hpp>
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
#include <boost/multi_index/sequenced_index.hpp>
|
#include <boost/multi_index/sequenced_index.hpp>
|
||||||
|
#include <boost/multi_index_container.hpp>
|
||||||
|
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
|
class CChain;
|
||||||
class CChainState;
|
class CChainState;
|
||||||
extern RecursiveMutex cs_main;
|
extern RecursiveMutex cs_main;
|
||||||
|
|
||||||
@ -59,6 +60,11 @@ struct LockPoints {
|
|||||||
CBlockIndex* maxInputBlock{nullptr};
|
CBlockIndex* maxInputBlock{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the LockPoints height and time are still valid on the current chain
|
||||||
|
*/
|
||||||
|
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
struct CompareIteratorByHash {
|
struct CompareIteratorByHash {
|
||||||
// SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T>
|
// SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T>
|
||||||
// (e.g. a wrapped CTxMemPoolEntry&)
|
// (e.g. a wrapped CTxMemPoolEntry&)
|
||||||
@ -171,58 +177,6 @@ public:
|
|||||||
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
|
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
|
|
||||||
struct update_descendant_state
|
|
||||||
{
|
|
||||||
update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
|
|
||||||
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void operator() (CTxMemPoolEntry &e)
|
|
||||||
{ e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int64_t modifySize;
|
|
||||||
CAmount modifyFee;
|
|
||||||
int64_t modifyCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct update_ancestor_state
|
|
||||||
{
|
|
||||||
update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int _modifySigOps) :
|
|
||||||
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOps(_modifySigOps)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void operator() (CTxMemPoolEntry &e)
|
|
||||||
{ e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOps); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int64_t modifySize;
|
|
||||||
CAmount modifyFee;
|
|
||||||
int64_t modifyCount;
|
|
||||||
int modifySigOps;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct update_fee_delta
|
|
||||||
{
|
|
||||||
explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
|
|
||||||
|
|
||||||
void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int64_t feeDelta;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct update_lock_points
|
|
||||||
{
|
|
||||||
explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
|
|
||||||
|
|
||||||
void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const LockPoints& lp;
|
|
||||||
};
|
|
||||||
|
|
||||||
// extracts a transaction hash from CTxMemPoolEntry or CTransactionRef
|
// extracts a transaction hash from CTxMemPoolEntry or CTransactionRef
|
||||||
struct mempoolentry_txid
|
struct mempoolentry_txid
|
||||||
{
|
{
|
||||||
@ -354,6 +308,16 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct update_lock_points
|
||||||
|
{
|
||||||
|
explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
|
||||||
|
|
||||||
|
void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const LockPoints& lp;
|
||||||
|
};
|
||||||
|
|
||||||
// Multi_index tag names
|
// Multi_index tag names
|
||||||
struct descendant_score {};
|
struct descendant_score {};
|
||||||
struct entry_time {};
|
struct entry_time {};
|
||||||
@ -645,7 +609,7 @@ public:
|
|||||||
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
|
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
|
||||||
* check does nothing.
|
* check does nothing.
|
||||||
*/
|
*/
|
||||||
void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
void check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
// addUnchecked must updated state for all ancestors of a given transaction,
|
// addUnchecked must updated state for all ancestors of a given transaction,
|
||||||
// to track size/count of descendant transactions. First version of
|
// to track size/count of descendant transactions. First version of
|
||||||
@ -667,7 +631,10 @@ public:
|
|||||||
bool removeSpentIndex(const uint256 txhash);
|
bool removeSpentIndex(const uint256 txhash);
|
||||||
|
|
||||||
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
void removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
|
/** After reorg, check if mempool entries are now non-final, premature coinbase spends, or have
|
||||||
|
* invalid lockpoints. Update lockpoints and remove entries (and descendants of entries) that
|
||||||
|
* are no longer valid. */
|
||||||
|
void removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
|
||||||
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
@ -20,9 +20,9 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
|||||||
case TransactionError::P2P_DISABLED:
|
case TransactionError::P2P_DISABLED:
|
||||||
return Untranslated("Peer-to-peer functionality missing or disabled");
|
return Untranslated("Peer-to-peer functionality missing or disabled");
|
||||||
case TransactionError::MEMPOOL_REJECTED:
|
case TransactionError::MEMPOOL_REJECTED:
|
||||||
return Untranslated("Transaction rejected by AcceptToMemoryPool");
|
return Untranslated("Transaction rejected by mempool");
|
||||||
case TransactionError::MEMPOOL_ERROR:
|
case TransactionError::MEMPOOL_ERROR:
|
||||||
return Untranslated("AcceptToMemoryPool failed");
|
return Untranslated("Mempool internal error");
|
||||||
case TransactionError::INVALID_PSBT:
|
case TransactionError::INVALID_PSBT:
|
||||||
return Untranslated("PSBT is not well-formed");
|
return Untranslated("PSBT is not well-formed");
|
||||||
case TransactionError::PSBT_MISMATCH:
|
case TransactionError::PSBT_MISMATCH:
|
||||||
|
@ -33,10 +33,6 @@ public:
|
|||||||
SaltedOutpointHasher(bool deterministic = false);
|
SaltedOutpointHasher(bool deterministic = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This *must* return size_t. With Boost 1.46 on 32-bit systems the
|
|
||||||
* unordered_map will behave unpredictably if the custom hasher returns a
|
|
||||||
* uint64_t, resulting in failures when syncing the chain (#4634).
|
|
||||||
*
|
|
||||||
* Having the hash noexcept allows libstdc++'s unordered_map to recalculate
|
* Having the hash noexcept allows libstdc++'s unordered_map to recalculate
|
||||||
* the hash during rehash, so it does not have to cache the value. This
|
* the hash during rehash, so it does not have to cache the value. This
|
||||||
* reduces node's memory by sizeof(size_t). The required recalculation has
|
* reduces node's memory by sizeof(size_t). The required recalculation has
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
@ -501,20 +502,6 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HexStr(const Span<const uint8_t> s)
|
|
||||||
{
|
|
||||||
std::string rv(s.size() * 2, '\0');
|
|
||||||
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
|
||||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
||||||
auto it = rv.begin();
|
|
||||||
for (uint8_t v : s) {
|
|
||||||
*it++ = hexmap[v >> 4];
|
|
||||||
*it++ = hexmap[v & 15];
|
|
||||||
}
|
|
||||||
assert(it == rv.end());
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ToLower(const std::string& str)
|
std::string ToLower(const std::string& str)
|
||||||
{
|
{
|
||||||
std::string r;
|
std::string r;
|
||||||
@ -535,3 +522,62 @@ std::string Capitalize(std::string str)
|
|||||||
str[0] = ToUpper(str.front());
|
str[0] = ToUpper(str.front());
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string HexStr(const Span<const uint8_t> s)
|
||||||
|
{
|
||||||
|
std::string rv(s.size() * 2, '\0');
|
||||||
|
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||||
|
auto it = rv.begin();
|
||||||
|
for (uint8_t v : s) {
|
||||||
|
*it++ = hexmap[v >> 4];
|
||||||
|
*it++ = hexmap[v & 15];
|
||||||
|
}
|
||||||
|
assert(it == rv.end());
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<uint64_t> ParseByteUnits(const std::string& str, ByteUnit default_multiplier)
|
||||||
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
auto multiplier = default_multiplier;
|
||||||
|
char unit = str.back();
|
||||||
|
switch (unit) {
|
||||||
|
case 'k':
|
||||||
|
multiplier = ByteUnit::k;
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
multiplier = ByteUnit::K;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
multiplier = ByteUnit::m;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
multiplier = ByteUnit::M;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
multiplier = ByteUnit::g;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
multiplier = ByteUnit::G;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
multiplier = ByteUnit::t;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
multiplier = ByteUnit::T;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unit = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t unit_amount = static_cast<uint64_t>(multiplier);
|
||||||
|
auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
|
||||||
|
if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return *parsed_num * unit_amount;
|
||||||
|
}
|
||||||
|
@ -28,6 +28,23 @@ enum SafeChars
|
|||||||
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
|
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by ParseByteUnits()
|
||||||
|
* Lowercase base 1000
|
||||||
|
* Uppercase base 1024
|
||||||
|
*/
|
||||||
|
enum class ByteUnit : uint64_t {
|
||||||
|
NOOP = 1ULL,
|
||||||
|
k = 1000ULL,
|
||||||
|
K = 1024ULL,
|
||||||
|
m = 1'000'000ULL,
|
||||||
|
M = 1ULL << 20,
|
||||||
|
g = 1'000'000'000ULL,
|
||||||
|
G = 1ULL << 30,
|
||||||
|
t = 1'000'000'000'000ULL,
|
||||||
|
T = 1ULL << 40,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email
|
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email
|
||||||
* addresses, but avoid anything even possibly remotely dangerous like & or >
|
* addresses, but avoid anything even possibly remotely dangerous like & or >
|
||||||
@ -312,7 +329,17 @@ std::string ToUpper(const std::string& str);
|
|||||||
*/
|
*/
|
||||||
std::string Capitalize(std::string str);
|
std::string Capitalize(std::string str);
|
||||||
|
|
||||||
/** Parse an HD keypaths like "m/7/0'/2000". */
|
/**
|
||||||
[[nodiscard]] bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
|
* Parse a string with suffix unit [k|K|m|M|g|G|t|T].
|
||||||
|
* Must be a whole integer, fractions not allowed (0.5t), no whitespace or +-
|
||||||
|
* Lowercase units are 1000 base. Uppercase units are 1024 base.
|
||||||
|
* Examples: 2m,27M,19g,41T
|
||||||
|
*
|
||||||
|
* @param[in] str the string to convert into bytes
|
||||||
|
* @param[in] default_multiplier if no unit is found in str use this unit
|
||||||
|
* @returns optional uint64_t bytes from str or nullopt
|
||||||
|
* if ToIntegral is false, str is empty, trailing whitespace or overflow
|
||||||
|
*/
|
||||||
|
std::optional<uint64_t> ParseByteUnits(const std::string& str, ByteUnit default_multiplier);
|
||||||
|
|
||||||
#endif // BITCOIN_UTIL_STRENCODINGS_H
|
#endif // BITCOIN_UTIL_STRENCODINGS_H
|
||||||
|
@ -154,7 +154,7 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
|
|||||||
|
|
||||||
// CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
|
// CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
|
||||||
// nLockTime because when IsFinalTx() is called within
|
// nLockTime because when IsFinalTx() is called within
|
||||||
// CBlock::AcceptBlock(), the height of the block *being*
|
// AcceptBlock(), the height of the block *being*
|
||||||
// evaluated is what is used. Thus if we want to know if a
|
// evaluated is what is used. Thus if we want to know if a
|
||||||
// transaction can be part of the *next* block, we need to call
|
// transaction can be part of the *next* block, we need to call
|
||||||
// IsFinalTx() with one more than active_chain_tip.Height().
|
// IsFinalTx() with one more than active_chain_tip.Height().
|
||||||
@ -172,24 +172,6 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
|
|||||||
return IsFinalTx(tx, nBlockHeight, nBlockTime);
|
return IsFinalTx(tx, nBlockHeight, nBlockTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
|
|
||||||
{
|
|
||||||
AssertLockHeld(cs_main);
|
|
||||||
assert(lp);
|
|
||||||
// If there are relative lock times then the maxInputBlock will be set
|
|
||||||
// If there are no relative lock times, the LockPoints don't depend on the chain
|
|
||||||
if (lp->maxInputBlock) {
|
|
||||||
// Check whether ::ChainActive() is an extension of the block at which the LockPoints
|
|
||||||
// calculation was valid. If not LockPoints are no longer valid
|
|
||||||
if (!active_chain.Contains(lp->maxInputBlock)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LockPoints still valid
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CheckSequenceLocks(CBlockIndex* tip,
|
bool CheckSequenceLocks(CBlockIndex* tip,
|
||||||
const CCoinsView& coins_view,
|
const CCoinsView& coins_view,
|
||||||
const CTransaction& tx,
|
const CTransaction& tx,
|
||||||
@ -389,8 +371,41 @@ void CChainState::MaybeUpdateMempoolForReorg(
|
|||||||
// the disconnectpool that were added back and cleans up the mempool state.
|
// the disconnectpool that were added back and cleans up the mempool state.
|
||||||
m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
|
m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
|
||||||
|
|
||||||
|
const auto check_final_and_mature = [this, flags=STANDARD_LOCKTIME_VERIFY_FLAGS](CTxMemPool::txiter it)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) {
|
||||||
|
bool should_remove = false;
|
||||||
|
AssertLockHeld(m_mempool->cs);
|
||||||
|
AssertLockHeld(::cs_main);
|
||||||
|
const CTransaction& tx = it->GetTx();
|
||||||
|
LockPoints lp = it->GetLockPoints();
|
||||||
|
const bool validLP{TestLockPointValidity(m_chain, lp)};
|
||||||
|
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
|
||||||
|
if (!CheckFinalTx(m_chain.Tip(), tx, flags)
|
||||||
|
|| !CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
|
||||||
|
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
|
||||||
|
// So it's critical that we remove the tx and not depend on the LockPoints.
|
||||||
|
should_remove = true;
|
||||||
|
} else if (it->GetSpendsCoinbase()) {
|
||||||
|
for (const CTxIn& txin : tx.vin) {
|
||||||
|
auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
|
||||||
|
if (it2 != m_mempool->mapTx.end())
|
||||||
|
continue;
|
||||||
|
const Coin &coin = CoinsTip().AccessCoin(txin.prevout);
|
||||||
|
assert(!coin.IsSpent());
|
||||||
|
const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
|
||||||
|
if (coin.IsSpent() || (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY)) {
|
||||||
|
should_remove = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CheckSequenceLocks updates lp. Update the mempool entry LockPoints.
|
||||||
|
if (!validLP) m_mempool->mapTx.modify(it, update_lock_points(lp));
|
||||||
|
return should_remove;
|
||||||
|
};
|
||||||
|
|
||||||
// We also need to remove any now-immature transactions
|
// We also need to remove any now-immature transactions
|
||||||
m_mempool->removeForReorg(*this, STANDARD_LOCKTIME_VERIFY_FLAGS);
|
m_mempool->removeForReorg(m_chain, check_final_and_mature);
|
||||||
// Re-limit mempool size, in case we added any transactions
|
// Re-limit mempool size, in case we added any transactions
|
||||||
LimitMempoolSize(
|
LimitMempoolSize(
|
||||||
*m_mempool,
|
*m_mempool,
|
||||||
@ -1386,12 +1401,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
|||||||
AddCoins(inputs, tx, nHeight);
|
AddCoins(inputs, tx, nHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
|
||||||
{
|
|
||||||
CTxUndo txundo;
|
|
||||||
UpdateCoins(tx, inputs, txundo, nHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CScriptCheck::operator()() {
|
bool CScriptCheck::operator()() {
|
||||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||||
PrecomputedTransactionData txdata(*ptxTo);
|
PrecomputedTransactionData txdata(*ptxTo);
|
||||||
@ -1948,8 +1957,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||||||
// can be duplicated to remove the ability to spend the first instance -- even after
|
// can be duplicated to remove the ability to spend the first instance -- even after
|
||||||
// being sent to another address.
|
// being sent to another address.
|
||||||
// See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information.
|
// See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information.
|
||||||
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
|
|
||||||
// already refuses previously-known transaction ids entirely.
|
|
||||||
// This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC.
|
// This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC.
|
||||||
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
|
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
|
||||||
// two in the chain that violate it. This prevents exploiting the issue against nodes during their
|
// two in the chain that violate it. This prevents exploiting the issue against nodes during their
|
||||||
@ -2939,7 +2946,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
|
|||||||
// any disconnected transactions back to the mempool.
|
// any disconnected transactions back to the mempool.
|
||||||
MaybeUpdateMempoolForReorg(disconnectpool, true);
|
MaybeUpdateMempoolForReorg(disconnectpool, true);
|
||||||
}
|
}
|
||||||
if (m_mempool) m_mempool->check(*this);
|
if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1);
|
||||||
|
|
||||||
CheckForkWarningConditions();
|
CheckForkWarningConditions();
|
||||||
|
|
||||||
@ -3986,6 +3993,19 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef& tx, bool test_accept, bool bypass_limits)
|
||||||
|
{
|
||||||
|
CChainState& active_chainstate = ActiveChainstate();
|
||||||
|
if (!active_chainstate.m_mempool) {
|
||||||
|
TxValidationState state;
|
||||||
|
state.Invalid(TxValidationResult::TX_NO_MEMPOOL, "no-mempool");
|
||||||
|
return MempoolAcceptResult::Failure(state);
|
||||||
|
}
|
||||||
|
auto result = AcceptToMemoryPool(active_chainstate, *active_chainstate.m_mempool, tx, bypass_limits, test_accept);
|
||||||
|
active_chainstate.m_mempool->check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool TestBlockValidity(BlockValidationState& state,
|
bool TestBlockValidity(BlockValidationState& state,
|
||||||
llmq::CChainLocksHandler& clhandler,
|
llmq::CChainLocksHandler& clhandler,
|
||||||
CEvoDB& evoDb,
|
CEvoDB& evoDb,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
|
#include <arith_uint256.h>
|
||||||
#include <attributes.h>
|
#include <attributes.h>
|
||||||
#include <chain.h>
|
#include <chain.h>
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
@ -19,10 +20,11 @@
|
|||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <policy/packages.h>
|
#include <policy/packages.h>
|
||||||
#include <script/script_error.h>
|
#include <script/script_error.h>
|
||||||
|
#include <serialize.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
#include <txdb.h>
|
#include <txdb.h>
|
||||||
#include <txmempool.h> // For CTxMemPool::cs
|
#include <txmempool.h> // For CTxMemPool::cs
|
||||||
#include <serialize.h>
|
#include <uint256.h>
|
||||||
#include <util/check.h>
|
#include <util/check.h>
|
||||||
#include <util/hasher.h>
|
#include <util/hasher.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
@ -38,17 +40,10 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace llmq {
|
|
||||||
class CChainLocksHandler;
|
|
||||||
class CInstantSendManager;
|
|
||||||
} // namespace llmq
|
|
||||||
|
|
||||||
class CEvoDB;
|
|
||||||
|
|
||||||
class CChainState;
|
class CChainState;
|
||||||
class CBlockIndex;
|
|
||||||
class CBlockTreeDB;
|
class CBlockTreeDB;
|
||||||
class CChainParams;
|
class CChainParams;
|
||||||
|
class CEvoDB;
|
||||||
class CMNHFManager;
|
class CMNHFManager;
|
||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
class TxValidationState;
|
class TxValidationState;
|
||||||
@ -61,6 +56,11 @@ struct DisconnectedBlockTransactions;
|
|||||||
struct LockPoints;
|
struct LockPoints;
|
||||||
struct AssumeutxoData;
|
struct AssumeutxoData;
|
||||||
|
|
||||||
|
namespace llmq {
|
||||||
|
class CChainLocksHandler;
|
||||||
|
class CInstantSendManager;
|
||||||
|
} // namespace llmq
|
||||||
|
|
||||||
/** Default for -minrelaytxfee, minimum relay fee for transactions */
|
/** Default for -minrelaytxfee, minimum relay fee for transactions */
|
||||||
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
|
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
|
||||||
/** Default for -limitancestorcount, max number of in-mempool ancestors */
|
/** Default for -limitancestorcount, max number of in-mempool ancestors */
|
||||||
@ -240,9 +240,16 @@ struct PackageMempoolAcceptResult
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Try to) add a transaction to the memory pool.
|
* Try to add a transaction to the mempool. This is an internal function and is
|
||||||
* @param[in] bypass_limits When true, don't enforce mempool fee limits.
|
* exposed only for testing. Client code should use ChainstateManager::ProcessTransaction()
|
||||||
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
|
*
|
||||||
|
* @param[in] active_chainstate Reference to the active chainstate.
|
||||||
|
* @param[in] pool Reference to the node's mempool.
|
||||||
|
* @param[in] tx The transaction to submit for mempool acceptance.
|
||||||
|
* @param[in] bypass_limits When true, don't enforce mempool fee and capacity limits.
|
||||||
|
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
|
||||||
|
*
|
||||||
|
* @returns a MempoolAcceptResult indicating whether the transaction was accepted/rejected with reason.
|
||||||
*/
|
*/
|
||||||
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
|
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
|
||||||
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
@ -266,9 +273,6 @@ bool GetUTXOCoin(CChainState& active_chainstate, const COutPoint& outpoint, Coin
|
|||||||
int GetUTXOHeight(CChainState& active_chainstate, const COutPoint& outpoint);
|
int GetUTXOHeight(CChainState& active_chainstate, const COutPoint& outpoint);
|
||||||
int GetUTXOConfirmations(CChainState& active_chainstate, const COutPoint& outpoint);
|
int GetUTXOConfirmations(CChainState& active_chainstate, const COutPoint& outpoint);
|
||||||
|
|
||||||
/** Apply the effects of this transaction on the UTXO set represented by view */
|
|
||||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
|
|
||||||
|
|
||||||
/** Transaction validation functions */
|
/** Transaction validation functions */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -280,11 +284,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
|
|||||||
*/
|
*/
|
||||||
bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether the LockPoints height and time are still valid on the current chain
|
|
||||||
*/
|
|
||||||
bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
|
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
|
||||||
* @param[in] tip Chain tip to check tx sequence locks against. For example,
|
* @param[in] tip Chain tip to check tx sequence locks against. For example,
|
||||||
@ -1024,6 +1023,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
|
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to add a transaction to the memory pool.
|
||||||
|
*
|
||||||
|
* @param[in] tx The transaction to submit for mempool acceptance.
|
||||||
|
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
|
||||||
|
* @param[in] bypass_limits When true, don't enforce mempool fee and capacity limits.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] MempoolAcceptResult ProcessTransaction(const CTransactionRef& tx, bool test_accept=false, bool bypass_limits=false)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
|
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
|
||||||
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
|
@ -15,14 +15,12 @@ becomes valid.
|
|||||||
import copy
|
import copy
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from test_framework.blocktools import create_block, create_coinbase, create_tx_with_script
|
from test_framework.blocktools import MAX_FUTURE_BLOCK_TIME, create_block, create_coinbase, create_tx_with_script
|
||||||
from test_framework.messages import COIN
|
from test_framework.messages import COIN
|
||||||
from test_framework.p2p import P2PDataStore
|
from test_framework.p2p import P2PDataStore
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import assert_equal
|
from test_framework.util import assert_equal
|
||||||
|
|
||||||
MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidBlockRequestTest(BitcoinTestFramework):
|
class InvalidBlockRequestTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
|
@ -27,9 +27,10 @@ import subprocess
|
|||||||
|
|
||||||
from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE
|
from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE
|
||||||
from test_framework.blocktools import (
|
from test_framework.blocktools import (
|
||||||
|
MAX_FUTURE_BLOCK_TIME,
|
||||||
|
TIME_GENESIS_BLOCK,
|
||||||
create_block,
|
create_block,
|
||||||
create_coinbase,
|
create_coinbase,
|
||||||
TIME_GENESIS_BLOCK,
|
|
||||||
)
|
)
|
||||||
from test_framework.governance import EXPECTED_STDERR_NO_GOV_PRUNE
|
from test_framework.governance import EXPECTED_STDERR_NO_GOV_PRUNE
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
@ -48,25 +49,27 @@ from test_framework.util import (
|
|||||||
assert_is_hex_string,
|
assert_is_hex_string,
|
||||||
assert_is_hash_string,
|
assert_is_hash_string,
|
||||||
get_datadir_path,
|
get_datadir_path,
|
||||||
set_node_times,
|
|
||||||
)
|
)
|
||||||
from test_framework.wallet import MiniWallet
|
from test_framework.wallet import MiniWallet
|
||||||
|
|
||||||
|
|
||||||
HEIGHT = 200 # blocks mined
|
HEIGHT = 200 # blocks mined
|
||||||
TIME_RANGE_STEP = 156 # ten-minute steps
|
TIME_RANGE_STEP = 156 # two and a half minute steps
|
||||||
TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP
|
TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP
|
||||||
|
TIME_RANGE_TIP = TIME_GENESIS_BLOCK + (HEIGHT - 1) * TIME_RANGE_STEP
|
||||||
TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
|
TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
|
||||||
|
|
||||||
|
|
||||||
class BlockchainTest(BitcoinTestFramework):
|
class BlockchainTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
|
self.disable_mocktime = True
|
||||||
self.setup_clean_chain = True
|
self.setup_clean_chain = True
|
||||||
self.num_nodes = 1
|
self.num_nodes = 1
|
||||||
self.supports_cli = False
|
self.supports_cli = False
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
self.mine_chain()
|
self.mine_chain()
|
||||||
|
self._test_max_future_block_time()
|
||||||
self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1', '-txindex=0']) # Set extra args with pruning after rescan is complete
|
self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1', '-txindex=0']) # Set extra args with pruning after rescan is complete
|
||||||
|
|
||||||
# Actual tests
|
# Actual tests
|
||||||
@ -85,10 +88,23 @@ class BlockchainTest(BitcoinTestFramework):
|
|||||||
self.log.info(f"Generate {HEIGHT} blocks after the genesis block in 156 sec")
|
self.log.info(f"Generate {HEIGHT} blocks after the genesis block in 156 sec")
|
||||||
for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP):
|
for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP):
|
||||||
# 156 sec steps from genesis block time
|
# 156 sec steps from genesis block time
|
||||||
set_node_times(self.nodes, t)
|
self.nodes[0].setmocktime(t)
|
||||||
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2SH_OP_TRUE)
|
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2SH_OP_TRUE)
|
||||||
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
|
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
|
||||||
|
|
||||||
|
def _test_max_future_block_time(self):
|
||||||
|
self.stop_node(0)
|
||||||
|
self.log.info("A block tip of more than MAX_FUTURE_BLOCK_TIME in the future raises an error")
|
||||||
|
self.nodes[0].assert_start_raises_init_error(
|
||||||
|
extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME - 1}"],
|
||||||
|
expected_msg=": The block database contains a block which appears to be from the future."
|
||||||
|
" This may be due to your computer's date and time being set incorrectly."
|
||||||
|
f" Only rebuild the block database if you are sure that your computer's date and time are correct.{os.linesep}"
|
||||||
|
"Please restart with -reindex or -reindex-chainstate to recover.",
|
||||||
|
)
|
||||||
|
self.log.info("A block tip of MAX_FUTURE_BLOCK_TIME in the future is fine")
|
||||||
|
self.start_node(0, extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME}"])
|
||||||
|
|
||||||
def _test_getblockchaininfo(self):
|
def _test_getblockchaininfo(self):
|
||||||
self.log.info("Test getblockchaininfo")
|
self.log.info("Test getblockchaininfo")
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ MAX_BLOCK_SIGOPS = 40000
|
|||||||
# Genesis block time (regtest)
|
# Genesis block time (regtest)
|
||||||
TIME_GENESIS_BLOCK = 1417713337
|
TIME_GENESIS_BLOCK = 1417713337
|
||||||
|
|
||||||
|
MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
|
||||||
|
|
||||||
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
|
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
|
||||||
COINBASE_MATURITY = 100
|
COINBASE_MATURITY = 100
|
||||||
|
|
||||||
|
@ -631,6 +631,8 @@ class NetworkThread(threading.Thread):
|
|||||||
|
|
||||||
NetworkThread.listeners = {}
|
NetworkThread.listeners = {}
|
||||||
NetworkThread.protos = {}
|
NetworkThread.protos = {}
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
NetworkThread.network_event_loop = asyncio.new_event_loop()
|
NetworkThread.network_event_loop = asyncio.new_event_loop()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -18,7 +18,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
|||||||
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
|
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
|
||||||
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
|
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
|
||||||
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
|
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
|
||||||
"txmempool -> validation -> txmempool"
|
|
||||||
"wallet/fees -> wallet/wallet -> wallet/fees"
|
"wallet/fees -> wallet/wallet -> wallet/fees"
|
||||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
||||||
"node/coinstats -> validation -> node/coinstats"
|
"node/coinstats -> validation -> node/coinstats"
|
||||||
@ -63,12 +62,13 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
|||||||
|
|
||||||
"llmq/chainlocks -> validation -> llmq/chainlocks"
|
"llmq/chainlocks -> validation -> llmq/chainlocks"
|
||||||
"coinjoin/coinjoin -> llmq/chainlocks -> net -> coinjoin/coinjoin"
|
"coinjoin/coinjoin -> llmq/chainlocks -> net -> coinjoin/coinjoin"
|
||||||
|
"evo/assetlocktx -> validation -> txmempool -> evo/assetlocktx"
|
||||||
"evo/deterministicmns -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns -> evo/deterministicmns"
|
"evo/deterministicmns -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns -> evo/deterministicmns"
|
||||||
"evo/deterministicmns -> llmq/utils -> net -> evo/deterministicmns"
|
"evo/deterministicmns -> llmq/utils -> net -> evo/deterministicmns"
|
||||||
|
"evo/deterministicmns -> validation -> txmempool -> evo/deterministicmns"
|
||||||
"policy/policy -> policy/settings -> policy/policy"
|
"policy/policy -> policy/settings -> policy/policy"
|
||||||
"consensus/tx_verify -> evo/assetlocktx -> validation -> consensus/tx_verify"
|
"consensus/tx_verify -> evo/assetlocktx -> validation -> consensus/tx_verify"
|
||||||
"consensus/tx_verify -> evo/assetlocktx -> llmq/signing -> net_processing -> txmempool -> consensus/tx_verify"
|
"consensus/tx_verify -> evo/assetlocktx -> validation -> txmempool -> consensus/tx_verify"
|
||||||
"evo/assetlocktx -> llmq/signing -> net_processing -> txmempool -> evo/assetlocktx"
|
|
||||||
|
|
||||||
"evo/simplifiedmns -> llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns"
|
"evo/simplifiedmns -> llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> evo/simplifiedmns"
|
||||||
"llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor"
|
"llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor"
|
||||||
|
Loading…
Reference in New Issue
Block a user