Merge pull request #4919 from kittywhiskers/bdb3

backport: bitcoin#15454, #16681, #16798, #16900, #18727, #19619, #20034, #20581, partial #17371, #18115 (berkeley db refactoring: part 3)
This commit is contained in:
PastaPastaPasta 2022-10-17 10:23:33 -05:00 committed by GitHub
commit 5260ebf525
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
122 changed files with 1042 additions and 736 deletions

View File

@ -0,0 +1,6 @@
Wallet
------
Dash Core will no longer create an unnamed `""` wallet by default when no wallet is specified on the command line or in the configuration files.
For backwards compatibility, if an unnamed `""` wallet already exists and would have been loaded previously, then it will still be loaded.
Users without an unnamed `""` wallet and without any other wallets to be loaded on startup will be prompted to either choose a wallet to load, or to create a new wallet.

View File

@ -0,0 +1,16 @@
Wallet
------
### Automatic wallet creation removed
Dash Core will no longer automatically create new wallets on startup. It will
load existing wallets specified by `-wallet` options on the command line or in
`dash.conf` or `settings.json` files. And by default it will also load a
top-level unnamed ("") wallet. However, if specified wallets don't exist,
Dash Core will now just log warnings instead of creating new wallets with
new keys and addresses like previous releases did.
New wallets can be created through the GUI (which has a more prominent create
wallet option), through the `dash-cli createwallet` or `dash-wallet
create` commands, or the `createwallet` RPC.

View File

@ -31,7 +31,7 @@ static void CoinSelection(benchmark::Bench& bench)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
const CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
const CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@ -63,7 +63,7 @@ static void CoinSelection(benchmark::Bench& bench)
typedef std::set<CInputCoin> CoinSet;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
static const CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
static const CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp

View File

@ -19,7 +19,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
CWallet wallet{chain.get(), WalletLocation(), CreateMockWalletDatabase()};
CWallet wallet{chain.get(), "", CreateMockWalletDatabase()};
{
bool first_run;
if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false);

View File

@ -1214,7 +1214,7 @@ std::set<BlockFilterType> g_enabled_filter_types;
std::terminate();
};
bool AppInitBasicSetup(ArgsManager& args)
bool AppInitBasicSetup(const ArgsManager& args)
{
// ********************************************************* Step 1: setup
#ifdef _MSC_VER

View File

@ -33,7 +33,7 @@ void InitParameterInteraction(ArgsManager& args);
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read.
*/
bool AppInitBasicSetup(ArgsManager& args);
bool AppInitBasicSetup(const ArgsManager& args);
/**
* Initialization: parameter interaction.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.

View File

@ -33,7 +33,6 @@ class proxyType;
struct bilingual_str;
enum class SynchronizationState;
struct CNodeStateStats;
enum class WalletCreationStatus;
struct NodeContext;
namespace interfaces {

View File

@ -75,7 +75,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
}
//! Construct wallet tx status struct.
WalletTxStatus MakeWalletTxStatus(CWallet& wallet, const CWalletTx& wtx)
WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
{
WalletTxStatus result;
result.block_height = wallet.chain().getBlockHeight(wtx.m_confirm.hashBlock).value_or(std::numeric_limits<int>::max());
@ -94,7 +94,7 @@ WalletTxStatus MakeWalletTxStatus(CWallet& wallet, const CWalletTx& wtx)
}
//! Construct wallet TxOut struct.
WalletTxOut MakeWalletTxOut(CWallet& wallet,
WalletTxOut MakeWalletTxOut(const CWallet& wallet,
const CWalletTx& wtx,
int n,
int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@ -582,8 +582,7 @@ public:
class WalletClientImpl : public WalletClient
{
public:
WalletClientImpl(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
: m_wallet_filenames(std::move(wallet_filenames))
WalletClientImpl(Chain& chain, ArgsManager& args)
{
m_context.chain = &chain;
m_context.args = &args;
@ -600,23 +599,30 @@ public:
m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
}
}
bool verify() override { return VerifyWallets(*m_context.chain, m_wallet_filenames); }
bool load() override { return LoadWallets(*m_context.chain, m_wallet_filenames); }
bool verify() override { return VerifyWallets(*m_context.chain); }
bool load() override { return LoadWallets(*m_context.chain); }
void start(CScheduler& scheduler) override { return StartWallets(scheduler, *Assert(m_context.args)); }
void flush() override { return FlushWallets(); }
void stop() override { return StopWallets(); }
void setMockTime(int64_t time) override { return SetMockTime(time); }
//! WalletClient methods
std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, WalletCreationStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) override
std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) override
{
std::shared_ptr<CWallet> wallet;
status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, true /* load_on_start */, error, warnings, wallet);
return MakeWallet(std::move(wallet));
DatabaseOptions options;
DatabaseStatus status;
options.require_create = true;
options.create_flags = wallet_creation_flags;
options.create_passphrase = passphrase;
return MakeWallet(CreateWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
}
std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
{
return MakeWallet(LoadWallet(*m_context.chain, WalletLocation(name), true /* load_on_start */, error, warnings));
DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
return MakeWallet(LoadWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
}
std::string getWalletDir() override
{
@ -653,9 +659,9 @@ public:
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<WalletImpl>(wallet) : nullptr; }
std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args)
{
return MakeUnique<WalletClientImpl>(chain, args, std::move(wallet_filenames));
return MakeUnique<WalletClientImpl>(chain, args);
}
} // namespace interfaces

View File

@ -29,7 +29,6 @@ class CKey;
class CWallet;
enum class FeeReason;
enum class TransactionError;
enum class WalletCreationStatus;
enum isminetype : unsigned int;
struct bilingual_str;
struct CRecipient;
@ -345,7 +344,7 @@ class WalletClient : public ChainClient
{
public:
//! Create new wallet.
virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, WalletCreationStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
//! Load existing wallet.
virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
@ -450,7 +449,7 @@ std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet);
//! Return implementation of ChainClient interface for a wallet client. This
//! function will be undefined in builds where ENABLE_WALLET is false.
std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames);
std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args);
} // namespace interfaces

View File

@ -145,7 +145,7 @@ bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_t
}
// The legacy hash serializes the hashBlock
static void PrepareHash(CHashWriter& ss, CCoinsStats& stats)
static void PrepareHash(CHashWriter& ss, const CCoinsStats& stats)
{
ss << stats.hashBlock;
}

View File

@ -870,6 +870,11 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
}
}
WalletController* BitcoinGUI::getWalletController()
{
return m_wallet_controller;
}
void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;

View File

@ -80,6 +80,7 @@ public:
void setClientModel(ClientModel *clientModel = nullptr, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
#ifdef ENABLE_WALLET
void setWalletController(WalletController* wallet_controller);
WalletController* getWalletController();
#endif
#ifdef ENABLE_WALLET

View File

@ -1455,7 +1455,7 @@ void updateFonts()
std::vector<QString> vecIgnoreClasses{
"QWidget", "QDialog", "QFrame", "QStackedWidget", "QDesktopWidget", "QDesktopScreenWidget",
"QTipLabel", "QMessageBox", "QMenu", "QComboBoxPrivateScroller", "QComboBoxPrivateContainer",
"QScrollBar", "QListView", "BitcoinGUI", "WalletView", "WalletFrame"
"QScrollBar", "QListView", "BitcoinGUI", "WalletView", "WalletFrame", "QVBoxLayout", "QGroupBox"
};
std::vector<QString> vecIgnoreObjects{
"messagesWidget"

View File

@ -59,7 +59,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
{
TestChain100Setup test;
node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);

View File

@ -109,7 +109,7 @@ void TestGUI(interfaces::Node& node)
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
AddWallet(wallet);
bool firstRun;
wallet->LoadWallet(firstRun);

View File

@ -225,10 +225,9 @@ void CreateWalletActivity::createWallet()
}
QTimer::singleShot(500, worker(), [this, name, flags] {
WalletCreationStatus status;
std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, status, m_error_message, m_warning_message);
std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
if (status == WalletCreationStatus::SUCCESS) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
QTimer::singleShot(500, this, &CreateWalletActivity::finish);
});

View File

@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/createwalletdialog.h>
#include <qt/walletcontroller.h>
#include <qt/walletframe.h>
#include <qt/walletmodel.h>
@ -12,8 +14,11 @@
#include <cassert>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
WalletFrame::WalletFrame(BitcoinGUI* _gui) :
QFrame(_gui),
@ -26,9 +31,25 @@ WalletFrame::WalletFrame(BitcoinGUI* _gui) :
walletFrameLayout->setContentsMargins(0,0,0,0);
walletFrameLayout->addWidget(walletStack);
QLabel *noWallet = new QLabel(tr("No wallet has been loaded."));
// hbox for no wallet
QGroupBox* no_wallet_group = new QGroupBox(walletStack);
QVBoxLayout* no_wallet_layout = new QVBoxLayout(no_wallet_group);
QLabel *noWallet = new QLabel(tr("No wallet has been loaded.\nGo to File > Open Wallet to load a wallet.\n- OR -"));
noWallet->setAlignment(Qt::AlignCenter);
walletStack->addWidget(noWallet);
no_wallet_layout->addWidget(noWallet, 0, Qt::AlignHCenter | Qt::AlignBottom);
// A button for create wallet dialog
QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
connect(create_wallet_button, &QPushButton::clicked, [this] {
auto activity = new CreateWalletActivity(gui->getWalletController(), this);
connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
no_wallet_group->setLayout(no_wallet_layout);
walletStack->addWidget(no_wallet_group);
masternodeListPage = new MasternodeList();
walletStack->addWidget(masternodeListPage);

View File

@ -782,7 +782,9 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
// Parse the prevtxs array
ParsePrevouts(request.params[2], &keystore, coins);
return SignTransaction(mtx, &keystore, coins, request.params[3]);
UniValue result(UniValue::VOBJ);
SignTransaction(mtx, &keystore, coins, request.params[3], result);
return result;
}
UniValue sendrawtransaction(const JSONRPCRequest& request)

View File

@ -194,55 +194,31 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
}
}
UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType)
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
{
int nHashType = ParseSighashString(hashType);
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Script verification errors
std::map<int, std::string> input_errors;
bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
}
void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, std::string>& input_errors, UniValue& result)
{
// Make errors UniValue
UniValue vErrors(UniValue::VARR);
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mtx);
// Sign what we can:
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
CTxIn& txin = mtx.vin[i];
auto coin = coins.find(txin.prevout);
if (coin == coins.end() || coin->second.IsSpent()) {
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
continue;
}
const CScript& prevPubKey = coin->second.out.scriptPubKey;
const CAmount& amount = coin->second.out.nValue;
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
} else {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
}
for (const auto& err_pair : input_errors) {
TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second);
}
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
result.pushKV("complete", fComplete);
result.pushKV("complete", complete);
if (!vErrors.empty()) {
if (result.exists("errors")) {
vErrors.push_backV(result["errors"].getValues());
}
result.pushKV("errors", vErrors);
}
return result;
}

View File

@ -6,6 +6,7 @@
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
#include <map>
#include <string>
class FillableSigningProvider;
class UniValue;
@ -21,9 +22,10 @@ class SigningProvider;
* @param keystore Temporary keystore containing signing keys
* @param coins Map of unspent outputs
* @param hashType The signature hash type
* @returns JSON object with details of signed transaction
* @param result JSON object where signed transaction results accumulate
*/
UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType);
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, std::string>& input_errors, UniValue& result);
/**
* Parse a prevtxs UniValue array and get the map of coins from it

View File

@ -56,7 +56,7 @@ public:
return setValid.contains(entry, erase);
}
void Set(uint256& entry)
void Set(const uint256& entry)
{
std::unique_lock<std::shared_mutex> lock(cs_sigcache);
setValid.insert(entry);

View File

@ -382,3 +382,42 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
}
return false;
}
bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, int nHashType, std::map<int, std::string>& input_errors)
{
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mtx);
// Sign what we can:
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
CTxIn& txin = mtx.vin[i];
auto coin = coins.find(txin.prevout);
if (coin == coins.end() || coin->second.IsSpent()) {
input_errors[i] = "Input not found or already spent";
continue;
}
const CScript& prevPubKey = coin->second.out.scriptPubKey;
const CAmount& amount = coin->second.out.nValue;
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
} else {
input_errors[i] = ScriptErrorString(serror);
}
}
}
return input_errors.empty();
}

View File

@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_SIGN_H
#define BITCOIN_SCRIPT_SIGN_H
#include <coins.h>
#include <hash.h>
#include <pubkey.h>
#include <script/interpreter.h>
@ -161,4 +162,7 @@ void UpdateInput(CTxIn& input, const SignatureData& data);
* Solvability is unrelated to whether we consider this output to be ours. */
bool IsSolvable(const SigningProvider& provider, const CScript& script);
/** Sign the CMutableTransaction */
bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* provider, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors);
#endif // BITCOIN_SCRIPT_SIGN_H

View File

@ -69,7 +69,7 @@ public:
}
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(CService& addr)
void SimConnFail(const CService& addr)
{
LOCK(cs);
int64_t nLastSuccess = 1;

View File

@ -70,7 +70,7 @@ public:
}
};
static CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
static CDataStream AddrmanToStream(const CAddrManSerializationMock& _addrman)
{
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
ssPeersIn << Params().MessageStart();

View File

@ -11,13 +11,13 @@
#include <stdexcept>
DebugLogHelper::DebugLogHelper(std::string message)
: m_message{std::move(message)}
DebugLogHelper::DebugLogHelper(std::string message, MatchFn match)
: m_message{std::move(message)}, m_match(std::move(match))
{
m_print_connection = LogInstance().PushBackCallback(
[this](const std::string& s) {
if (m_found) return;
m_found = s.find(m_message) != std::string::npos;
m_found = s.find(m_message) != std::string::npos && m_match(&s);
});
noui_test_redirect();
}
@ -26,7 +26,7 @@ void DebugLogHelper::check_found()
{
noui_reconnect();
LogInstance().DeleteCallback(m_print_connection);
if (!m_found) {
if (!m_found && m_match(nullptr)) {
throw std::runtime_error(strprintf("'%s' not found in debug log\n", m_message));
}
}

View File

@ -17,10 +17,22 @@ class DebugLogHelper
bool m_found{false};
std::list<std::function<void(const std::string&)>>::iterator m_print_connection;
//! Custom match checking function.
//!
//! Invoked with pointers to lines containing matching strings, and with
//! null if check_found() is called without any successful match.
//!
//! Can return true to enable default DebugLogHelper behavior of:
//! (1) ending search after first successful match, and
//! (2) raising an error in check_found if no match was found
//! Can return false to do the opposite in either case.
using MatchFn = std::function<bool(const std::string* line)>;
MatchFn m_match;
void check_found();
public:
DebugLogHelper(std::string message);
DebugLogHelper(std::string message, MatchFn match = [](const std::string*){ return true; });
~DebugLogHelper() { check_found(); }
};

View File

@ -1802,7 +1802,7 @@ BOOST_AUTO_TEST_CASE(test_Capitalize)
BOOST_CHECK_EQUAL(Capitalize("\x00\xfe\xff"), "\x00\xfe\xff");
}
static std::string SpanToStr(Span<const char>& span)
static std::string SpanToStr(const Span<const char>& span)
{
return std::string(span.begin(), span.end());
}

View File

@ -68,6 +68,7 @@
#include <malloc.h>
#endif
#include <boost/algorithm/string/replace.hpp>
#include <thread>
#include <univalue.h>
@ -1208,6 +1209,15 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
}
#endif
#ifndef WIN32
std::string ShellEscape(const std::string& arg)
{
std::string escaped = arg;
boost::replace_all(escaped, "'", "'\"'\"'");
return "'" + escaped + "'";
}
#endif
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand)
{

View File

@ -103,6 +103,9 @@ fs::path GetConfigFile(const std::string& confPath);
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
#ifndef WIN32
std::string ShellEscape(const std::string& arg);
#endif
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
#endif

View File

@ -52,18 +52,6 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
return memcmp(value, &rhs.value, sizeof(value)) == 0;
}
bool IsBDBWalletLoaded(const fs::path& wallet_path)
{
fs::path env_directory;
std::string database_filename;
SplitWalletPath(wallet_path, env_directory, database_filename);
LOCK(cs_db);
auto env = g_dbenvs.find(env_directory.string());
if (env == g_dbenvs.end()) return false;
auto database = env->second.lock();
return database && database->IsDatabaseLoaded(database_filename);
}
/**
* @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
* @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
@ -371,7 +359,6 @@ void BerkeleyDatabase::Open(const char* pszMode)
if (ret != 0) {
throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
}
m_file_path = (env->Directory() / strFile).string();
// Call CheckUniqueFileid on the containing BDB environment to
// avoid BDB data consistency bugs that happen when different data
@ -824,3 +811,35 @@ std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(const char* mode, boo
{
return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
}
bool ExistsBerkeleyDatabase(const fs::path& path)
{
fs::path env_directory;
std::string data_filename;
SplitWalletPath(path, env_directory, data_filename);
return IsBerkeleyBtree(env_directory / data_filename);
}
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
{
std::unique_ptr<BerkeleyDatabase> db;
{
LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
std::string data_filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(path, data_filename);
if (env->m_databases.count(data_filename)) {
error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).string()));
status = DatabaseStatus::FAILED_ALREADY_LOADED;
return nullptr;
}
db = MakeUnique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
}
if (options.verify && !db->Verify(error)) {
status = DatabaseStatus::FAILED_VERIFY;
return nullptr;
}
status = DatabaseStatus::SUCCESS;
return db;
}

View File

@ -63,7 +63,6 @@ public:
bool IsMock() const { return fMockDb; }
bool IsInitialized() const { return fDbEnvInit; }
bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
fs::path Directory() const { return strPath; }
bool Open(bilingual_str& error);
@ -87,8 +86,8 @@ public:
/** Get BerkeleyEnvironment and database filename given a wallet path. */
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
/** Return whether a wallet database is currently loaded. */
bool IsBDBWalletLoaded(const fs::path& wallet_path);
/** Check format of database file */
bool IsBerkeleyBtree(const fs::path& path);
class BerkeleyBatch;
@ -143,7 +142,10 @@ public:
void ReloadDbEnv() override;
/** Verifies the environment and database file */
bool Verify(bilingual_str& error) override;
bool Verify(bilingual_str& error);
/** Return path to main database filename */
std::string Filename() override { return (env->Directory() / strFile).string(); }
/**
* Pointer to shared database environment.
@ -224,4 +226,10 @@ public:
std::string BerkeleyDatabaseVersion();
//! Check if Berkeley database exists at specified path.
bool ExistsBerkeleyDatabase(const fs::path& path);
//! Return object giving access to Berkeley database at specified path.
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
#endif // BITCOIN_WALLET_BDB_H

View File

@ -21,11 +21,3 @@ void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::
database_filename = "wallet.dat";
}
}
fs::path WalletDataFilePath(const fs::path& wallet_path)
{
fs::path env_directory;
std::string database_filename;
SplitWalletPath(wallet_path, env_directory, database_filename);
return env_directory / database_filename;
}

View File

@ -9,6 +9,7 @@
#include <clientversion.h>
#include <fs.h>
#include <streams.h>
#include <support/allocators/secure.h>
#include <util/memory.h>
#include <atomic>
@ -17,8 +18,6 @@
struct bilingual_str;
/** Given a wallet directory path or legacy file path, return path to main data file in the wallet database. */
fs::path WalletDataFilePath(const fs::path& wallet_path);
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
/** RAII class that provides access to a WalletDatabase */
@ -141,16 +140,14 @@ public:
virtual void ReloadDbEnv() = 0;
/** Return path to main database file for logs and error messages. */
virtual std::string Filename() = 0;
std::atomic<unsigned int> nUpdateCounter;
unsigned int nLastSeen;
unsigned int nLastFlushed;
int64_t nLastWalletUpdate;
/** Verifies the environment and database file */
virtual bool Verify(bilingual_str& error) = 0;
std::string m_file_path;
/** Make a DatabaseBatch connected to this database */
virtual std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) = 0;
};
@ -191,8 +188,34 @@ public:
bool PeriodicFlush() override { return true; }
void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {}
bool Verify(bilingual_str& errorStr) override { return true; }
std::string Filename() override { return "dummy"; }
std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); }
};
enum class DatabaseFormat {
BERKELEY,
};
struct DatabaseOptions {
bool require_existing = false;
bool require_create = false;
uint64_t create_flags = 0;
SecureString create_passphrase;
bool verify = true;
};
enum class DatabaseStatus {
SUCCESS,
FAILED_BAD_PATH,
FAILED_BAD_FORMAT,
FAILED_ALREADY_LOADED,
FAILED_ALREADY_EXISTS,
FAILED_NOT_FOUND,
FAILED_CREATE,
FAILED_VERIFY,
FAILED_ENCRYPT,
};
std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
#endif // BITCOIN_WALLET_DB_H

View File

@ -58,12 +58,12 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
" (1 = start from wallet creation time, 2 = start from genesis block)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
argsman.AddArg("-wallet=<path>", "Specify wallet path to load at startup. Can be used multiple times to load multiple wallets. Path is to a directory containing wallet data and log files. If the path is not absolute, it is interpreted relative to <walletdir>. This only loads existing wallets and does not create new ones. For backwards compatibility this also accepts names of existing top-level data files in <walletdir>.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
argsman.AddArg("-walletbackupsdir=<dir>", "Specify full path to directory for automatic wallet backups (must exist)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
#if HAVE_SYSTEM
argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
@ -166,16 +166,7 @@ void WalletInit::Construct(NodeContext& node) const
LogPrintf("Wallet disabled!\n");
return;
}
// If there's no -wallet setting with a list of wallets to load, set it to
// load the default "" wallet.
if (!args.IsArgSet("wallet")) {
args.LockSettings([&](util::Settings& settings) {
util::SettingsValue wallets(util::SettingsValue::VARR);
wallets.push_back(""); // Default wallet name is ""
settings.rw_settings["wallet"] = wallets;
});
}
auto wallet_client = interfaces::MakeWalletClient(*node.chain, args, args.GetArgs("-wallet"));
auto wallet_client = interfaces::MakeWalletClient(*node.chain, args);
node.wallet_client = wallet_client.get();
node.chain_clients.emplace_back(std::move(wallet_client));
}

View File

@ -5,9 +5,10 @@
#include <wallet/load.h>
#include <net.h>
#include <coinjoin/client.h>
#include <coinjoin/options.h>
#include <fs.h>
#include <net.h>
#include <interfaces/chain.h>
#include <scheduler.h>
#include <util/string.h>
@ -18,7 +19,7 @@
#include <univalue.h>
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
bool VerifyWallets(interfaces::Chain& chain)
{
if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
@ -43,37 +44,67 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
chain.initMessage(_("Verifying wallet(s)...").translated);
// For backwards compatibility if an unnamed top level wallet exists in the
// wallets directory, include it in the default list of wallets to load.
if (!gArgs.IsArgSet("wallet")) {
DatabaseOptions options;
DatabaseStatus status;
bilingual_str error_string;
options.require_existing = true;
options.verify = false;
if (MakeWalletDatabase("", options, status, error_string)) {
gArgs.LockSettings([&](util::Settings& settings) {
util::SettingsValue wallets(util::SettingsValue::VARR);
wallets.push_back(""); // Default wallet name is ""
settings.rw_settings["wallet"] = wallets;
});
}
}
// Keep track of each wallet absolute path to detect duplicates.
std::set<fs::path> wallet_paths;
for (const auto& wallet_file : wallet_files) {
WalletLocation location(wallet_file);
for (const auto& wallet_file : gArgs.GetArgs("-wallet")) {
const fs::path path = fs::absolute(wallet_file, GetWalletDir());
if (!wallet_paths.insert(location.GetPath()).second) {
if (!wallet_paths.insert(path).second) {
chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
return false;
}
DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
options.verify = true;
bilingual_str error_string;
std::vector<bilingual_str> warnings;
bool verify_success = CWallet::Verify(chain, location, error_string, warnings);
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!verify_success) {
chain.initError(error_string);
return false;
if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
if (status == DatabaseStatus::FAILED_NOT_FOUND) {
chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s\n", error_string.original)));
} else {
chain.initError(error_string);
return false;
}
}
}
return true;
}
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
bool LoadWallets(interfaces::Chain& chain)
{
try {
for (const std::string& walletFile : wallet_files) {
for (const std::string& name : gArgs.GetArgs("-wallet")) {
DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
bilingual_str error_string;
std::vector<bilingual_str> warnings;
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile), error_string, warnings);
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error_string);
if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
continue;
}
std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(chain, name, std::move(database), options.create_flags, error_string, warnings) : nullptr;
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!pwallet) {
chain.initError(error_string);

View File

@ -18,10 +18,10 @@ class Chain;
} // namespace interfaces
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
bool VerifyWallets(interfaces::Chain& chain);
//! Load wallet databases.
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
bool LoadWallets(interfaces::Chain& chain);
//! Complete startup of wallets.
void StartWallets(CScheduler& scheduler, const ArgsManager& args);

View File

@ -2690,23 +2690,21 @@ static UniValue loadwallet(const JSONRPCRequest& request)
}.Check(request);
WalletContext& context = EnsureWalletContext(request.context);
WalletLocation location(request.params[0].get_str());
if (!location.Exists()) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
} else if (fs::is_directory(location.GetPath())) {
// The given filename is a directory. Check that there's a wallet.dat file.
fs::path wallet_dat_file = location.GetPath() / "wallet.dat";
if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
}
}
const std::string name(request.params[0].get_str());
DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
bilingual_str error;
std::vector<bilingual_str> warnings;
Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, location, load_on_start, error, warnings);
if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original);
std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, name, load_on_start, options, status, error, warnings);
if (!wallet) {
// Map bad format to not found, since bad format is returned when the
// wallet directory exists, but doesn't contain a data file.
RPCErrorCode code = status == DatabaseStatus::FAILED_NOT_FOUND || status == DatabaseStatus::FAILED_BAD_FORMAT ? RPC_WALLET_NOT_FOUND : RPC_WALLET_ERROR;
throw JSONRPCError(code, error.original);
}
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
@ -2831,18 +2829,17 @@ static UniValue createwallet(const JSONRPCRequest& request)
flags |= WALLET_FLAG_AVOID_REUSE;
}
DatabaseOptions options;
DatabaseStatus status;
options.require_create = true;
options.create_flags = flags;
options.create_passphrase = passphrase;
bilingual_str error;
std::shared_ptr<CWallet> wallet;
Optional<bool> load_on_start = request.params[5].isNull() ? nullopt : Optional<bool>(request.params[5].get_bool());
WalletCreationStatus status = CreateWallet(*context.chain, passphrase, flags, request.params[0].get_str(), load_on_start, error, warnings, wallet);
switch (status) {
case WalletCreationStatus::CREATION_FAILED:
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
case WalletCreationStatus::ENCRYPTION_FAILED:
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error.original);
case WalletCreationStatus::SUCCESS:
break;
// no default case, so the compiler can warn about missing cases
std::shared_ptr<CWallet> wallet = CreateWallet(*context.chain, request.params[0].get_str(), load_on_start, options, status, error, warnings);
if (!wallet) {
RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
throw JSONRPCError(code, error.original);
}
UniValue obj(UniValue::VOBJ);
@ -3377,7 +3374,9 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
// Parse the prevtxs array
ParsePrevouts(request.params[1], nullptr, coins);
return SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, request.params[2]);
UniValue result(UniValue::VOBJ);
SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, request.params[2], result);
return result;
}
static UniValue rescanblockchain(const JSONRPCRequest& request)

View File

@ -24,6 +24,13 @@ static bool KeyFilter(const std::string& type)
bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
options.verify = false;
std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
if (!database) return false;
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
@ -126,7 +133,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
NodeContext node;
auto chain = interfaces::MakeChain(node);
DbTxn* ptxn = env->TxnBegin();
CWallet dummyWallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet dummyWallet(chain.get(), "", CreateDummyWalletDatabase());
for (KeyValPair& row : salvagedData)
{
/* Filter for only private key type KV pairs to be added to the salvaged wallet */

View File

@ -131,7 +131,7 @@ public:
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
NodeContext node;
chain = interfaces::MakeChain(node);
wallet = MakeUnique<CWallet>(chain.get(), WalletLocation(), CreateMockWalletDatabase());
wallet = MakeUnique<CWallet>(chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
AddWallet(wallet);

View File

@ -32,7 +32,7 @@ typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);

View File

@ -10,7 +10,7 @@
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
m_wallet_client = MakeWalletClient(*m_chain, *Assert(m_node.args), {});
m_wallet_client = MakeWalletClient(*m_chain, *Assert(m_node.args));
std::string sep;
sep += fs::path::preferred_separator;

View File

@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
scriptPubKey = GetScriptForDestination(pubkeys[0].GetID());
@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
scriptPubKey = GetScriptForDestination(uncompressedPubkey.GetID());
@ -99,7 +99,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
CScript redeemScript = GetScriptForDestination(pubkeys[0].GetID());
@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid)
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
CScript redeemscript_inner = GetScriptForDestination(pubkeys[0].GetID());
@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1]));
@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -202,7 +202,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard
{
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
LOCK(keystore.cs_wallet);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));

View File

@ -7,7 +7,7 @@
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
m_wallet(m_chain.get(), WalletLocation(), CreateMockWalletDatabase())
m_wallet(m_chain.get(), "", CreateMockWalletDatabase())
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);

View File

@ -21,7 +21,7 @@ struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args), {});
std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args));
CWallet m_wallet;
std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
};

View File

@ -5,6 +5,7 @@
#include <wallet/wallet.h>
#include <iostream>
#include <future>
#include <memory>
#include <stdint.h>
#include <vector>
@ -14,6 +15,8 @@
#include <node/context.h>
#include <policy/policy.h>
#include <rpc/server.h>
#include <rpc/rawtransaction_util.h>
#include <test/util/logging.h>
#include <test/util/setup_common.h>
#include <util/ref.h>
#include <util/translation.h>
@ -31,6 +34,41 @@ extern UniValue getnewaddress(const JSONRPCRequest& request);
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
{
DatabaseOptions options;
DatabaseStatus status;
bilingual_str error;
std::vector<bilingual_str> warnings;
auto database = MakeWalletDatabase("", options, status, error);
auto wallet = CWallet::Create(chain, "", std::move(database), options.create_flags, error, warnings);
wallet->postInitProcess();
return wallet;
}
static void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
{
std::vector<bilingual_str> warnings;
SyncWithValidationInterfaceQueue();
wallet->m_chain_notifications_handler.reset();
RemoveWallet(wallet, nullopt, warnings);
UnloadWallet(std::move(wallet));
}
static CMutableTransaction TestSimpleSpend(const CTransaction& from, uint32_t index, const CKey& key, const CScript& pubkey)
{
CMutableTransaction mtx;
mtx.vout.push_back({from.vout[index].nValue - DEFAULT_TRANSACTION_MAXFEE, pubkey});
mtx.vin.push_back({CTxIn{from.GetHash(), index}});
FillableSigningProvider keystore;
keystore.AddKey(key);
std::map<COutPoint, Coin> coins;
coins[mtx.vin[0].prevout].out = from.vout[index];
std::map<int, std::string> input_errors;
BOOST_CHECK(SignTransaction(mtx, &keystore, coins, SIGHASH_ALL, input_errors));
return mtx;
}
static void AddKey(CWallet& wallet, const CKey& key)
{
auto spk_man = wallet.GetLegacyScriptPubKeyMan();
@ -52,7 +90,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions accommodates a null start block.
{
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -71,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -97,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -122,7 +160,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks.
{
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -161,7 +199,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
AddWallet(wallet);
UniValue keys;
keys.setArray();
@ -224,7 +262,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file.
{
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
auto spk_man = wallet->GetLegacyScriptPubKeyMan();
LOCK(wallet->cs_wallet);
AssertLockHeld(spk_man->cs_wallet);
@ -243,7 +281,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
util::Ref context;
JSONRPCRequest request(context);
@ -277,7 +315,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
NodeContext node;
auto chain = interfaces::MakeChain(node);
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
auto spk_man = wallet.GetLegacyScriptPubKeyMan();
CWalletTx wtx(&wallet, m_coinbase_txns.back());
@ -380,7 +418,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
{
LOCK(wallet->cs_wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -521,7 +559,7 @@ public:
CreateTransactionTestSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
AddWallet(wallet);
@ -1004,7 +1042,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
@ -1013,4 +1051,86 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
BOOST_CHECK(!wallet->GetNewDestination("", dest, error));
}
//! Test CreateWalletFromFile function and its behavior handling potential race
//! conditions if it's called the same time an incoming transaction shows up in
//! the mempool or a new block.
//!
//! It isn't possible for a unit test to totally verify there aren't race
//! conditions without hooking into the implementation more, so this test just
//! verifies that new transactions are detected during loading without any
//! notifications at all, to infer that timing of notifications shouldn't
//! matter. The test could be extended to cover other scenarios in the future.
BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
{
// Create new wallet with known key and unload it.
auto chain = interfaces::MakeChain(m_node);
auto wallet = TestLoadWallet(*chain);
CKey key;
key.MakeNewKey(true);
AddKey(*wallet, key);
TestUnloadWallet(std::move(wallet));
// Add log hook to detect AddToWallet events from rescans, blockConnected,
// and transactionAddedToMempool notifications
int addtx_count = 0;
DebugLogHelper addtx_counter("[default wallet] AddToWallet", [&](const std::string* s) {
if (s) ++addtx_count;
return false;
});
bool rescan_completed = false;
DebugLogHelper rescan_check("[default wallet] Rescan completed", [&](const std::string* s) {
if (s) {
// For now, just assert that cs_main is being held during the
// rescan, ensuring that a new block couldn't be connected
// that the wallet would miss. After
// https://github.com/bitcoin/bitcoin/pull/16426 when cs_main is no
// longer held, the test can be extended to append a new block here
// and check it's handled correctly.
// AssertLockHeld(::cs_main);
rescan_completed = true;
}
return false;
});
// Block the queue to prevent the wallet receiving blockConnected and
// transactionAddedToMempool notifications, and create block and mempool
// transactions paying to the wallet
std::promise<void> promise;
CallFunctionInValidationInterfaceQueue([&promise] {
promise.get_future().wait();
});
std::string error;
m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
BOOST_CHECK(chain->broadcastTransaction(MakeTransactionRef(mempool_tx), error, DEFAULT_TRANSACTION_MAXFEE, false));
// Reload wallet and make sure new transactions are detected despite events
// being blocked
wallet = TestLoadWallet(*chain);
BOOST_CHECK(rescan_completed);
BOOST_CHECK_EQUAL(addtx_count, 2);
unsigned int block_tx_time, mempool_tx_time;
{
LOCK(wallet->cs_wallet);
block_tx_time = wallet->mapWallet.at(block_tx.GetHash()).nTimeReceived;
mempool_tx_time = wallet->mapWallet.at(mempool_tx.GetHash()).nTimeReceived;
}
// Unblock notification queue and make sure stale blockConnected and
// transactionAddedToMempool events are processed
promise.set_value();
SyncWithValidationInterfaceQueue();
BOOST_CHECK_EQUAL(addtx_count, 4);
{
LOCK(wallet->cs_wallet);
BOOST_CHECK_EQUAL(block_tx_time, wallet->mapWallet.at(block_tx.GetHash()).nTimeReceived);
BOOST_CHECK_EQUAL(mempool_tx_time, wallet->mapWallet.at(mempool_tx.GetHash()).nTimeReceived);
}
TestUnloadWallet(std::move(wallet));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -37,6 +37,7 @@
#include <wallet/coincontrol.h>
#include <wallet/coinselection.h>
#include <wallet/fees.h>
#include <walletinitinterface.h>
#include <warnings.h>
#include <coinjoin/client.h>
@ -112,6 +113,7 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet)
if (i != vpwallets.end()) return false;
coinJoinClientManagers.emplace(std::make_pair(wallet->GetName(), std::make_shared<CCoinJoinClientManager>(*wallet)));
vpwallets.push_back(wallet);
g_wallet_init_interface.InitCoinJoinSettings();
return true;
}
@ -130,6 +132,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on
vpwallets.erase(i);
auto it = coinJoinClientManagers.find(wallet->GetName());
coinJoinClientManagers.erase(it);
g_wallet_init_interface.InitCoinJoinSettings();
// Write the wallet setting
UpdateWalletSetting(chain, name, load_on_start, warnings);
@ -214,15 +217,16 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
namespace {
std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const WalletLocation& location, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings)
std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
if (!CWallet::Verify(chain, location, error, warnings)) {
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return nullptr;
}
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings);
std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
return nullptr;
@ -231,7 +235,7 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const Wall
wallet->postInitProcess();
// Write the wallet setting
UpdateWalletSetting(chain, location.GetName(), load_on_start, warnings);
UpdateWalletSetting(chain, name, load_on_start, warnings);
return wallet;
} catch (const std::runtime_error& e) {
@ -241,20 +245,23 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const Wall
}
} // namespace
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings)
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(location.GetName()));
auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
if (!result.second) {
error = Untranslated("Wallet already being loading.");
return nullptr;
}
auto wallet = LoadWalletInternal(chain, location, load_on_start, error, warnings);
auto wallet = LoadWalletInternal(chain, name, load_on_start, options, status, error, warnings);
WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));
return wallet;
}
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
uint64_t wallet_creation_flags = options.create_flags;
const SecureString& passphrase = options.create_passphrase;
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
@ -263,43 +270,42 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
}
// Check the wallet file location
WalletLocation location(name);
if (location.Exists()) {
error = Untranslated(strprintf("Wallet %s already exists.", location.GetName()));
return WalletCreationStatus::CREATION_FAILED;
}
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
if (!CWallet::Verify(chain, location, error, warnings)) {
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED;
status = DatabaseStatus::FAILED_VERIFY;
return nullptr;
}
// Do not allow a passphrase when private keys are disabled
if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
return WalletCreationStatus::CREATION_FAILED;
status = DatabaseStatus::FAILED_CREATE;
return nullptr;
}
// Make the wallet
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings, wallet_creation_flags);
std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), wallet_creation_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED;
status = DatabaseStatus::FAILED_CREATE;
return nullptr;
}
// Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) {
error = Untranslated("Error: Wallet created but failed to encrypt.");
return WalletCreationStatus::ENCRYPTION_FAILED;
status = DatabaseStatus::FAILED_ENCRYPT;
return nullptr;
}
if (!create_blank) {
// Unlock the wallet
if (!wallet->Unlock(passphrase)) {
error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
return WalletCreationStatus::ENCRYPTION_FAILED;
status = DatabaseStatus::FAILED_ENCRYPT;
return nullptr;
}
// Set a HD chain for the wallet
@ -319,12 +325,12 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
}
AddWallet(wallet);
wallet->postInitProcess();
result = wallet;
// Write the wallet settings
UpdateWalletSetting(chain, name, load_on_start, warnings);
return WalletCreationStatus::SUCCESS;
status = DatabaseStatus::SUCCESS;
return wallet;
}
/** @defgroup mapWallet
@ -894,6 +900,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
if (!strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
#ifndef WIN32
// Substituting the wallet name isn't currently supported on windows
// because windows shell escaping has not been implemented yet:
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875
// A few ways it could be implemented in the future are described in:
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094
boost::replace_all(strCmd, "%w", ShellEscape(GetName()));
#endif
std::thread t(runCommand, strCmd);
t.detach(); // thread runs free
}
@ -4189,7 +4203,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
{
// Do some checking on wallet path. It should be either a:
//
@ -4197,50 +4211,26 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// 2. Path to an existing directory.
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
LOCK(cs_wallets);
const fs::path& wallet_path = location.GetPath();
const fs::path& wallet_path = fs::absolute(name, GetWalletDir());
fs::file_type path_type = fs::symlink_status(wallet_path).type();
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
(path_type == fs::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) {
(path_type == fs::regular_file && fs::path(name).filename() == name))) {
error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
location.GetName(), GetWalletDir()));
return false;
name, GetWalletDir()));
status = DatabaseStatus::FAILED_BAD_PATH;
return nullptr;
}
// Make sure that the wallet path doesn't clash with an existing wallet path
if (IsWalletLoaded(wallet_path)) {
error_string = Untranslated(strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName()));
return false;
}
// Keep same database environment instance across Verify/Recover calls below.
std::unique_ptr<WalletDatabase> database = CreateWalletDatabase(wallet_path);
try {
if (!database->Verify(error_string)) {
return false;
}
} catch (const fs::filesystem_error& e) {
error_string = Untranslated(strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e)));
return false;
}
// Let tempWallet hold the pointer to the corresponding wallet database.
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, std::move(database));
if (!tempWallet->AutoBackupWallet(wallet_path, error_string, warnings) && !error_string.original.empty()) {
return false;
}
return true;
return MakeDatabase(wallet_path, options, status, error_string);
}
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags)
std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
const std::string walletFile = WalletDataFilePath(location.GetPath()).string();
const std::string& walletFile = database->Filename();
chain.initMessage(_("Loading wallet...").translated);
@ -4248,7 +4238,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, location, CreateWalletDatabase(location.GetPath())), ReleaseWallet);
std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, name, std::move(database)), ReleaseWallet);
if (!walletInstance->AutoBackupWallet(walletFile, error, warnings) && !error.original.empty()) {
return nullptr;
}
AddWallet(walletInstance);
auto unload_wallet = [&](const bilingual_str& strError) {
RemoveWallet(walletInstance, nullopt);

View File

@ -56,16 +56,10 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start);
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
enum class WalletCreationStatus {
SUCCESS,
CREATION_FAILED,
ENCRYPTION_FAILED
};
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
//! -paytxfee default
constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
@ -741,8 +735,8 @@ private:
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
/** Wallet location which includes wallet name (see WalletLocation). */
WalletLocation m_location;
/** Wallet name: relative directory name or "" for default wallet. */
std::string m_name;
/** Internal database handle. */
std::unique_ptr<WalletDatabase> database;
@ -801,11 +795,9 @@ public:
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
const WalletLocation& GetLocation() const { return m_location; }
/** Get a name for this wallet for logging/debugging purposes.
*/
const std::string& GetName() const { return m_location.GetName(); }
const std::string& GetName() const { return m_name; }
// Map from governance object hash to governance object, they are added by gobject_prepare.
std::map<uint256, CGovernanceObject> m_gobjects;
@ -815,12 +807,12 @@ public:
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
CWallet(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database)
: fUseCrypto(false),
fDecryptionThoroughlyChecked(false),
fOnlyMixingAllowed(false),
m_chain(chain),
m_location(location),
m_name(name),
database(std::move(database))
{
}
@ -1185,11 +1177,8 @@ public:
/* Resend a transaction */
bool ResendTransaction(const uint256& hashTx);
//! Verify wallet naming and perform salvage on the wallet if required
static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags = 0);
static std::shared_ptr<CWallet> Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
/**
* Wallet post-init setup

View File

@ -17,6 +17,8 @@
#include <sync.h>
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
#include <wallet/bdb.h>
#include <wallet/wallet.h>
#include <validation.h>
@ -809,16 +811,41 @@ bool WalletBatch::TxnAbort()
return m_batch->TxnAbort();
}
bool IsWalletLoaded(const fs::path& wallet_path)
std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
{
return IsBDBWalletLoaded(wallet_path);
}
bool exists;
try {
exists = fs::symlink_status(path).type() != fs::file_not_found;
} catch (const fs::filesystem_error& e) {
error = Untranslated(strprintf("Failed to access database path '%s': %s", path.string(), fsbridge::get_filesystem_error_message(e)));
status = DatabaseStatus::FAILED_BAD_PATH;
return nullptr;
}
/** Return object for accessing database at specified path. */
std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path)
{
std::string filename;
return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
Optional<DatabaseFormat> format;
if (exists) {
if (ExistsBerkeleyDatabase(path)) {
format = DatabaseFormat::BERKELEY;
}
} else if (options.require_existing) {
error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string()));
status = DatabaseStatus::FAILED_NOT_FOUND;
return nullptr;
}
if (!format && options.require_existing) {
error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", path.string()));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
if (format && options.require_create) {
error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", path.string()));
status = DatabaseStatus::FAILED_ALREADY_EXISTS;
return nullptr;
}
return MakeBerkeleyDatabase(path, options, status, error);
}
/** Return object for accessing dummy database with no read/write capabilities. */

View File

@ -239,12 +239,6 @@ using KeyFilterFn = std::function<bool(const std::string&)>;
//! Unserialize a given Key-Value pair and load it into the wallet
bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr);
/** Return whether a wallet database is currently loaded. */
bool IsWalletLoaded(const fs::path& wallet_path);
/** Return object for accessing database at specified path. */
std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path);
/** Return object for accessing dummy database with no read/write capabilities. */
std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase();

View File

@ -21,21 +21,8 @@ static void WalletToolReleaseWallet(CWallet* wallet)
delete wallet;
}
static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path)
static void WalletCreate(CWallet* wallet_instance)
{
if (fs::exists(path)) {
tfm::format(std::cerr, "Error: File exists already\n");
return nullptr;
}
// dummy chain interface
std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
bool first_run = true;
DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
if (load_wallet_ret != DBErrors::LOAD_OK) {
tfm::format(std::cerr, "Error creating %s", name);
return nullptr;
}
wallet_instance->SetMinVersion(FEATURE_COMPRPUBKEY);
// generate a new HD seed
@ -45,18 +32,26 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
tfm::format(std::cout, "Topping up keypool...\n");
wallet_instance->TopUpKeyPool();
return wallet_instance;
}
static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path)
static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, bool create)
{
if (!fs::exists(path)) {
tfm::format(std::cerr, "Error: Wallet files does not exist\n");
DatabaseOptions options;
DatabaseStatus status;
if (create) {
options.require_create = true;
} else {
options.require_existing = true;
}
bilingual_str error;
std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
if (!database) {
tfm::format(std::cerr, "%s\n", error.original);
return nullptr;
}
// dummy chain interface
std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret;
try {
bool first_run;
@ -88,6 +83,8 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
}
}
if (create) WalletCreate(wallet_instance.get());
return wallet_instance;
}
@ -110,19 +107,14 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
fs::path path = fs::absolute(name, GetWalletDir());
if (command == "create") {
std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path);
std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ true);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
}
} else if (command == "info" || command == "salvage") {
if (!fs::exists(path)) {
tfm::format(std::cerr, "Error: no wallet file at %s\n", name);
return false;
}
if (command == "info") {
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ false);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();

View File

@ -9,8 +9,6 @@
namespace WalletTool {
std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path);
std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path);
void WalletShowInfo(CWallet* wallet_instance);
bool ExecuteWalletToolFunc(const std::string& command, const std::string& file);

View File

@ -29,7 +29,7 @@ fs::path GetWalletDir()
return path;
}
static bool IsBerkeleyBtree(const fs::path& path)
bool IsBerkeleyBtree(const fs::path& path)
{
if (!fs::exists(path)) return false;
@ -91,14 +91,3 @@ std::vector<fs::path> ListWalletDir()
return paths;
}
WalletLocation::WalletLocation(const std::string& name)
: m_name(name)
, m_path(fs::absolute(name, GetWalletDir()))
{
}
bool WalletLocation::Exists() const
{
return fs::symlink_status(m_path).type() != fs::file_not_found;
}

View File

@ -56,24 +56,4 @@ fs::path GetWalletDir();
//! Get wallets in wallet directory.
std::vector<fs::path> ListWalletDir();
//! The WalletLocation class provides wallet information.
class WalletLocation final
{
std::string m_name;
fs::path m_path;
public:
explicit WalletLocation() {}
explicit WalletLocation(const std::string& name);
//! Get wallet name.
const std::string& GetName() const { return m_name; }
//! Get wallet absolute path.
const fs::path& GetPath() const { return m_path; }
//! Return whether the wallet exists.
bool Exists() const;
};
#endif // BITCOIN_WALLET_WALLETUTIL_H

View File

@ -25,7 +25,6 @@ from test_framework.mininode import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
wait_until,
)
@ -116,7 +115,7 @@ class ExampleTest(BitcoinTestFramework):
# In this test, we're not connecting node2 to node0 or node1. Calls to
# sync_all() should not include node2, since we're not expecting it to
# sync.
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all(self.nodes[0:2])
# Use setup_nodes() to customize the node start behaviour (for example if
@ -184,7 +183,7 @@ class ExampleTest(BitcoinTestFramework):
self.nodes[1].waitforblockheight(11)
self.log.info("Connect node2 and node1")
connect_nodes(self.nodes[1], 2)
self.connect_nodes(1, 2)
self.log.info("Wait for node2 to receive all the blocks from node1")
self.sync_all()

View File

@ -11,7 +11,7 @@
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import wait_until, get_datadir_path, connect_nodes
from test_framework.util import wait_until, get_datadir_path
import os
class AbortNodeTest(BitcoinTestFramework):
@ -35,7 +35,7 @@ class AbortNodeTest(BitcoinTestFramework):
# attempt.
self.nodes[1].generate(3)
with self.nodes[0].assert_debug_log(["Failed to disconnect block"]):
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.nodes[1].generate(1)
# Check that node0 aborted

View File

@ -13,7 +13,7 @@ from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
from test_framework.script import CScript, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160
from test_framework.util import assert_equal, connect_nodes
from test_framework.util import assert_equal
class AddressIndexTest(BitcoinTestFramework):
@ -33,9 +33,9 @@ class AddressIndexTest(BitcoinTestFramework):
# Nodes 2/3 are used for testing
self.start_node(2, ["-addressindex"])
self.start_node(3, ["-addressindex"])
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
self.connect_nodes(0, 1)
self.connect_nodes(0, 2)
self.connect_nodes(0, 3)
self.sync_all()
self.import_deterministic_coinbase_privkeys()
@ -44,12 +44,12 @@ class AddressIndexTest(BitcoinTestFramework):
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(["-addressindex=0"], "You need to rebuild the database using -reindex to change -addressindex", match=ErrorMatch.PARTIAL_REGEX)
self.start_node(1, ["-addressindex=0", "-reindex"])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all()
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(["-addressindex"], "You need to rebuild the database using -reindex to change -addressindex", match=ErrorMatch.PARTIAL_REGEX)
self.start_node(1, ["-addressindex", "-reindex"])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all()
self.log.info("Mining blocks...")

View File

@ -14,6 +14,7 @@ class ConfArgsTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
self.supports_cli = False
self.wallet_names = []
def test_config_file_parser(self):
# Assume node is stopped
@ -147,19 +148,15 @@ class ConfArgsTest(BitcoinTestFramework):
# Create the directory and ensure the config file now works
os.mkdir(new_data_dir)
# Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
self.start_node(0, ['-conf='+conf_file])
self.stop_node(0)
assert os.path.exists(os.path.join(new_data_dir, self.chain, 'blocks'))
if self.is_wallet_compiled():
assert os.path.exists(os.path.join(new_data_dir, self.chain, 'wallets', 'w1'))
# Ensure command line argument overrides datadir in conf
os.mkdir(new_data_dir_2)
self.nodes[0].datadir = new_data_dir_2
self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file, '-wallet=w2'])
self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file])
assert os.path.exists(os.path.join(new_data_dir_2, self.chain, 'blocks'))
if self.is_wallet_compiled():
assert os.path.exists(os.path.join(new_data_dir_2, self.chain, 'wallets', 'w2'))
if __name__ == '__main__':

View File

@ -12,7 +12,7 @@ from decimal import Decimal
from test_framework.blocktools import create_block, create_coinbase, get_masternode_payment
from test_framework.messages import CCbTx, COIN, CTransaction, FromHex, ToHex, uint256_to_string
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, connect_nodes, force_finish_mnsync, get_bip9_status, p2p_port
from test_framework.util import assert_equal, force_finish_mnsync, get_bip9_status, p2p_port
class Masternode(object):
pass
@ -43,7 +43,7 @@ class DIP3Test(BitcoinTestFramework):
self.start_node(0, extra_args=self.extra_args)
for node in self.nodes[1:]:
if node is not None and node.process is not None:
connect_nodes(node, 0)
self.connect_nodes(node.index, 0)
def run_test(self):
self.log.info("funding controller node")
@ -269,7 +269,7 @@ class DIP3Test(BitcoinTestFramework):
self.start_node(mn.idx, extra_args = self.extra_args + ['-masternodeblsprivkey=%s' % mn.blsMnkey])
force_finish_mnsync(self.nodes[mn.idx])
mn.node = self.nodes[mn.idx]
connect_nodes(mn.node, 0)
self.connect_nodes(mn.idx, 0)
self.sync_all()
def spend_mn_collateral(self, mn, with_dummy_input_output=False):

View File

@ -13,7 +13,6 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_greater_than_or_equal,
connect_nodes,
satoshi_round,
)
@ -213,9 +212,9 @@ class EstimateFeeTest(BitcoinTestFramework):
# so the estimates would not be affected by the splitting transactions
self.start_node(1)
self.start_node(2)
connect_nodes(self.nodes[1], 0)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[2], 1)
self.connect_nodes(1, 0)
self.connect_nodes(0, 2)
self.connect_nodes(2, 1)
self.sync_all()

View File

@ -15,7 +15,7 @@ class FilelockTest(BitcoinTestFramework):
def setup_network(self):
self.add_nodes(self.num_nodes, extra_args=None)
self.nodes[0].start([])
self.nodes[0].start()
self.nodes[0].wait_for_rpc_connection()
def run_test(self):
@ -27,10 +27,11 @@ class FilelockTest(BitcoinTestFramework):
self.nodes[1].assert_start_raises_init_error(extra_args=['-datadir={}'.format(self.nodes[0].datadir), '-noserver'], expected_msg=expected_msg)
if self.is_wallet_compiled():
self.nodes[0].createwallet(self.default_wallet_name)
wallet_dir = os.path.join(datadir, 'wallets')
self.log.info("Check that we can't start a second dashd instance using the same wallet")
expected_msg = "Error: Error initializing wallet database environment"
self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-wallet=' + self.default_wallet_name, '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
if __name__ == '__main__':
FilelockTest().main()

View File

@ -13,7 +13,7 @@ Checks LLMQs based ChainLocks
import time
from test_framework.test_framework import DashTestFramework
from test_framework.util import connect_nodes, force_finish_mnsync, isolate_node, reconnect_isolated_node
from test_framework.util import force_finish_mnsync
class LLMQChainLocksTest(DashTestFramework):
@ -27,7 +27,7 @@ class LLMQChainLocksTest(DashTestFramework):
# Usually node0 is the one that does this, but in this test we isolate it multiple times
for i in range(len(self.nodes)):
if i != 1:
connect_nodes(self.nodes[i], 1)
self.connect_nodes(i, 1)
self.activate_dip8()
@ -53,24 +53,24 @@ class LLMQChainLocksTest(DashTestFramework):
assert block['chainlock']
self.log.info("Isolate node, mine on another, and reconnect")
isolate_node(self.nodes[0])
self.isolate_node(0)
node0_mining_addr = self.nodes[0].getnewaddress()
node0_tip = self.nodes[0].getbestblockhash()
self.nodes[1].generatetoaddress(5, node0_mining_addr)
self.wait_for_chainlocked_block(self.nodes[1], self.nodes[1].getbestblockhash())
assert self.nodes[0].getbestblockhash() == node0_tip
reconnect_isolated_node(self.nodes[0], 1)
self.reconnect_isolated_node(0, 1)
self.nodes[1].generatetoaddress(1, node0_mining_addr)
self.wait_for_chainlocked_block_all_nodes(self.nodes[1].getbestblockhash())
self.log.info("Isolate node, mine on both parts of the network, and reconnect")
isolate_node(self.nodes[0])
self.isolate_node(0)
bad_tip = self.nodes[0].generate(5)[-1]
self.nodes[1].generatetoaddress(1, node0_mining_addr)
good_tip = self.nodes[1].getbestblockhash()
self.wait_for_chainlocked_block(self.nodes[1], good_tip)
assert not self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]
reconnect_isolated_node(self.nodes[0], 1)
self.reconnect_isolated_node(0, 1)
self.nodes[1].generatetoaddress(1, node0_mining_addr)
self.wait_for_chainlocked_block_all_nodes(self.nodes[1].getbestblockhash())
assert self.nodes[0].getblock(self.nodes[0].getbestblockhash())["previousblockhash"] == good_tip
@ -90,7 +90,7 @@ class LLMQChainLocksTest(DashTestFramework):
self.log.info("Restart it so that it forgets all the chainlock messages from the past")
self.stop_node(0)
self.start_node(0)
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
assert self.nodes[0].getbestblockhash() == good_tip
self.nodes[0].invalidateblock(good_tip)
self.log.info("Now try to reorg the chain")
@ -129,7 +129,7 @@ class LLMQChainLocksTest(DashTestFramework):
self.log.info("Isolate a node and let it create some transactions which won't get IS locked")
force_finish_mnsync(self.nodes[0])
isolate_node(self.nodes[0])
self.isolate_node(0)
txs = []
for i in range(3):
txs.append(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1))
@ -155,7 +155,7 @@ class LLMQChainLocksTest(DashTestFramework):
# Enable network on first node again, which will cause the blocks to propagate and IS locks to happen retroactively
# for the mined TXs, which will then allow the network to create a CLSIG
self.log.info("Re-enable network on first node and wait for chainlock")
reconnect_isolated_node(self.nodes[0], 1)
self.reconnect_isolated_node(0, 1)
self.wait_for_chainlocked_block(self.nodes[0], self.nodes[0].getbestblockhash(), timeout=30)
def create_chained_txs(self, node, amount):

View File

@ -13,7 +13,7 @@ Checks intra quorum connections
import time
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_greater_than_or_equal, connect_nodes, Options, wait_until
from test_framework.util import assert_greater_than_or_equal, Options, wait_until
# Probes should age after this many seconds.
# NOTE: mine_quorum() can bump mocktime quite often internally so make sure this number is high enough.
@ -119,7 +119,7 @@ class LLMQConnections(DashTestFramework):
# Also re-connect non-masternode connections
for i in range(1, len(self.nodes)):
connect_nodes(self.nodes[i], 0)
self.connect_nodes(i, 0)
self.nodes[i].ping()
# wait for ping/pong so that we can be sure that spork propagation works
time.sleep(1) # needed to make sure we don't check before the ping is actually sent (fPingQueued might be true but SendMessages still not called)

View File

@ -6,7 +6,7 @@
import time
from test_framework.mininode import logger
from test_framework.test_framework import DashTestFramework
from test_framework.util import force_finish_mnsync, connect_nodes, wait_until
from test_framework.util import force_finish_mnsync, wait_until
'''
feature_llmq_data_recovery.py
@ -29,19 +29,19 @@ class QuorumDataRecoveryTest(DashTestFramework):
self.set_dash_llmq_test_params(4, 3)
def restart_mn(self, mn, reindex=False, qvvec_sync=[], qdata_recovery_enabled=True):
args = self.extra_args[mn.nodeIdx] + ['-masternodeblsprivkey=%s' % mn.keyOperator,
args = self.extra_args[mn.node.index] + ['-masternodeblsprivkey=%s' % mn.keyOperator,
'-llmq-data-recovery=%d' % qdata_recovery_enabled]
for llmq_sync in qvvec_sync:
args.append('-llmq-qvvec-sync=%s:%d' % (llmq_type_strings[llmq_sync[0]], llmq_sync[1]))
if reindex:
args.append('-reindex')
bb_hash = mn.node.getbestblockhash()
self.restart_node(mn.nodeIdx, args)
self.restart_node(mn.node.index, args)
wait_until(lambda: mn.node.getbestblockhash() == bb_hash)
else:
self.restart_node(mn.nodeIdx, args)
self.restart_node(mn.node.index, args)
force_finish_mnsync(mn.node)
connect_nodes(mn.node, 0)
self.connect_nodes(mn.node.index, 0)
if qdata_recovery_enabled:
# trigger recovery threads and wait for them to start
self.nodes[0].generate(1)

View File

@ -6,7 +6,7 @@ import time
from test_framework.messages import CTransaction, FromHex, hash256, ser_compact_size, ser_string
from test_framework.test_framework import DashTestFramework
from test_framework.util import wait_until, connect_nodes
from test_framework.util import wait_until
'''
feature_llmq_is_migration.py
@ -33,7 +33,7 @@ class LLMQISMigrationTest(DashTestFramework):
for i in range(len(self.nodes)):
if i != 1:
connect_nodes(self.nodes[i], 0)
self.connect_nodes(i, 0)
self.activate_dip8()

View File

@ -16,7 +16,7 @@ and by having a higher relay fee on nodes 4 and 5.
import time
from test_framework.test_framework import DashTestFramework
from test_framework.util import set_node_times, isolate_node, reconnect_isolated_node
from test_framework.util import set_node_times
class LLMQ_IS_RetroactiveSigning(DashTestFramework):
@ -69,13 +69,13 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
self.wait_for_chainlocked_block_all_nodes(block)
self.log.info("testing normal signing with partially known TX")
isolate_node(self.nodes[3])
self.isolate_node(3)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
# Make sure nodes 1 and 2 received the TX before we continue,
# otherwise it might announce the TX to node 3 when reconnecting
self.wait_for_tx(txid, self.nodes[1])
self.wait_for_tx(txid, self.nodes[2])
reconnect_isolated_node(self.nodes[3], 0)
self.reconnect_isolated_node(3, 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
self.wait_for_mnauth(self.nodes[3], 2)
@ -88,7 +88,7 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
self.wait_for_instantlock(txid, self.nodes[0])
self.log.info("testing retroactive signing with unknown TX")
isolate_node(self.nodes[3])
self.isolate_node(3)
rawtx = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1})
rawtx = self.nodes[0].fundrawtransaction(rawtx)['hex']
rawtx = self.nodes[0].signrawtransactionwithwallet(rawtx)['hex']
@ -96,18 +96,18 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
# Make node 3 consider the TX as safe
self.bump_mocktime(10 * 60 + 1)
block = self.nodes[3].generatetoaddress(1, self.nodes[0].getnewaddress())[0]
reconnect_isolated_node(self.nodes[3], 0)
self.reconnect_isolated_node(3, 0)
self.wait_for_chainlocked_block_all_nodes(block)
self.nodes[0].setmocktime(self.mocktime)
self.log.info("testing retroactive signing with partially known TX")
isolate_node(self.nodes[3])
self.isolate_node(3)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
# Make sure nodes 1 and 2 received the TX before we continue,
# otherwise it might announce the TX to node 3 when reconnecting
self.wait_for_tx(txid, self.nodes[1])
self.wait_for_tx(txid, self.nodes[2])
reconnect_isolated_node(self.nodes[3], 0)
self.reconnect_isolated_node(3, 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
self.wait_for_mnauth(self.nodes[3], 2)
@ -136,7 +136,7 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
def test_all_nodes_session_timeout(self, do_cycle_llmqs):
set_node_times(self.nodes, self.mocktime)
isolate_node(self.nodes[3])
self.isolate_node(3)
rawtx = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1})
rawtx = self.nodes[0].fundrawtransaction(rawtx)['hex']
rawtx = self.nodes[0].signrawtransactionwithwallet(rawtx)['hex']
@ -150,7 +150,7 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
# Make the signing session for the IS lock timeout on nodes 1-3
self.bump_mocktime(61)
time.sleep(2) # make sure Cleanup() is called
reconnect_isolated_node(self.nodes[3], 0)
self.reconnect_isolated_node(3, 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
self.wait_for_mnauth(self.nodes[3], 2)
@ -167,7 +167,7 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
def test_single_node_session_timeout(self, do_cycle_llmqs):
set_node_times(self.nodes, self.mocktime)
isolate_node(self.nodes[3])
self.isolate_node(3)
rawtx = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1})
rawtx = self.nodes[0].fundrawtransaction(rawtx)['hex']
rawtx = self.nodes[0].signrawtransactionwithwallet(rawtx)['hex']
@ -176,7 +176,7 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
# Make the signing session for the IS lock timeout on node 3
self.bump_mocktime(61)
time.sleep(2) # make sure Cleanup() is called
reconnect_isolated_node(self.nodes[3], 0)
self.reconnect_isolated_node(3, 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
self.wait_for_mnauth(self.nodes[3], 2)

View File

@ -17,7 +17,6 @@ from test_framework.mininode import P2PInterface
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
connect_nodes,
wait_until,
)
@ -67,7 +66,7 @@ class LLMQQuorumRotationTest(DashTestFramework):
for i in range(len(self.nodes)):
if i != 1:
connect_nodes(self.nodes[i], 0)
self.connect_nodes(i, 0)
self.activate_dip8()

View File

@ -13,7 +13,7 @@ Checks LLMQs signing sessions
from test_framework.messages import CSigShare, msg_qsigshare, uint256_to_string
from test_framework.mininode import P2PInterface
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, connect_nodes, force_finish_mnsync, hex_str_to_bytes, wait_until
from test_framework.util import assert_equal, assert_raises_rpc_error, force_finish_mnsync, hex_str_to_bytes, wait_until
class LLMQSigningTest(DashTestFramework):
@ -184,7 +184,7 @@ class LLMQSigningTest(DashTestFramework):
assert_sigs_nochange(False, False, False, 3)
# Need to re-connect so that it later gets the recovered sig
mn.node.setnetworkactive(True)
connect_nodes(mn.node, 0)
self.connect_nodes(mn.node.index, 0)
force_finish_mnsync(mn.node)
# Make sure intra-quorum connections were also restored
self.bump_mocktime(1) # need this to bypass quorum connection retry timeout

View File

@ -13,7 +13,7 @@ Checks simple PoSe system based on LLMQ commitments
import time
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_equal, connect_nodes, force_finish_mnsync, p2p_port, wait_until
from test_framework.util import assert_equal, force_finish_mnsync, p2p_port, wait_until
class LLMQSimplePoSeTest(DashTestFramework):
@ -71,18 +71,18 @@ class LLMQSimplePoSeTest(DashTestFramework):
def close_mn_port(self, mn):
self.stop_node(mn.node.index)
self.start_masternode(mn, ["-listen=0", "-nobind"])
connect_nodes(mn.node, 0)
self.connect_nodes(mn.node.index, 0)
# Make sure the to-be-banned node is still connected well via outbound connections
for mn2 in self.mninfo:
if mn2 is not mn:
connect_nodes(mn.node, mn2.node.index)
self.connect_nodes(mn.node.index, mn2.node.index)
self.reset_probe_timeouts()
return False, False
def force_old_mn_proto(self, mn):
self.stop_node(mn.node.index)
self.start_masternode(mn, ["-pushversion=70216"])
connect_nodes(mn.node, 0)
self.connect_nodes(mn.node.index, 0)
self.reset_probe_timeouts()
return False, True
@ -199,7 +199,7 @@ class LLMQSimplePoSeTest(DashTestFramework):
self.start_masternode(mn)
else:
mn.node.setnetworkactive(True)
connect_nodes(mn.node, 0)
self.connect_nodes(mn.node.index, 0)
self.sync_all()
# Isolate and re-connect all MNs (otherwise there might be open connections with no MNAUTH for MNs which were banned before)
@ -208,7 +208,7 @@ class LLMQSimplePoSeTest(DashTestFramework):
wait_until(lambda: mn.node.getconnectioncount() == 0)
mn.node.setnetworkactive(True)
force_finish_mnsync(mn.node)
connect_nodes(mn.node, 0)
self.connect_nodes(mn.node.index, 0)
def reset_probe_timeouts(self):
# Make sure all masternodes will reconnect/re-probe

View File

@ -18,7 +18,7 @@ only succeeds past a given node once its nMinimumChainWork has been exceeded.
import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import connect_nodes, assert_equal
from test_framework.util import assert_equal
# 2 hashes required per regtest block (with no difficulty adjustment)
REGTEST_WORK_PER_BLOCK = 2
@ -41,7 +41,7 @@ class MinimumChainWorkTest(BitcoinTestFramework):
# block relay to inbound peers.
self.setup_nodes()
for i in range(self.num_nodes-1):
connect_nodes(self.nodes[i+1], i)
self.connect_nodes(i+1, i)
def run_test(self):
# Start building a chain on node0. node2 shouldn't be able to sync until node1's

View File

@ -5,7 +5,7 @@
import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import connect_nodes, wait_until
from test_framework.util import wait_until
'''
feature_multikeysporks.py
@ -66,7 +66,7 @@ class MultiKeySporkTest(BitcoinTestFramework):
# connect nodes at start
for i in range(0, 5):
for j in range(i, 5):
connect_nodes(self.nodes[i], j)
self.connect_nodes(i, j)
def get_test_spork_value(self, node, spork_name):
self.bump_mocktime(5) # advance ProcessTick
@ -95,7 +95,7 @@ class MultiKeySporkTest(BitcoinTestFramework):
# restart again with corect_params, should resync spork parts from other nodes
self.restart_node(0, self.node0_extra_args)
for i in range(1, 5):
connect_nodes(self.nodes[0], i)
self.connect_nodes(0, i)
# third signer set spork value
self.nodes[2].sporkupdate(spork_name, 1)
@ -110,7 +110,7 @@ class MultiKeySporkTest(BitcoinTestFramework):
# restart again with corect_params, should resync sporks from other nodes
self.restart_node(0, self.node0_extra_args)
for i in range(1, 5):
connect_nodes(self.nodes[0], i)
self.connect_nodes(0, i)
wait_until(lambda: self.get_test_spork_value(self.nodes[0], spork_name) == 1, sleep=0.1, timeout=10)

View File

@ -10,9 +10,18 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
wait_until,
connect_nodes,
)
# Linux allow all characters other than \x00
# Windows disallow control characters (0-31) and /\?%:|"<>
FILE_CHAR_START = 32 if os.name == 'nt' else 1
FILE_CHAR_END = 128
FILE_CHAR_BLACKLIST = '/\\?%*:|"<>' if os.name == 'nt' else '/'
def notify_outputname(walletname, txid):
return txid if os.name == 'nt' else '{}_{}'.format(walletname, txid)
class NotificationsTest(BitcoinTestFramework):
def set_test_params(self):
@ -20,6 +29,7 @@ class NotificationsTest(BitcoinTestFramework):
self.setup_clean_chain = True
def setup_network(self):
self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHAR_BLACKLIST)
self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify")
self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify")
self.walletnotify_dir = os.path.join(self.options.tmpdir, "walletnotify")
@ -33,7 +43,8 @@ class NotificationsTest(BitcoinTestFramework):
"-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s'))],
["-blockversion=211",
"-rescan",
"-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, '%s'))]]
"-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s')))]]
self.wallet_names = [self.default_wallet_name, self.wallet]
super().setup_network()
def run_test(self):
@ -53,7 +64,7 @@ class NotificationsTest(BitcoinTestFramework):
wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count)))
assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
self.stop_node(1)
@ -63,12 +74,12 @@ class NotificationsTest(BitcoinTestFramework):
self.log.info("test -walletnotify after rescan")
# restart node to rescan to force wallet notifications
self.start_node(1)
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count)))
assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
# TODO: add test for `-alertnotify` large fork notifications

View File

@ -14,7 +14,7 @@ from test_framework.blocktools import create_coinbase
from test_framework.messages import CBlock, ToHex
from test_framework.script import CScript, OP_RETURN, OP_NOP
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, connect_nodes, disconnect_nodes, wait_until
from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, wait_until
# Rescans start at the earliest block up to 2 hours before a key timestamp, so
# the manual prune RPC avoids pruning blocks in the same window to be
@ -96,18 +96,17 @@ class PruneTest(BitcoinTestFramework):
self.prunedir = os.path.join(self.nodes[2].datadir, self.chain, 'blocks', '')
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 2)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[0], 4)
self.connect_nodes(0, 1)
self.connect_nodes(1, 2)
self.connect_nodes(0, 2)
self.connect_nodes(0, 3)
self.connect_nodes(0, 4)
self.sync_blocks(self.nodes[0:5])
def setup_nodes(self):
self.add_nodes(self.num_nodes, self.extra_args)
self.start_nodes()
for n in self.nodes:
n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase', rescan=False)
self.import_deterministic_coinbase_privkeys()
def create_big_chain(self):
# Start by creating some coinbases we can spend later
@ -143,8 +142,8 @@ class PruneTest(BitcoinTestFramework):
for j in range(12):
# Disconnect node 0 so it can mine a longer reorg chain without knowing about node 1's soon-to-be-stale chain
# Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects
disconnect_nodes(self.nodes[0], 1)
disconnect_nodes(self.nodes[0], 2)
self.disconnect_nodes(0, 1)
self.disconnect_nodes(0, 2)
# Mine 24 blocks in node 1
mine_large_blocks(self.nodes[1], 24)
@ -152,8 +151,8 @@ class PruneTest(BitcoinTestFramework):
mine_large_blocks(self.nodes[0], 25)
# Create connections in the order so both nodes can see the reorg at the same time
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
self.connect_nodes(0, 1)
self.connect_nodes(0, 2)
self.sync_blocks(self.nodes[0:3])
self.log.info("Usage can be over target because of high stale rate: %d" % calc_usage(self.prunedir))
@ -184,15 +183,15 @@ class PruneTest(BitcoinTestFramework):
# Mine one block to avoid automatic recovery from forks on restart
self.nodes[1].generate(1)
# Disconnect node1 and generate the new chain
disconnect_nodes(self.nodes[0], 1)
disconnect_nodes(self.nodes[1], 2)
self.disconnect_nodes(0, 1)
self.disconnect_nodes(1, 2)
self.log.info("Generating new longer chain of 300 more blocks")
self.nodes[1].generate(299)
self.log.info("Reconnect nodes")
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 2)
self.connect_nodes(0, 1)
self.connect_nodes(1, 2)
self.sync_blocks(self.nodes[0:3], timeout=120)
self.log.info("Verify height on node 2: %d" % self.nodes[2].getblockcount())
@ -343,7 +342,7 @@ class PruneTest(BitcoinTestFramework):
# check that wallet loads successfully when restarting a pruned node after IBD.
# this was reported to fail in #7494.
self.log.info("Syncing node 5 to test wallet")
connect_nodes(self.nodes[0], 5)
self.connect_nodes(0, 5)
nds = [self.nodes[0], self.nodes[5]]
self.sync_blocks(nds, wait=5, timeout=300)
self.stop_node(5, expected_stderr='Warning: You are starting with governance validation disabled. This is expected because you are running a pruned node.') # stop and start to trigger rescan

View File

@ -17,6 +17,7 @@ class SettingsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
self.wallet_names = []
def run_test(self):
node, = self.nodes

View File

@ -14,7 +14,7 @@ from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut
from test_framework.script import CScript, OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_HASH160
from test_framework.test_node import ErrorMatch
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, connect_nodes
from test_framework.util import assert_equal
class SpentIndexTest(BitcoinTestFramework):
@ -34,9 +34,9 @@ class SpentIndexTest(BitcoinTestFramework):
# Nodes 2/3 are used for testing
self.start_node(2, ["-spentindex"])
self.start_node(3, ["-spentindex", "-txindex"])
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
self.connect_nodes(0, 1)
self.connect_nodes(0, 2)
self.connect_nodes(0, 3)
self.sync_all()
self.import_deterministic_coinbase_privkeys()
@ -45,12 +45,12 @@ class SpentIndexTest(BitcoinTestFramework):
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(["-spentindex=0"], "You need to rebuild the database using -reindex to change -spentindex", match=ErrorMatch.PARTIAL_REGEX)
self.start_node(1, ["-spentindex=0", "-reindex"])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all()
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(["-spentindex"], "You need to rebuild the database using -reindex to change -spentindex", match=ErrorMatch.PARTIAL_REGEX)
self.start_node(1, ["-spentindex", "-reindex"])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all()
self.log.info("Mining blocks...")

View File

@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import connect_nodes, wait_until
from test_framework.util import wait_until
'''
'''
@ -19,7 +19,7 @@ class SporkTest(BitcoinTestFramework):
self.disable_mocktime()
self.setup_nodes()
# connect only 2 first nodes at start
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
def get_test_spork_state(self, node):
info = node.spork('active')
@ -57,7 +57,7 @@ class SporkTest(BitcoinTestFramework):
self.nodes[1].generate(1)
# connect new node and check spork propagation after restoring from cache
connect_nodes(self.nodes[1], 2)
self.connect_nodes(1, 2)
wait_until(lambda: self.get_test_spork_state(self.nodes[2]), sleep=0.1, timeout=10)
if __name__ == '__main__':

View File

@ -9,7 +9,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
from test_framework.util import assert_equal, connect_nodes
from test_framework.util import assert_equal
class TimestampIndexTest(BitcoinTestFramework):
@ -26,9 +26,9 @@ class TimestampIndexTest(BitcoinTestFramework):
# Nodes 2/3 are used for testing
self.start_node(2)
self.start_node(3, ["-timestampindex"])
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
self.connect_nodes(0, 1)
self.connect_nodes(0, 2)
self.connect_nodes(0, 3)
self.sync_all()
@ -37,12 +37,12 @@ class TimestampIndexTest(BitcoinTestFramework):
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(["-timestampindex=0"], "You need to rebuild the database using -reindex to change -timestampindex", match=ErrorMatch.PARTIAL_REGEX)
self.start_node(1, ["-timestampindex=0", "-reindex"])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all()
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(["-timestampindex"], "You need to rebuild the database using -reindex to change -timestampindex", match=ErrorMatch.PARTIAL_REGEX)
self.start_node(1, ["-timestampindex", "-reindex"])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all()
self.log.info("Mining 5 blocks...")

View File

@ -12,7 +12,7 @@ import binascii
from test_framework.messages import COutPoint, CTransaction, CTxIn, CTxOut
from test_framework.script import CScript, OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_HASH160
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, connect_nodes
from test_framework.util import assert_equal
class TxIndexTest(BitcoinTestFramework):
@ -32,9 +32,9 @@ class TxIndexTest(BitcoinTestFramework):
# Nodes 2/3 are used for testing
self.start_node(2, ["-txindex"])
self.start_node(3, ["-txindex"])
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
self.connect_nodes(0, 1)
self.connect_nodes(0, 2)
self.connect_nodes(0, 3)
self.sync_all()
self.import_deterministic_coinbase_privkeys()

View File

@ -42,7 +42,7 @@ from test_framework.test_framework import BitcoinTestFramework
# from test_framework.mininode import P2PTxInvStore
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal, assert_raises_rpc_error, connect_nodes, disconnect_nodes,
assert_greater_than_or_equal, assert_raises_rpc_error,
)
@ -79,9 +79,9 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time)
# disconnect nodes & make a txn that remains in the unbroadcast set.
disconnect_nodes(self.nodes[0], 2)
self.disconnect_nodes(0, 2)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("12"))
connect_nodes(self.nodes[0], 2)
self.connect_nodes(0, 2)
self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
self.stop_nodes()
@ -153,7 +153,7 @@ class MempoolPersistTest(BitcoinTestFramework):
node0.generate(1)
# disconnect nodes to make a txn that remains in the unbroadcast set.
disconnect_nodes(node0, 1)
self.disconnect_nodes(0, 1)
node0.sendtoaddress(self.nodes[1].getnewaddress(), Decimal("12"))
# shutdown, then startup with wallet disabled

View File

@ -11,9 +11,7 @@ from test_framework.mininode import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
create_confirmed_utxos,
disconnect_nodes,
)
@ -35,7 +33,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
min_relay_fee = node.getnetworkinfo()["relayfee"]
utxos = create_confirmed_utxos(min_relay_fee, node, 10)
disconnect_nodes(node, 1)
self.disconnect_nodes(0, 1)
self.log.info("Generate transactions that only node 0 knows about")
@ -62,7 +60,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
self.restart_node(0)
self.log.info("Reconnect nodes & check if they are sent to node 1")
connect_nodes(node, 1)
self.connect_nodes(0, 1)
# fast forward into the future & ensure that the second node has the txns
node.mockscheduler(15 * 60) # 15 min in seconds
@ -81,8 +79,8 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
def test_txn_removal(self):
self.log.info("Test that transactions removed from mempool are removed from unbroadcast set")
node = self.nodes[0]
disconnect_nodes(node, 1)
node.disconnect_p2ps
self.disconnect_nodes(0, 1)
node.disconnect_p2ps()
# since the node doesn't have any connections, it will not receive
# any GETDATAs & thus the transaction will remain in the unbroadcast set.

View File

@ -25,7 +25,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
connect_nodes,
)
from test_framework.script import CScriptNum
@ -53,8 +52,8 @@ class MiningTest(BitcoinTestFramework):
assert_equal(mining_info['currentblocktx'], 0)
assert_equal(mining_info['currentblocksize'], 1000)
self.restart_node(0)
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 0)
self.connect_nodes(0, 1)
self.connect_nodes(1, 0)
def run_test(self):
self.mine_chain()

View File

@ -22,8 +22,6 @@ from test_framework.mininode import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
disconnect_nodes,
wait_until,
)
@ -62,7 +60,7 @@ class CompactFiltersTest(BitcoinTestFramework):
self.sync_blocks(timeout=600)
# Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting
disconnect_nodes(self.nodes[0], 1)
self.disconnect_nodes(0, 1)
self.nodes[0].generate(1)
wait_until(lambda: self.nodes[0].getblockcount() == 1000)
@ -91,7 +89,7 @@ class CompactFiltersTest(BitcoinTestFramework):
assert_equal(len(response.headers), 1)
self.log.info("Reorg node 0 to a new chain.")
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_blocks(timeout=600)
main_block_hash = self.nodes[0].getblockhash(1000)

View File

@ -6,7 +6,7 @@
from test_framework.mininode import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, connect_nodes
from test_framework.util import assert_equal
class ConnectDevnetNodes(BitcoinTestFramework):
def set_test_params(self):
@ -18,7 +18,7 @@ class ConnectDevnetNodes(BitcoinTestFramework):
self.add_nodes(self.num_nodes)
self.start_node(0)
self.start_node(1)
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_all()

View File

@ -8,7 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
connect_nodes,
wait_until,
)
@ -19,8 +18,8 @@ class DisconnectBanTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Connect nodes both way")
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 0)
self.connect_nodes(0, 1)
self.connect_nodes(1, 0)
self.log.info("Test setban and listbanned RPCs")
@ -75,8 +74,8 @@ class DisconnectBanTest(BitcoinTestFramework):
# Clear ban lists
self.nodes[1].clearbanned()
self.log.info("Connect nodes both way")
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 0)
self.connect_nodes(0, 1)
self.connect_nodes(1, 0)
self.log.info("Test disconnectnode RPCs")
@ -95,7 +94,7 @@ class DisconnectBanTest(BitcoinTestFramework):
assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
self.log.info("disconnectnode: successfully reconnect node")
connect_nodes(self.nodes[0], 1) # reconnect the node
self.connect_nodes(0, 1) # reconnect the node
assert_equal(len(self.nodes[0].getpeerinfo()), 2)
assert [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]

View File

@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, isolate_node, reconnect_isolated_node
from test_framework.util import assert_equal, assert_raises_rpc_error
'''
p2p_instantsend.py
@ -43,7 +43,7 @@ class InstantSendTest(DashTestFramework):
# create doublespending transaction, but don't relay it
dblspnd_tx = self.create_raw_tx(sender, isolated, 0.5, 1, 100)
# isolate one node from network
isolate_node(isolated)
self.isolate_node(self.isolated_idx)
# instantsend to receiver
receiver_addr = receiver.getnewaddress()
is_id = sender.sendtoaddress(receiver_addr, 0.9)
@ -60,7 +60,7 @@ class InstantSendTest(DashTestFramework):
isolated.generate(1)
wrong_block = isolated.getbestblockhash()
# connect isolated block to network
reconnect_isolated_node(isolated, 0)
self.reconnect_isolated_node(self.isolated_idx, 0)
# check doublespend block is rejected by other nodes
timeout = 10
for idx, node in enumerate(self.nodes):
@ -98,13 +98,13 @@ class InstantSendTest(DashTestFramework):
# create doublespending transaction, but don't relay it
dblspnd_tx = self.create_raw_tx(sender, isolated, 0.5, 1, 100)
# isolate one node from network
isolate_node(isolated)
self.isolate_node(self.isolated_idx)
# send doublespend transaction to isolated node
dblspnd_txid = isolated.sendrawtransaction(dblspnd_tx['hex'])
assert dblspnd_txid in set(isolated.getrawmempool())
# let isolated node rejoin the network
# The previously isolated node should NOT relay the doublespending TX
reconnect_isolated_node(isolated, 0)
self.reconnect_isolated_node(self.isolated_idx, 0)
for node in connected_nodes:
assert_raises_rpc_error(-5, "No such mempool or blockchain transaction", node.getrawtransaction, dblspnd_txid)
# Instantsend to receiver. The previously isolated node won't accept the tx but it should

View File

@ -11,7 +11,7 @@ and that it responds to getdata requests for blocks correctly:
from test_framework.messages import CInv, MSG_BLOCK, msg_getdata, NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_HEADERS_COMPRESSED, msg_verack
from test_framework.mininode import P2PInterface, mininode_lock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, disconnect_nodes, connect_nodes, wait_until
from test_framework.util import assert_equal, wait_until
class P2PIgnoreInv(P2PInterface):
firstAddrnServices = 0
@ -35,12 +35,9 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
self.extra_args = [['-prune=550', '-txindex=0', '-addrmantest'], [], []]
def disconnect_all(self):
disconnect_nodes(self.nodes[0], 1)
disconnect_nodes(self.nodes[1], 0)
disconnect_nodes(self.nodes[2], 1)
disconnect_nodes(self.nodes[2], 0)
disconnect_nodes(self.nodes[0], 2)
disconnect_nodes(self.nodes[1], 2)
self.disconnect_nodes(0, 1)
self.disconnect_nodes(0, 2)
self.disconnect_nodes(1, 2)
def setup_network(self):
self.add_nodes(self.num_nodes, self.extra_args)
@ -58,7 +55,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), expected_services)
self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.")
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
blocks = self.nodes[1].generatetoaddress(292, self.nodes[1].get_deterministic_priv_key().address)
self.sync_blocks([self.nodes[0], self.nodes[1]])
@ -84,7 +81,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
# connect unsynced node 2 with pruned NODE_NETWORK_LIMITED peer
# because node 2 is in IBD and node 0 is a NODE_NETWORK_LIMITED peer, sync must not be possible
connect_nodes(self.nodes[0], 2)
self.connect_nodes(0, 2)
try:
self.sync_blocks([self.nodes[0], self.nodes[2]], timeout=5)
except:
@ -93,7 +90,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
assert_equal(self.nodes[2].getblockheader(self.nodes[2].getbestblockhash())['height'], 0)
# now connect also to node 1 (non pruned)
connect_nodes(self.nodes[1], 2)
self.connect_nodes(1, 2)
# sync must be possible
self.sync_blocks()
@ -105,7 +102,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address)
# connect node1 (non pruned) with node0 (pruned) and check if the can sync
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
# sync must be possible, node 1 is no longer in IBD and should therefore connect to node 0 (NODE_NETWORK_LIMITED)
self.sync_blocks([self.nodes[0], self.nodes[1]])

View File

@ -11,7 +11,6 @@ from test_framework.test_node import ErrorMatch
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
p2p_port,
)
@ -28,6 +27,12 @@ class P2PPermissionsTests(BitcoinTestFramework):
["relay", "noban", "mempool"],
True)
self.checkpermission(
# no permission (even with forcerelay)
["-whitelist=@127.0.0.1", "-whitelistforcerelay=1"],
[],
False)
self.checkpermission(
# relay permission removed (no specific permissions)
["-whitelist=127.0.0.1", "-whitelistrelay=0"],
@ -85,7 +90,7 @@ class P2PPermissionsTests(BitcoinTestFramework):
def checkpermission(self, args, expectedPermissions, whitelisted):
self.restart_node(1, args)
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
assert_equal(peerinfo['whitelisted'], whitelisted)
assert_equal(len(expectedPermissions), len(peerinfo['permissions']))

View File

@ -14,7 +14,6 @@ from test_framework.test_framework import DashTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
connect_nodes,
force_finish_mnsync,
wait_until,
)
@ -127,12 +126,12 @@ class QuorumDataMessagesTest(DashTestFramework):
self.set_dash_test_params(4, 3, fast_dip3_enforcement=True, extra_args=extra_args)
def restart_mn(self, mn, reindex=False):
args = self.extra_args[mn.nodeIdx] + ['-masternodeblsprivkey=%s' % mn.keyOperator]
args = self.extra_args[mn.node.index] + ['-masternodeblsprivkey=%s' % mn.keyOperator]
if reindex:
args.append('-reindex')
self.restart_node(mn.nodeIdx, args)
self.restart_node(mn.node.index, args)
force_finish_mnsync(mn.node)
connect_nodes(mn.node, 0)
self.connect_nodes(mn.node.index, 0)
self.sync_blocks()
def run_test(self):
@ -371,7 +370,7 @@ class QuorumDataMessagesTest(DashTestFramework):
for extra_args in [[], ["-watchquorums"]]:
self.restart_node(0, self.extra_args[0] + extra_args)
for i in range(self.num_nodes - 1):
connect_nodes(node0, i + 1)
self.connect_nodes(0, i + 1)
p2p_node0 = p2p_connection(node0)
p2p_mn2 = p2p_connection(mn2.node)
id_p2p_node0 = get_mininode_id(node0)

View File

@ -56,7 +56,7 @@ from test_framework.blocktools import create_block, create_coinbase, create_tx_w
from test_framework.messages import CBlockHeader, CInv, MSG_BLOCK, msg_block, msg_headers, msg_inv
from test_framework.mininode import mininode_lock, P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, connect_nodes
from test_framework.util import assert_equal, assert_raises_rpc_error
class AcceptBlockTest(BitcoinTestFramework):
@ -300,7 +300,7 @@ class AcceptBlockTest(BitcoinTestFramework):
test_node.wait_for_disconnect()
# 9. Connect node1 to node0 and ensure it is able to sync
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_blocks([self.nodes[0], self.nodes[1]])
self.log.info("Successfully synced nodes 1 and 0")

View File

@ -12,7 +12,6 @@ from test_framework.util import (
assert_greater_than,
assert_greater_than_or_equal,
assert_raises_rpc_error,
connect_nodes,
count_bytes,
find_vout_for_address,
)
@ -39,10 +38,10 @@ class RawTransactionsTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 2)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
self.connect_nodes(0, 1)
self.connect_nodes(1, 2)
self.connect_nodes(0, 2)
self.connect_nodes(0, 3)
def run_test(self):
self.log.info("Connect nodes, set fees, generate blocks, and sync")

View File

@ -7,7 +7,7 @@ from decimal import Decimal
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than, connect_nodes
from test_framework.util import assert_equal, assert_greater_than
# Create one-input, one-output, no-fee transaction:
class RawTransactionsTest(BitcoinTestFramework):
@ -22,10 +22,10 @@ class RawTransactionsTest(BitcoinTestFramework):
def setup_network(self):
super().setup_network()
connect_nodes(self.nodes[0],1)
connect_nodes(self.nodes[1],2)
connect_nodes(self.nodes[0],2)
connect_nodes(self.nodes[0],3)
self.connect_nodes(0,1)
self.connect_nodes(1,2)
self.connect_nodes(0,2)
self.connect_nodes(0,3)
def run_test(self):
self.log.info("Mining blocks...")
@ -451,10 +451,10 @@ class RawTransactionsTest(BitcoinTestFramework):
for node in self.nodes:
node.settxfee(min_relay_tx_fee)
connect_nodes(self.nodes[0],1)
connect_nodes(self.nodes[1],2)
connect_nodes(self.nodes[0],2)
connect_nodes(self.nodes[0],3)
self.connect_nodes(0,1)
self.connect_nodes(1,2)
self.connect_nodes(0,2)
self.connect_nodes(0,3)
self.sync_all()
# drain the keypool

View File

@ -7,7 +7,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal, assert_is_hex_string, assert_raises_rpc_error,
connect_nodes, disconnect_nodes
)
FILTER_TYPES = ["basic"]
@ -20,7 +19,7 @@ class GetBlockFilterTest(BitcoinTestFramework):
def run_test(self):
# Create two chains by disconnecting nodes 0 & 1, mining, then reconnecting
disconnect_nodes(self.nodes[0], 1)
self.disconnect_nodes(0, 1)
self.nodes[0].generate(3)
self.nodes[1].generate(4)
@ -29,7 +28,7 @@ class GetBlockFilterTest(BitcoinTestFramework):
chain0_hashes = [self.nodes[0].getblockhash(block_height) for block_height in range(4)]
# Reorg node 0 to a new chain
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_blocks()
assert_equal(self.nodes[0].getblockcount(), 4)

View File

@ -8,7 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR
from test_framework.util import (
assert_equal,
connect_nodes,
wait_until,
)
@ -33,7 +32,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getblockcount(), 6)
self.log.info("Connect nodes to force a reorg")
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_blocks(self.nodes[0:2])
assert_equal(self.nodes[0].getblockcount(), 6)
badhash = self.nodes[1].getblockhash(2)
@ -44,7 +43,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbestblockhash(), besthash_n0)
self.log.info("Make sure we won't reorg to a lower work chain:")
connect_nodes(self.nodes[ 1], 2)
self.connect_nodes( 1, 2)
self.log.info("Sync node 2 to node 1 so both have 6 blocks")
self.sync_blocks(self.nodes[1:3])
assert_equal(self.nodes[2].getblockcount(), 6)
@ -64,7 +63,7 @@ class InvalidateTest(BitcoinTestFramework):
self.log.info("Make sure ResetBlockFailureFlags does the job correctly")
self.restart_node(0, extra_args=["-checkblocks=5"])
self.restart_node(1, extra_args=["-checkblocks=5"])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.nodes[0].generate(10)
self.sync_blocks(self.nodes[0:2])
newheight = self.nodes[0].getblockcount()
@ -72,7 +71,7 @@ class InvalidateTest(BitcoinTestFramework):
self.restart_node(0, extra_args=["-checkblocks=5"])
tip = self.nodes[0].generate(10)[-1]
self.nodes[1].generate(9)
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
self.sync_blocks(self.nodes[0:2])
assert_equal(self.nodes[0].getblockcount(), newheight + 10 * (j + 1))
assert_equal(self.nodes[1].getblockcount(), newheight + 10 * (j + 1))

View File

@ -13,7 +13,6 @@ from test_framework.util import (
assert_greater_than_or_equal,
assert_greater_than,
assert_raises_rpc_error,
connect_nodes,
p2p_port,
wait_until,
)
@ -58,8 +57,8 @@ class NetTest(DashTestFramework):
self.nodes[0].ping()
wait_until(lambda: all(['pingtime' in n for n in self.nodes[0].getpeerinfo()]))
self.log.info('Connect nodes both way')
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 0)
self.connect_nodes(0, 1)
self.connect_nodes(1, 0)
self._test_connection_count()
self._test_getnettotals()
@ -117,8 +116,8 @@ class NetTest(DashTestFramework):
self.nodes[0].setnetworkactive(state=True)
self.log.info('Connect nodes both way')
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 0)
self.connect_nodes(0, 1)
self.connect_nodes(1, 0)
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
@ -129,7 +128,7 @@ class NetTest(DashTestFramework):
assert_net_servicesnames(int(info["localservices"], 16), info["localservicesnames"])
self.log.info('Test extended connections info')
connect_nodes(self.nodes[1], 2)
self.connect_nodes(1, 2)
self.nodes[1].ping()
wait_until(lambda: all(['pingtime' in n for n in self.nodes[1].getpeerinfo()]))
assert_equal(self.nodes[1].getnetworkinfo()['connections'], 3)

View File

@ -18,6 +18,10 @@ class HTTPBasicsTest(BitcoinTestFramework):
self.num_nodes = 1
self.supports_cli = False
def setup_nodes(self):
self.add_nodes(self.num_nodes)
self.start_nodes()
def setup_chain(self):
super().setup_chain()
# Append rpcauth to dash.conf before initialization
@ -36,7 +40,6 @@ class HTTPBasicsTest(BitcoinTestFramework):
f.write(rpcauthoperator+"\n")
def run_test(self):
url = urllib.parse.urlparse(self.nodes[0].url)
def test_command(method, params, auth, expexted_status, should_not_match=False):

View File

@ -7,7 +7,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
)
def unidirectional_node_sync_via_rpc(node_src, node_dest):
@ -61,7 +60,7 @@ class PreciousTest(BitcoinTestFramework):
self.log.info("Connect nodes and check no reorg occurs")
# Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync)
node_sync_via_rpc(self.nodes[0:2])
connect_nodes(self.nodes[0], 1)
self.connect_nodes(0, 1)
assert_equal(self.nodes[0].getbestblockhash(), hashC)
assert_equal(self.nodes[1].getbestblockhash(), hashG)
self.log.info("Make Node0 prefer block G")
@ -98,8 +97,8 @@ class PreciousTest(BitcoinTestFramework):
hashL = self.nodes[2].getbestblockhash()
self.log.info("Connect nodes and check no reorg occurs")
node_sync_via_rpc(self.nodes[1:3])
connect_nodes(self.nodes[1], 2)
connect_nodes(self.nodes[0], 2)
self.connect_nodes(1, 2)
self.connect_nodes(0, 2)
assert_equal(self.nodes[0].getbestblockhash(), hashH)
assert_equal(self.nodes[1].getbestblockhash(), hashH)
assert_equal(self.nodes[2].getbestblockhash(), hashL)

View File

@ -5,11 +5,17 @@
"""Test the Partially Signed Transaction RPCs.
"""
import os
import json
from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, find_output
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
find_output
)
import json
import os
# Create one-input, one-output, no-fee transaction:
class PSBTTest(BitcoinTestFramework):

View File

@ -25,7 +25,7 @@ class RPCMasternodeTest(DashTestFramework):
mn = self.mninfo[idx]
for member in quorum_info["members"]:
if member["proTxHash"] == mn.proTxHash:
assert_equal(member["service"], '127.0.0.1:%d' % p2p_port(mn.nodeIdx))
assert_equal(member["service"], '127.0.0.1:%d' % p2p_port(mn.node.index))
if __name__ == '__main__':
RPCMasternodeTest().main()

View File

@ -20,7 +20,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
connect_nodes,
hex_str_to_bytes,
)
@ -60,7 +59,7 @@ class RawTransactionsTest(BitcoinTestFramework):
def setup_network(self):
super().setup_network()
connect_nodes(self.nodes[0], 2)
self.connect_nodes(0, 2)
def run_test(self):
self.log.info('prepare some coins for multiple *rawtransaction commands')

View File

@ -55,7 +55,8 @@ class ScantxoutsetTest(BitcoinTestFramework):
self.log.info("Stop node, remove wallet, mine again some blocks...")
self.stop_node(0)
shutil.rmtree(os.path.join(self.nodes[0].datadir, self.chain, 'wallets'))
self.start_node(0)
self.start_node(0, ['-nowallet'])
self.import_deterministic_coinbase_privkeys()
self.nodes[0].generate(110)
scan = self.nodes[0].scantxoutset("start", [])

Some files were not shown because too many files have changed in this diff Show More