Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
d2f9d42
Merge bitcoin/bitcoin#26794: test: test banlist database recreation
DashCoreAutoGuix Jul 24, 2025
5351b8b
Merge bitcoin/bitcoin#26924: refactor: Add missing includes to fix gc…
DashCoreAutoGuix Jul 24, 2025
77db9c8
Merge bitcoin/bitcoin#26507: test: remove unused vars in `feature_block`
DashCoreAutoGuix Jul 24, 2025
379f039
Merge bitcoin/bitcoin#25158: rpc, wallet: add abandoned field for all…
DashCoreAutoGuix Jul 24, 2025
8300adb
Merge bitcoin/bitcoin#24330: doc: release-process: Specify remote nam…
DashCoreAutoGuix Jul 24, 2025
f7ddd56
Merge bitcoin/bitcoin#24282: docs: Move explanation of hardened key s…
DashCoreAutoGuix Jul 24, 2025
fff6c95
Merge bitcoin/bitcoin#24359: doc: Fix typos
DashCoreAutoGuix Jul 24, 2025
7c07a80
Merge bitcoin/bitcoin#22151: build: Follow Transifex docs to prepare …
DashCoreAutoGuix Jul 24, 2025
2fcb230
Merge bitcoin/bitcoin#27068: wallet: SecureString to allow null chara…
DashCoreAutoGuix Jul 24, 2025
4818e39
Merge bitcoin#26119: doc: Move -permitbaremultisig to the relay help …
PastaPastaPasta Jul 25, 2025
c07cb09
Merge bitcoin/bitcoin#27271: RPC: Fix fund transaction crash when at …
DashCoreAutoGuix Jul 25, 2025
425ed5e
Merge bitcoin/bitcoin#27124: docs: add ramdisk guide for running test…
DashCoreAutoGuix Jul 26, 2025
669b60e
Merge bitcoin/bitcoin#27073: Convert ArgsManager::GetDataDir to a rea…
DashCoreAutoGuix Jul 26, 2025
49a05e4
Merge bitcoin/bitcoin#26194: rpc, wallet: use the same `next_index` k…
DashCoreAutoGuix Jul 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/descriptors.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ Descriptors consist of several types of expressions. The top level expression is
- Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps.
- Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children.
- The usage of hardened derivation steps requires providing the private key.
- Anywhere a `'` suffix is permitted to denote hardened derivation, the suffix `h` can be used instead.

(Anywhere a `'` suffix is permitted to denote hardened derivation, the suffix `h` can be used instead.)

`ADDR` expressions are any type of supported address:
- P2PKH addresses (base58, of the form `X...`). Note that P2PKH addresses in descriptors cannot be used for P2PK outputs (use the `pk` function instead).
Expand Down
4 changes: 4 additions & 0 deletions doc/release-note-26194.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add `next_index` in `listdescriptors` RPC
-----------------

- Added a new `next_index` field in the response in `listdescriptors` to have the same format as `importdescriptors`
6 changes: 6 additions & 0 deletions doc/release-notes-25158.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
RPC Wallet
----------

- The `gettransaction`, `listtransactions`, `listsinceblock` RPCs now return
the `abandoned` field for all transactions. Previously, the "abandoned" field
was only returned for sent transactions. (#25158)
6 changes: 6 additions & 0 deletions doc/release-notes-27068.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Wallet
------

- Wallet passphrases may now contain null characters.
Prior to this change, only characters up to the first
null character were recognized and accepted. (#27068)
2 changes: 1 addition & 1 deletion doc/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Checkout the Dash Core version you'd like to build:
pushd ./dash
export SIGNER='(your builder key, ie udjinm6, pasta, etc)'
export VERSION='(new version, e.g. 20.0.0)'
git fetch "v${VERSION}"
git fetch origin "v${VERSION}"
git checkout "v${VERSION}"
popd
```
Expand Down
6 changes: 5 additions & 1 deletion src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,15 @@ $(srcdir)/qt/dashstrings.cpp: FORCE
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations"
$(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES)

# The resulted dash_en.xlf source file should follow Transifex requirements.
# See: https://docs.transifex.com/formats/xliff#how-to-distinguish-between-a-source-file-and-a-translation-file
translate: $(srcdir)/qt/dashstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) -no-obsolete -I $(srcdir) -locations relative $^ -ts $(srcdir)/qt/locale/dash_en.ts
@test -n $(LCONVERT) || echo "lconvert is required for updating translations"
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LCONVERT) -o $(srcdir)/qt/locale/dash_en.xlf -i $(srcdir)/qt/locale/dash_en.ts
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LCONVERT) -drop-translations -o $(srcdir)/qt/locale/dash_en.xlf -i $(srcdir)/qt/locale/dash_en.ts
@$(SED) -i.old -e 's|source-language="en" target-language="en"|source-language="en"|' -e '/<target xml:space="preserve"><\/target>/d' $(srcdir)/qt/locale/dash_en.xlf
@rm -f $(srcdir)/qt/locale/dash_en.xlf.old

$(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM)
@test -f $(RCC)
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/sha256_arm_shani.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks)
MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(chunk + 48)));
chunk += 64;

// Original implemenation preloaded message and constant addition which was 1-3% slower.
// Original implementation preloaded message and constant addition which was 1-3% slower.
// Now included as first step in quad round code saving one Q Neon register
// "TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0]));"

Expand Down
3 changes: 2 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,6 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
// TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from
// https://github.com/bitcoin/bitcoin/pull/23542 have become widespread.
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
Expand Down Expand Up @@ -746,6 +745,8 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
Expand Down
38 changes: 29 additions & 9 deletions src/qt/askpassphrasedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,10 @@ void AskPassphraseDialog::accept()
oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE);
newpass2.reserve(MAX_PASSPHRASE_SIZE);
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make this input mlock()'d to begin with.
oldpass.assign(ui->passEdit1->text().toStdString().c_str());
newpass1.assign(ui->passEdit2->text().toStdString().c_str());
newpass2.assign(ui->passEdit3->text().toStdString().c_str());

oldpass.assign(std::string_view{ui->passEdit1->text().toStdString()});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably 27068 is not full, see upgradetohd in src/wallet/rpc/wallet.cpp

    } else {
        // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
        // Alternately, find a way to make request.params[0] mlock()'d to begin with.
        secureWalletPassphrase = request.params[2].get_str().c_str();
    }

newpass1.assign(std::string_view{ui->passEdit2->text().toStdString()});
newpass2.assign(std::string_view{ui->passEdit3->text().toStdString()});

secureClearPassFields();

Expand Down Expand Up @@ -185,8 +184,19 @@ void AskPassphraseDialog::accept()
try {
if(!model->setWalletLocked(false, oldpass, mode == UnlockMixing))
{
QMessageBox::critical(this, tr("Wallet unlock failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
// Check if the passphrase has a null character (see #27067 for details)
if (oldpass.find('\0') == std::string::npos) {
QMessageBox::critical(this, tr("Wallet unlock failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
} else {
QMessageBox::critical(this, tr("Wallet unlock failed"),
tr("The passphrase entered for the wallet decryption is incorrect. "
"It contains a null character (ie - a zero byte). "
"If the passphrase was set with a version of this software prior to 23.0, "
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ec99c2f4fa289ff004af58618fba00c6088670ca: nit: merge it into original commit maybe

"please try again with only the characters up to — but not including — "
"the first null character. If this is successful, please set a new "
"passphrase to avoid this issue in the future."));
}
}
else
{
Expand All @@ -207,8 +217,18 @@ void AskPassphraseDialog::accept()
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
// Check if the old passphrase had a null character (see #27067 for details)
if (oldpass.find('\0') == std::string::npos) {
QMessageBox::critical(this, tr("Passphrase change failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
} else {
QMessageBox::critical(this, tr("Passphrase change failed"),
tr("The old passphrase entered for the wallet decryption is incorrect. "
"It contains a null character (ie - a zero byte). "
"If the passphrase was set with a version of this software prior to 23.0, "
"please try again with only the characters up to — but not including — "
"the first null character."));
}
}
}
else
Expand Down
1 change: 1 addition & 0 deletions src/qt/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans

static bool InitSettings()
{
gArgs.EnsureDataDir();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

27073: need to pull 27176 as well (related docs update)

if (!gArgs.GetSettingsPath()) {
return true; // Do nothing if settings file disabled.
}
Expand Down
1 change: 1 addition & 0 deletions src/support/allocators/secure.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct secure_allocator {
};

// This is exactly like std::string, but with a custom allocator.
// TODO: Consider finding a way to make incoming RPC request.params[i] mlock()ed as well
typedef std::basic_string<char, std::char_traits<char>, secure_allocator<char> > SecureString;

typedef std::vector<unsigned char, secure_allocator<unsigned char> > SecureVector;
Expand Down
1 change: 1 addition & 0 deletions src/support/lockedpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#endif

#include <algorithm>
#include <limits>
#include <stdexcept>
#ifdef ARENA_DEBUG
#include <iomanip>
Expand Down
4 changes: 2 additions & 2 deletions src/support/lockedpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
#ifndef BITCOIN_SUPPORT_LOCKEDPOOL_H
#define BITCOIN_SUPPORT_LOCKEDPOOL_H

#include <stdint.h>
#include <cstddef>
#include <list>
#include <map>
#include <mutex>
#include <memory>
#include <mutex>
#include <unordered_map>

/**
Expand Down
41 changes: 30 additions & 11 deletions src/util/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,7 @@ fs::path ArgsManager::GetDataDir(bool net_specific) const
LOCK(cs_args);
fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;

// Cache the path to avoid calling fs::create_directories on every call of
// this function
// Used cached path if available
if (!path.empty()) return path;

const fs::path datadir{GetPathArg("-datadir")};
Expand All @@ -463,15 +462,8 @@ fs::path ArgsManager::GetDataDir(bool net_specific) const
path = GetDefaultDataDir();
}

if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}

if (net_specific && !BaseParams().DataDir().empty()) {
path /= fs::PathFromString(BaseParams().DataDir());
if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}
}

return path;
Expand All @@ -485,6 +477,27 @@ fs::path ArgsManager::GetBackupsDirPath()
return fs::absolute(GetPathArg("-walletbackupsdir"));
}

void ArgsManager::EnsureDataDir() const
{
/**
* "/wallets" subdirectories are created in all **new**
* datadirs, because wallet code will create new wallets in the "wallets"
* subdirectory only if exists already, otherwise it will create them in
* the top-level datadir where they could interfere with other files.
* Wallet init code currently avoids creating "wallets" directories itself
* for backwards compatibility, but this be changed in the future and
* wallet code here could go away (#16220).
*/
auto path{GetDataDir(false)};
if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}
path = GetDataDir(true);
if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}
}

void ArgsManager::ClearPathCache()
{
LOCK(cs_args);
Expand Down Expand Up @@ -530,6 +543,7 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const

bool ArgsManager::InitSettings(std::string& error)
{
EnsureDataDir();
if (!GetSettingsPath()) {
return true; // Do nothing if settings file disabled.
}
Expand Down Expand Up @@ -963,6 +977,11 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return true;
}

fs::path ArgsManager::GetConfigFilePath() const
{
return GetConfigFile(GetPathArg("-conf", BITCOIN_CONF_FILENAME));
}

bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{
{
Expand All @@ -971,8 +990,8 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
m_config_sections.clear();
}

const fs::path conf_path = GetPathArg("-conf", BITCOIN_CONF_FILENAME);
std::ifstream stream{GetConfigFile(conf_path)};
const auto conf_path{GetConfigFilePath()};
std::ifstream stream{conf_path};

// not ok to have a config file specified that cannot be opened
if (IsArgSet("-conf") && !stream.good()) {
Expand Down
12 changes: 11 additions & 1 deletion src/util/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ class ArgsManager
void SelectConfigNetwork(const std::string& network);

[[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error);

/**
* Return config file path (read-only)
*/
fs::path GetConfigFilePath() const;
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);

/**
Expand Down Expand Up @@ -495,13 +500,18 @@ class ArgsManager
*/
void LogArgs() const;

/**
* If datadir does not exist, create it along with wallets/
* subdirectory(s).
*/
void EnsureDataDir() const;

private:
/**
* Get data directory path
*
* @param net_specific Append network identifier to the returned path
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
* @post Returned directory path is created unless it is empty
*/
fs::path GetDataDir(bool net_specific) const;

Expand Down
4 changes: 3 additions & 1 deletion src/wallet/rpc/backup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1980,7 +1980,8 @@ RPCHelpMan listdescriptors()
{RPCResult::Type::NUM, "", "Range start inclusive"},
{RPCResult::Type::NUM, "", "Range end inclusive"},
}},
{RPCResult::Type::NUM, "next", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"},
{RPCResult::Type::NUM, "next", /*optional=*/true, "Same as next_index field. Kept for compatibility reason."},
{RPCResult::Type::NUM, "next_index", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"},
}},
}}
}},
Expand Down Expand Up @@ -2041,6 +2042,7 @@ RPCHelpMan listdescriptors()
range.push_back(wallet_descriptor.range_end - 1);
spk.pushKV("range", range);
spk.pushKV("next", wallet_descriptor.next_index);
spk.pushKV("next_index", wallet_descriptor.next_index);
}
descriptors.push_back(spk);
}
Expand Down
Loading
Loading