21ad71c578 Merge #21676: test: Use mocktime to avoid intermittent failure in rpc_tests (MarcoFalke)
76a41eb245 Merge #21602: rpc: add additional ban time fields to listbanned (MarcoFalke)
adea52a5fe Merge bitcoin-core/gui#260: Handle exceptions instead of crash (W. J. van der Laan)
7e023c394f Merge #17934: doc: Use CONFIG_SITE variable instead of --prefix option (fanquake)
bc6e3ed6e4 Merge #21606: fuzz: Extend psbt fuzz target a bit (MarcoFalke)
233fb245f7 Merge #21445: cirrus: Use SSD cluster for speedup (fanquake)
a224b800e4 Merge #21609: ci: increase CPU count of sanitizer job to increase memory limit (MarcoFalke)
ad947099a0 test: remove exception for util::Ref which doesn't exist more (Konstantin Akimov)
6674ee85ab Merge #21390: test: Test improvements for UTXO set hash tests (MarcoFalke)
e10eec249b Merge #21338: test: add functional test for anchors.dat (MarcoFalke)
d9c31d6817 Merge #21411: test: add logging, reduce blocks, move sync_all in wallet_ groups (MarcoFalke)

Pull request description:

  ## Issue being fixed or feature implemented
  Regular backports from bitcoin v22

  ## Note for reviewers:
  PRs bitcoin#17934 and bitcoin#21606 have been backported partially in past.

  ## What was done?
  Removed unused sanitizer rules (see bitcoin#21366 and dashpay/dash#5055)
   - bitcoin/bitcoin#21338
   - bitcoin/bitcoin#21390
   - bitcoin/bitcoin#21609
   - bitcoin/bitcoin#21445
   - bitcoin/bitcoin#21606
   - bitcoin/bitcoin#17934
   - bitcoin-core/gui#260
   - bitcoin/bitcoin#21602
   - bitcoin/bitcoin#21676

  ## How Has This Been Tested?
  Run unit/functional tests

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  PastaPastaPasta:
    utACK 21ad71c578

Tree-SHA512: 94276d56255300d7d8c056d15b468720ba028d83cc585b16396e8bad90157e9e010490be239e09cccd4362f575f8df2d56dde3fb505745376c7790d70e3635cd
This commit is contained in:
pasta 2024-04-12 10:30:20 -05:00
commit 7aa8f54c0f
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
29 changed files with 259 additions and 60 deletions

View File

@ -29,7 +29,6 @@ global_task_template: &GLOBAL_TASK_TEMPLATE
# Each project has 16 CPU in total, assign 2 to each container, so that 8 tasks run in parallel # Each project has 16 CPU in total, assign 2 to each container, so that 8 tasks run in parallel
cpu: 2 cpu: 2
memory: 8G # Set to 8GB to avoid OOM. https://cirrus-ci.org/guide/linux/#linux-containers memory: 8G # Set to 8GB to avoid OOM. https://cirrus-ci.org/guide/linux/#linux-containers
kvm: true # Use kvm to avoid spurious CI failures in the default virtualization cluster, see https://github.com/bitcoin/bitcoin/issues/20093
ccache_cache: ccache_cache:
folder: "/tmp/ccache_dir" folder: "/tmp/ccache_dir"
depends_built_cache: depends_built_cache:
@ -102,7 +101,7 @@ task:
<< : *GLOBAL_TASK_TEMPLATE << : *GLOBAL_TASK_TEMPLATE
container: container:
image: ubuntu:lunar image: ubuntu:lunar
cpu: 4 # Double CPU and increase Memory to avoid timeout cpu: 6 # Increase CPU and Memory to avoid timeout
memory: 24G memory: 24G
env: env:
MAKEJOBS: "-j8" MAKEJOBS: "-j8"

View File

@ -24,7 +24,7 @@ The multiprocess feature requires [Cap'n Proto](https://capnproto.org/) and [lib
``` ```
cd <DASH_SOURCE_DIRECTORY> cd <DASH_SOURCE_DIRECTORY>
make -C depends NO_QT=1 MULTIPROCESS=1 make -C depends NO_QT=1 MULTIPROCESS=1
./configure --prefix=$PWD/depends/x86_64-pc-linux-gnu CONFIG_SITE=$PWD/depends/x86_64-pc-linux-gnu/share/config.site ./configure
make make
src/dash-node -regtest -printtoconsole -debug=ipc src/dash-node -regtest -printtoconsole -debug=ipc
DASHD=dash-node test/functional/test_runner.py DASHD=dash-node test/functional/test_runner.py

View File

@ -0,0 +1,7 @@
Updated RPCs
------------
- The `listbanned` RPC now returns two new numeric fields: `ban_duration` and `time_remaining`.
Respectively, these new fields indicate the duration of a ban and the time remaining until a ban expires,
both in seconds. Additionally, the `ban_created` field is repositioned to come before `banned_until`. (#5976)

View File

@ -18,6 +18,7 @@
#include <map> #include <map>
// Database-independent metric indicating the UTXO set size
uint64_t GetBogoSize(const CScript& script_pub_key) uint64_t GetBogoSize(const CScript& script_pub_key)
{ {
return 32 /* txid */ + return 32 /* txid */ +

View File

@ -47,11 +47,13 @@
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QLatin1String>
#include <QLibraryInfo> #include <QLibraryInfo>
#include <QLocale> #include <QLocale>
#include <QMessageBox> #include <QMessageBox>
#include <QProcess> #include <QProcess>
#include <QSettings> #include <QSettings>
#include <QStringBuilder>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
@ -491,10 +493,23 @@ void BitcoinApplication::shutdownResult()
void BitcoinApplication::handleRunawayException(const QString &message) void BitcoinApplication::handleRunawayException(const QString &message)
{ {
QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("<br><br>") + message); QMessageBox::critical(
nullptr, tr("Runaway exception"),
tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) %
QLatin1String("<br><br>") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
::exit(EXIT_FAILURE); ::exit(EXIT_FAILURE);
} }
void BitcoinApplication::handleNonFatalException(const QString& message)
{
assert(QThread::currentThread() == thread());
QMessageBox::warning(
nullptr, tr("Internal error"),
tr("An internal error occurred. %1 will attempt to continue safely. This is "
"an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) %
QLatin1String("<br><br>") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
}
WId BitcoinApplication::getMainWinId() const WId BitcoinApplication::getMainWinId() const
{ {
if (!window) if (!window)

View File

@ -96,6 +96,12 @@ public Q_SLOTS:
/// Handle runaway exceptions. Shows a message box with the problem and quits the program. /// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message); void handleRunawayException(const QString &message);
/**
* A helper function that shows a message box
* with details about a non-fatal exception.
*/
void handleNonFatalException(const QString& message);
Q_SIGNALS: Q_SIGNALS:
void requestedInitialize(); void requestedInitialize();
void requestedRestart(QStringList args); void requestedRestart(QStringList args);

View File

@ -891,7 +891,7 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
m_open_wallet_action->setEnabled(true); m_open_wallet_action->setEnabled(true);
m_open_wallet_action->setMenu(m_open_wallet_menu); m_open_wallet_action->setMenu(m_open_wallet_menu);
connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet); GUIUtil::ExceptionSafeConnect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet); connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
for (WalletModel* wallet_model : m_wallet_controller->getOpenWallets()) { for (WalletModel* wallet_model : m_wallet_controller->getOpenWallets()) {

View File

@ -52,6 +52,7 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QJsonObject> #include <QJsonObject>
#include <QKeyEvent> #include <QKeyEvent>
#include <QLatin1String>
#include <QLineEdit> #include <QLineEdit>
#include <QList> #include <QList>
#include <QLocale> #include <QLocale>
@ -65,6 +66,7 @@
#include <QShortcut> #include <QShortcut>
#include <QSize> #include <QSize>
#include <QString> #include <QString>
#include <QStringBuilder>
#include <QTextDocument> // for Qt::mightBeRichText #include <QTextDocument> // for Qt::mightBeRichText
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
@ -1874,4 +1876,22 @@ QImage GetImage(const QLabel* label)
#endif #endif
} }
QString MakeHtmlLink(const QString& source, const QString& link)
{
return QString(source).replace(
link,
QLatin1String("<a href=\"") % link % QLatin1String("\">") % link % QLatin1String("</a>"));
}
void PrintSlotException(
const std::exception* exception,
const QObject* sender,
const QObject* receiver)
{
std::string description = sender->metaObject()->className();
description += "->";
description += receiver->metaObject()->className();
PrintExceptionContinue(std::make_exception_ptr(exception), description.c_str());
}
} // namespace GUIUtil } // namespace GUIUtil

View File

@ -9,18 +9,23 @@
#include <fs.h> #include <fs.h>
#include <qt/guiconstants.h> #include <qt/guiconstants.h>
#include <netaddress.h> #include <netaddress.h>
#include <util/check.h>
#include <QApplication>
#include <QEvent> #include <QEvent>
#include <QHeaderView> #include <QHeaderView>
#include <QItemDelegate> #include <QItemDelegate>
#include <QMessageBox> #include <QMessageBox>
#include <QMetaObject>
#include <QObject> #include <QObject>
#include <QProgressBar> #include <QProgressBar>
#include <QString> #include <QString>
#include <QTableView> #include <QTableView>
#include <QLabel> #include <QLabel>
#include <cassert>
#include <chrono> #include <chrono>
#include <utility>
class QValidatedLineEdit; class QValidatedLineEdit;
class OptionsModel; class OptionsModel;
@ -520,6 +525,58 @@ namespace GUIUtil
QObject::connect(&source, &QObject::destroyed, object, std::forward<Fn>(function), connection); QObject::connect(&source, &QObject::destroyed, object, std::forward<Fn>(function), connection);
} }
/**
* Replaces a plain text link with an HTML tagged one.
*/
QString MakeHtmlLink(const QString& source, const QString& link);
void PrintSlotException(
const std::exception* exception,
const QObject* sender,
const QObject* receiver);
/**
* A drop-in replacement of QObject::connect function
* (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that
* guaranties that all exceptions are handled within the slot.
*
* NOTE: This function is incompatible with Qt private signals.
*/
template <typename Sender, typename Signal, typename Receiver, typename Slot>
auto ExceptionSafeConnect(
Sender sender, Signal signal, Receiver receiver, Slot method,
Qt::ConnectionType type = Qt::AutoConnection)
{
return QObject::connect(
sender, signal, receiver,
[sender, receiver, method](auto&&... args) {
bool ok{true};
try {
(receiver->*method)(std::forward<decltype(args)>(args)...);
} catch (const NonFatalCheckError& e) {
PrintSlotException(&e, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleNonFatalException",
blockingGUIThreadConnection(),
Q_ARG(QString, QString::fromStdString(e.what())));
} catch (const std::exception& e) {
PrintSlotException(&e, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleRunawayException",
blockingGUIThreadConnection(),
Q_ARG(QString, QString::fromStdString(e.what())));
} catch (...) {
PrintSlotException(nullptr, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleRunawayException",
blockingGUIThreadConnection(),
Q_ARG(QString, "Unknown failure occurred."));
}
assert(ok);
},
type);
}
} // namespace GUIUtil } // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H #endif // BITCOIN_QT_GUIUTIL_H

View File

@ -153,6 +153,8 @@ SendCoinsDialog::SendCoinsDialog(bool _fCoinJoin, QWidget* parent) :
} }
m_coin_control->UseCoinJoin(_fCoinJoin); m_coin_control->UseCoinJoin(_fCoinJoin);
GUIUtil::ExceptionSafeConnect(ui->sendButton, &QPushButton::clicked, this, &SendCoinsDialog::sendButtonClicked);
} }
void SendCoinsDialog::setClientModel(ClientModel *_clientModel) void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
@ -459,7 +461,7 @@ bool SendCoinsDialog::send(const QList<SendCoinsRecipient>& recipients, QString&
return true; return true;
} }
void SendCoinsDialog::on_sendButton_clicked() void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
{ {
if(!model || !model->getOptionsModel()) if(!model || !model->getOptionsModel())
return; return;

View File

@ -83,7 +83,7 @@ private:
void updateCoinControlState(CCoinControl& ctrl); void updateCoinControlState(CCoinControl& ctrl);
private Q_SLOTS: private Q_SLOTS:
void on_sendButton_clicked(); void sendButtonClicked(bool checked);
void on_buttonChooseFee_clicked(); void on_buttonChooseFee_clicked();
void on_buttonMinimizeFee_clicked(); void on_buttonMinimizeFee_clicked();
void removeEntry(SendCoinsEntry* entry); void removeEntry(SendCoinsEntry* entry);

View File

@ -68,7 +68,7 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe
if (status == CT_NEW) txid = hash; if (status == CT_NEW) txid = hash;
})); }));
ConfirmSend(); ConfirmSend();
bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked"); bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "sendButtonClicked", Q_ARG(bool, false));
assert(invoked); assert(invoked);
return txid; return txid;
} }

View File

@ -12,6 +12,7 @@
#include <qt/addresstablemodel.h> #include <qt/addresstablemodel.h>
#include <qt/clientmodel.h> #include <qt/clientmodel.h>
#include <qt/guiconstants.h> #include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h> #include <qt/optionsmodel.h>
#include <qt/paymentserver.h> #include <qt/paymentserver.h>
#include <qt/recentrequeststablemodel.h> #include <qt/recentrequeststablemodel.h>
@ -68,7 +69,10 @@ WalletModel::~WalletModel()
void WalletModel::startPollBalance() void WalletModel::startPollBalance()
{ {
// This timer will be fired repeatedly to update the balance // This timer will be fired repeatedly to update the balance
connect(timer, &QTimer::timeout, this, &WalletModel::pollBalanceChanged); // Since the QTimer::timeout is a private signal, it cannot be used
// in the GUIUtil::ExceptionSafeConnect directly.
connect(timer, &QTimer::timeout, this, &WalletModel::timerTimeout);
GUIUtil::ExceptionSafeConnect(this, &WalletModel::timerTimeout, this, &WalletModel::pollBalanceChanged);
timer->start(MODEL_UPDATE_DELAY); timer->start(MODEL_UPDATE_DELAY);
} }

View File

@ -233,6 +233,8 @@ Q_SIGNALS:
// Notify that there are now keys in the keypool // Notify that there are now keys in the keypool
void canGetAddressesChanged(); void canGetAddressesChanged();
void timerTimeout();
public Q_SLOTS: public Q_SLOTS:
/* Starts a timer to periodically update the balance */ /* Starts a timer to periodically update the balance */
void startPollBalance(); void startPollBalance();

View File

@ -1366,15 +1366,15 @@ static RPCHelpMan gettxoutsetinfo()
RPCResult{ RPCResult{
RPCResult::Type::OBJ, "", "", RPCResult::Type::OBJ, "", "",
{ {
{RPCResult::Type::NUM, "height", "The current block height (index)"}, {RPCResult::Type::NUM, "height", "The block height (index) of the returned statistics"},
{RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"}, {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"}, {RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
{RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"}, {RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
{RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"}, {RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs (not available when coinstatsindex is used)"}, {RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"}, {RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"}, {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
{RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"}, {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
{RPCResult::Type::OBJ, "block_info", "Info on amounts in the block at this block height (only available if coinstatsindex is used)", {RPCResult::Type::OBJ, "block_info", "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
{ {

View File

@ -789,9 +789,11 @@ static RPCHelpMan listbanned()
{ {
{RPCResult::Type::OBJ, "", "", {RPCResult::Type::OBJ, "", "",
{ {
{RPCResult::Type::STR, "address", ""}, {RPCResult::Type::STR, "address", "The IP/Subnet of the banned node"},
{RPCResult::Type::NUM_TIME, "banned_until", ""}, {RPCResult::Type::NUM_TIME, "ban_created", "The " + UNIX_EPOCH_TIME + " the ban was created"},
{RPCResult::Type::NUM_TIME, "ban_created", ""}, {RPCResult::Type::NUM_TIME, "banned_until", "The " + UNIX_EPOCH_TIME + " the ban expires"},
{RPCResult::Type::NUM_TIME, "ban_duration", "The ban duration, in seconds"},
{RPCResult::Type::NUM_TIME, "time_remaining", "The time remaining until the ban expires, in seconds"},
}}, }},
}}, }},
RPCExamples{ RPCExamples{
@ -808,6 +810,7 @@ static RPCHelpMan listbanned()
banmap_t banMap; banmap_t banMap;
node.banman->GetBanned(banMap); node.banman->GetBanned(banMap);
const int64_t current_time{GetTime()};
UniValue bannedAddresses(UniValue::VARR); UniValue bannedAddresses(UniValue::VARR);
for (const auto& entry : banMap) for (const auto& entry : banMap)
@ -815,8 +818,10 @@ static RPCHelpMan listbanned()
const CBanEntry& banEntry = entry.second; const CBanEntry& banEntry = entry.second;
UniValue rec(UniValue::VOBJ); UniValue rec(UniValue::VOBJ);
rec.pushKV("address", entry.first.ToString()); rec.pushKV("address", entry.first.ToString());
rec.pushKV("banned_until", banEntry.nBanUntil);
rec.pushKV("ban_created", banEntry.nCreateTime); rec.pushKV("ban_created", banEntry.nCreateTime);
rec.pushKV("banned_until", banEntry.nBanUntil);
rec.pushKV("ban_duration", (banEntry.nBanUntil - banEntry.nCreateTime));
rec.pushKV("time_remaining", (banEntry.nBanUntil - current_time));
bannedAddresses.push_back(rec); bannedAddresses.push_back(rec);
} }

View File

@ -114,7 +114,6 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in disconnect BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in disconnect
} }
BOOST_CHECK(dummyNode1.fDisconnect == true); BOOST_CHECK(dummyNode1.fDisconnect == true);
SetMockTime(0);
peerLogic->FinalizeNode(dummyNode1); peerLogic->FinalizeNode(dummyNode1);
} }

View File

@ -46,6 +46,7 @@ FUZZ_TARGET(psbt)
(void)PSBTInputSigned(input); (void)PSBTInputSigned(input);
(void)input.IsNull(); (void)input.IsNull();
} }
(void)CountPSBTUnsignedInputs(psbt);
for (const PSBTOutput& output : psbt.outputs) { for (const PSBTOutput& output : psbt.outputs) {
(void)output.IsNull(); (void)output.IsNull();

View File

@ -14,7 +14,6 @@ BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(logging_timer) BOOST_AUTO_TEST_CASE(logging_timer)
{ {
SetMockTime(1); SetMockTime(1);
auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg"); auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
SetMockTime(2); SetMockTime(2);
@ -29,8 +28,6 @@ BOOST_AUTO_TEST_CASE(logging_timer)
auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg"); auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
SetMockTime(2); SetMockTime(2);
BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000μs)"); BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000μs)");
SetMockTime(0);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -571,8 +571,6 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0); BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
// ... unless it has gone all the way to 0 (after getting past 1000/2) // ... unless it has gone all the way to 0 (after getting past 1000/2)
SetMockTime(0);
} }
inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>()) inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>())

View File

@ -317,22 +317,29 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array(); ar = r.get_array();
o1 = ar[0].get_obj(); o1 = ar[0].get_obj();
adr = find_value(o1, "address"); adr = find_value(o1, "address");
UniValue banned_until = find_value(o1, "banned_until"); int64_t banned_until{find_value(o1, "banned_until").get_int64()};
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
BOOST_CHECK_EQUAL(banned_until.get_int64(), 9907731200); // absolute time check BOOST_CHECK_EQUAL(banned_until, 9907731200); // absolute time check
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned"))); BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));
auto now = 10'000s;
SetMockTime(now);
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 200"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 200")));
SetMockTime(now += 2s);
const int64_t time_remaining_expected{198};
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));
ar = r.get_array(); ar = r.get_array();
o1 = ar[0].get_obj(); o1 = ar[0].get_obj();
adr = find_value(o1, "address"); adr = find_value(o1, "address");
banned_until = find_value(o1, "banned_until"); banned_until = find_value(o1, "banned_until").get_int64();
const int64_t ban_created{find_value(o1, "ban_created").get_int64()};
const int64_t ban_duration{find_value(o1, "ban_duration").get_int64()};
const int64_t time_remaining{find_value(o1, "time_remaining").get_int64()};
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
int64_t now = GetTime(); BOOST_CHECK_EQUAL(banned_until, time_remaining_expected + now.count());
BOOST_CHECK(banned_until.get_int64() > now); BOOST_CHECK_EQUAL(ban_duration, banned_until - ban_created);
BOOST_CHECK(banned_until.get_int64()-now <= 200); BOOST_CHECK_EQUAL(time_remaining, time_remaining_expected);
// must throw an exception because 127.0.0.1 is in already banned subnet range // must throw an exception because 127.0.0.1 is in already banned subnet range
BOOST_CHECK_THROW(r = CallRPC(std::string("setban 127.0.0.1 add")), std::runtime_error); BOOST_CHECK_THROW(r = CallRPC(std::string("setban 127.0.0.1 add")), std::runtime_error);

View File

@ -196,6 +196,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
BasicTestingSetup::~BasicTestingSetup() BasicTestingSetup::~BasicTestingSetup()
{ {
SetMockTime(0s); // Reset mocktime for following tests
connman.reset(); connman.reset();
llmq::quorumSnapshotManager.reset(); llmq::quorumSnapshotManager.reset();
m_node.cpoolman.reset(); m_node.cpoolman.reset();
@ -500,7 +501,6 @@ TestChainSetup::~TestChainSetup()
g_txindex->Stop(); g_txindex->Stop();
SyncWithValidationInterfaceQueue(); SyncWithValidationInterfaceQueue();
g_txindex.reset(); g_txindex.reset();
SetMockTime(0);
} }
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const

View File

@ -315,8 +315,6 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
BOOST_CHECK_EQUAL(found, expected); BOOST_CHECK_EQUAL(found, expected);
} }
} }
SetMockTime(0);
} }
// Verify getaddressinfo RPC produces more or less expected results // Verify getaddressinfo RPC produces more or less expected results
@ -473,9 +471,6 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
// If there are future entries, new transaction should use time of the // If there are future entries, new transaction should use time of the
// newest entry that is no more than 300 seconds ahead of the clock time. // newest entry that is no more than 300 seconds ahead of the clock time.
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 5, 50, 600), 300); BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 5, 50, 600), 300);
// Reset mock time for other tests.
SetMockTime(0);
} }
BOOST_AUTO_TEST_CASE(LoadReceiveRequests) BOOST_AUTO_TEST_CASE(LoadReceiveRequests)

View File

@ -0,0 +1,85 @@
#!/usr/bin/env python3
# Copyright (c) 2020 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test Anchors functionality"""
import os
from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
def check_node_connections(*, node, num_in, num_out):
info = node.getnetworkinfo()
assert_equal(info["connections_in"], num_in)
assert_equal(info["connections_out"], num_out)
class AnchorsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
def setup_network(self):
self.setup_nodes()
def run_test(self):
self.log.info("Add 2 block-relay-only connections to node 0")
for i in range(2):
self.log.debug(f"block-relay-only: {i}")
self.nodes[0].add_outbound_p2p_connection(
P2PInterface(), p2p_idx=i, connection_type="block-relay-only"
)
self.log.info("Add 5 inbound connections to node 0")
for i in range(5):
self.log.debug(f"inbound: {i}")
self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info("Check node 0 connections")
check_node_connections(node=self.nodes[0], num_in=5, num_out=2)
# 127.0.0.1
ip = "7f000001"
# Since the ip is always 127.0.0.1 for this case,
# we store only the port to identify the peers
block_relay_nodes_port = []
inbound_nodes_port = []
for p in self.nodes[0].getpeerinfo():
addr_split = p["addr"].split(":")
if p["connection_type"] == "block-relay-only":
block_relay_nodes_port.append(hex(int(addr_split[1]))[2:])
else:
inbound_nodes_port.append(hex(int(addr_split[1]))[2:])
self.log.info("Stop node 0")
self.stop_node(0)
node0_anchors_path = os.path.join(
self.nodes[0].datadir, "regtest", "anchors.dat"
)
# It should contain only the block-relay-only addresses
self.log.info("Check the addresses in anchors.dat")
with open(node0_anchors_path, "rb") as file_handler:
anchors = file_handler.read().hex()
for port in block_relay_nodes_port:
ip_port = ip + port
assert ip_port in anchors
for port in inbound_nodes_port:
ip_port = ip + port
assert ip_port not in anchors
self.log.info("Start node 0")
self.start_node(0)
self.log.info("When node starts, check if anchors.dat doesn't exist anymore")
assert not os.path.exists(node0_anchors_path)
if __name__ == "__main__":
AnchorsTest().main()

View File

@ -6,7 +6,6 @@
import struct import struct
from test_framework.blocktools import create_transaction
from test_framework.messages import ( from test_framework.messages import (
CBlock, CBlock,
COutPoint, COutPoint,
@ -15,38 +14,30 @@ from test_framework.messages import (
from test_framework.crypto.muhash import MuHash3072 from test_framework.crypto.muhash import MuHash3072
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
from test_framework.wallet import MiniWallet
class UTXOSetHashTest(BitcoinTestFramework): class UTXOSetHashTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 1 self.num_nodes = 1
self.setup_clean_chain = True self.setup_clean_chain = True
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def test_deterministic_hash_results(self):
self.log.info("Test deterministic UTXO set hash results")
# These depend on the setup_clean_chain option, the chain loaded from the cache
assert_equal(self.nodes[0].gettxoutsetinfo()['hash_serialized_2'], "b61ee2cb582d2f4f94493f3d480e9a59d064706e98a12be0f335a3eeadd5678a")
assert_equal(self.nodes[0].gettxoutsetinfo("muhash")['muhash'], "dd5ad2a105c2d29495f577245c357409002329b9f4d6182c0af3dc2f462555c8")
def test_muhash_implementation(self): def test_muhash_implementation(self):
self.log.info("Test MuHash implementation consistency") self.log.info("Test MuHash implementation consistency")
node = self.nodes[0] node = self.nodes[0]
wallet = MiniWallet(node)
mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1
node.setmocktime(mocktime)
# Generate 100 blocks and remove the first since we plan to spend its # Generate 100 blocks and remove the first since we plan to spend its
# coinbase # coinbase
block_hashes = node.generate(100) block_hashes = wallet.generate(1) + node.generate(99)
blocks = list(map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes)) blocks = list(map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes))
spending = blocks.pop(0) blocks.pop(0)
# Create a spending transaction and mine a block which includes it # Create a spending transaction and mine a block which includes it
tx = create_transaction(node, spending.vtx[0].rehash(), node.getnewaddress(), amount=49) txid = wallet.send_self_transfer(from_node=node)['txid']
txid = node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0) tx_block = node.generateblock(output=wallet.get_address(), transactions=[txid])['hash']
tx_block = node.generateblock(node.getnewaddress(), [txid])['hash']
blocks.append(from_hex(CBlock(), node.getblock(tx_block, False))) blocks.append(from_hex(CBlock(), node.getblock(tx_block, False)))
# Serialize the outputs that should be in the UTXO set and add them to # Serialize the outputs that should be in the UTXO set and add them to
@ -77,8 +68,11 @@ class UTXOSetHashTest(BitcoinTestFramework):
assert_equal(finalized[::-1].hex(), node_muhash) assert_equal(finalized[::-1].hex(), node_muhash)
self.log.info("Test deterministic UTXO set hash results")
assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "4eb23b9673b7472e33ebf6e6aefee849fe5bc5e598fd387c2f9222070cc2f711")
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "dd74d7f1fb047577915d490e82d063e843f7853ae7543c9980a28c67326e1a2c")
def run_test(self): def run_test(self):
self.test_deterministic_hash_results()
self.test_muhash_implementation() self.test_muhash_implementation()

View File

@ -39,6 +39,9 @@ class MiniWallet:
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']}) self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
return blocks return blocks
def get_address(self):
return self._address
def get_utxo(self, *, txid=''): def get_utxo(self, *, txid=''):
""" """
Returns a utxo and marks it as spent (pops it from the internal list) Returns a utxo and marks it as spent (pops it from the internal list)

View File

@ -318,6 +318,7 @@ BASE_SCRIPTS = [
'p2p_ping.py', 'p2p_ping.py',
'rpc_scantxoutset.py', 'rpc_scantxoutset.py',
'feature_logging.py', 'feature_logging.py',
'feature_anchors.py',
'feature_coinstatsindex.py', 'feature_coinstatsindex.py',
'wallet_orphanedreward.py', 'wallet_orphanedreward.py',
'p2p_node_network_limited.py', 'p2p_node_network_limited.py',

View File

@ -32,8 +32,9 @@ class WalletGroupTest(BitcoinTestFramework):
self.skip_if_no_wallet() self.skip_if_no_wallet()
def run_test(self): def run_test(self):
self.log.info("Setting up")
# Mine some coins # Mine some coins
self.nodes[0].generate(COINBASE_MATURITY + 10) self.nodes[0].generate(COINBASE_MATURITY + 1)
# Get some addresses from the two nodes # Get some addresses from the two nodes
addr1 = [self.nodes[1].getnewaddress() for _ in range(3)] addr1 = [self.nodes[1].getnewaddress() for _ in range(3)]
@ -51,6 +52,7 @@ class WalletGroupTest(BitcoinTestFramework):
# - node[1] should pick one 0.5 UTXO and leave the rest # - node[1] should pick one 0.5 UTXO and leave the rest
# - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a
# given address, and leave the rest # given address, and leave the rest
self.log.info("Test sending transactions picks one UTXO group and leaves the rest")
txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
tx1 = self.nodes[1].getrawtransaction(txid1, True) tx1 = self.nodes[1].getrawtransaction(txid1, True)
# txid1 should have 1 input and 2 outputs # txid1 should have 1 input and 2 outputs
@ -73,7 +75,7 @@ class WalletGroupTest(BitcoinTestFramework):
assert_approx(v[0], vexp=0.2, vspan=0.0001) assert_approx(v[0], vexp=0.2, vspan=0.0001)
assert_approx(v[1], vexp=1.3, vspan=0.0001) assert_approx(v[1], vexp=1.3, vspan=0.0001)
# Test 'avoid partial if warranted, even if disabled' self.log.info("Test avoiding partial spends if warranted, even if avoidpartialspends is disabled")
self.sync_all() self.sync_all()
self.nodes[0].generate(1) self.nodes[0].generate(1)
# Nodes 1-2 now have confirmed UTXOs (letters denote destinations): # Nodes 1-2 now have confirmed UTXOs (letters denote destinations):
@ -107,7 +109,7 @@ class WalletGroupTest(BitcoinTestFramework):
assert_equal(input_addrs[0], input_addrs[1]) assert_equal(input_addrs[0], input_addrs[1])
# Node 2 enforces avoidpartialspends so needs no checking here # Node 2 enforces avoidpartialspends so needs no checking here
# Test wallet option maxapsfee with Node 3 self.log.info("Test wallet option maxapsfee")
addr_aps = self.nodes[3].getnewaddress() addr_aps = self.nodes[3].getnewaddress()
self.nodes[0].sendtoaddress(addr_aps, 1.0) self.nodes[0].sendtoaddress(addr_aps, 1.0)
self.nodes[0].sendtoaddress(addr_aps, 1.0) self.nodes[0].sendtoaddress(addr_aps, 1.0)
@ -134,6 +136,7 @@ class WalletGroupTest(BitcoinTestFramework):
# Test wallet option maxapsfee with node 4, which sets maxapsfee # Test wallet option maxapsfee with node 4, which sets maxapsfee
# 1 sat higher, crossing the threshold from non-grouped to grouped. # 1 sat higher, crossing the threshold from non-grouped to grouped.
self.log.info("Test wallet option maxapsfee threshold from non-grouped to grouped")
addr_aps3 = self.nodes[4].getnewaddress() addr_aps3 = self.nodes[4].getnewaddress()
[self.nodes[0].sendtoaddress(addr_aps3, 1.0) for _ in range(5)] [self.nodes[0].sendtoaddress(addr_aps3, 1.0) for _ in range(5)]
self.nodes[0].generate(1) self.nodes[0].generate(1)
@ -150,8 +153,7 @@ class WalletGroupTest(BitcoinTestFramework):
self.sync_all() self.sync_all()
self.nodes[0].generate(1) self.nodes[0].generate(1)
# Fill node2's wallet with 10000 outputs corresponding to the same self.log.info("Fill a wallet with 10,000 outputs corresponding to the same scriptPubKey")
# scriptPubKey
for _ in range(5): for _ in range(5):
raw_tx = self.nodes[0].createrawtransaction([{"txid":"0"*64, "vout":0}], [{addr2[0]: 0.05}]) raw_tx = self.nodes[0].createrawtransaction([{"txid":"0"*64, "vout":0}], [{addr2[0]: 0.05}])
tx = tx_from_hex(raw_tx) tx = tx_from_hex(raw_tx)
@ -161,12 +163,12 @@ class WalletGroupTest(BitcoinTestFramework):
signed_tx = self.nodes[0].signrawtransactionwithwallet(funded_tx['hex']) signed_tx = self.nodes[0].signrawtransactionwithwallet(funded_tx['hex'])
self.nodes[0].sendrawtransaction(signed_tx['hex']) self.nodes[0].sendrawtransaction(signed_tx['hex'])
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all() self.sync_all()
# Check that we can create a transaction that only requires ~100 of our # Check that we can create a transaction that only requires ~100 of our
# utxos, without pulling in all outputs and creating a transaction that # utxos, without pulling in all outputs and creating a transaction that
# is way too big. # is way too big.
self.log.info("Test creating txn that only requires ~100 of our UTXOs without pulling in all outputs")
assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5) assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5)

View File

@ -55,7 +55,6 @@ IGNORED_WARNINGS=(
"src/test/checkqueue_tests.cpp:.* Struct 'UniqueCheck' has a constructor with 1 argument that is not explicit." "src/test/checkqueue_tests.cpp:.* Struct 'UniqueCheck' has a constructor with 1 argument that is not explicit."
"src/test/fuzz/util.h:.* Class 'FuzzedFileProvider' has a constructor with 1 argument that is not explicit." "src/test/fuzz/util.h:.* Class 'FuzzedFileProvider' has a constructor with 1 argument that is not explicit."
"src/test/fuzz/util.h:.* Class 'FuzzedAutoFileProvider' has a constructor with 1 argument that is not explicit." "src/test/fuzz/util.h:.* Class 'FuzzedAutoFileProvider' has a constructor with 1 argument that is not explicit."
"src/util/ref.h:.* Class 'Ref' has a constructor with 1 argument that is not explicit."
"src/wallet/db.h:.* Class 'BerkeleyEnvironment' has a constructor with 1 argument that is not explicit." "src/wallet/db.h:.* Class 'BerkeleyEnvironment' has a constructor with 1 argument that is not explicit."
) )