Merge fa91ad3420 into merged_master (Bitcoin PR bitcoin/bitcoin#34268)

This commit is contained in:
ivanlele 2026-07-14 17:32:46 +00:00
commit 7036b9351f
No known key found for this signature in database
17 changed files with 257 additions and 28 deletions

View file

@ -28,9 +28,9 @@ get_directory_property(precious_variables CACHE_VARIABLES)
#=============================
set(CLIENT_NAME "Elements Core")
set(CLIENT_VERSION_MAJOR 29)
set(CLIENT_VERSION_MINOR 2)
set(CLIENT_VERSION_MINOR 3)
set(CLIENT_VERSION_BUILD 0)
set(CLIENT_VERSION_RC 0)
set(CLIENT_VERSION_RC 1)
set(CLIENT_VERSION_IS_RELEASE "true")
set(COPYRIGHT_YEAR "2025")

View file

@ -10,14 +10,13 @@ to addrman with).
Update `MIN_BLOCKS` in `makeseeds.py` and the `-m`/`--minblocks` arguments below, as needed.
The seeds compiled into the release are created from sipa's, achow101's and luke-jr's
The seeds compiled into the release are created from sipa's and achow101's
DNS seed, virtu's crawler, and asmap community AS map data. Run the following commands
from the `/contrib/seeds` directory:
```
curl https://bitcoin.sipa.be/seeds.txt.gz | gzip -dc > seeds_main.txt
curl https://21.ninja/seeds.txt.gz | gzip -dc >> seeds_main.txt
curl https://luke.dashjr.org/programs/bitcoin/files/charts/seeds.txt >> seeds_main.txt
curl https://mainnet.achownodes.xyz/seeds.txt.gz | gzip -dc >> seeds_main.txt
curl https://signet.achownodes.xyz/seeds.txt.gz | gzip -dc > seeds_signet.txt
curl https://testnet.achownodes.xyz/seeds.txt.gz | gzip -dc > seeds_test.txt

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-CLI "1" "October 2025" "bitcoin-cli v29.2.0" "User Commands"
.TH BITCOIN-CLI "1" "January 2026" "bitcoin-cli v29.3.0rc1" "User Commands"
.SH NAME
bitcoin-cli \- manual page for bitcoin-cli v29.2.0
bitcoin-cli \- manual page for bitcoin-cli v29.3.0rc1
.SH SYNOPSIS
.B bitcoin-cli
[\fI\,options\/\fR] \fI\,<command> \/\fR[\fI\,params\/\fR]
@ -15,7 +15,7 @@ bitcoin-cli \- manual page for bitcoin-cli v29.2.0
.B bitcoin-cli
[\fI\,options\/\fR] \fI\,help <command>\/\fR
.SH DESCRIPTION
Bitcoin Core RPC client version v29.2.0
Bitcoin Core RPC client version v29.3.0rc1
.PP
The bitcoin\-cli utility provides a command line interface to interact with a Bitcoin Core RPC server.
.PP

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-QT "1" "October 2025" "bitcoin-qt v29.2.0" "User Commands"
.TH BITCOIN-QT "1" "January 2026" "bitcoin-qt v29.3.0rc1" "User Commands"
.SH NAME
bitcoin-qt \- manual page for bitcoin-qt v29.2.0
bitcoin-qt \- manual page for bitcoin-qt v29.3.0rc1
.SH SYNOPSIS
.B bitcoin-qt
[\fI\,options\/\fR] [\fI\,URI\/\fR]
.SH DESCRIPTION
Bitcoin Core version v29.2.0
Bitcoin Core version v29.3.0rc1
.PP
The bitcoin\-qt application provides a graphical interface for interacting with Bitcoin Core.
.PP

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-TX "1" "October 2025" "bitcoin-tx v29.2.0" "User Commands"
.TH BITCOIN-TX "1" "January 2026" "bitcoin-tx v29.3.0rc1" "User Commands"
.SH NAME
bitcoin-tx \- manual page for bitcoin-tx v29.2.0
bitcoin-tx \- manual page for bitcoin-tx v29.3.0rc1
.SH SYNOPSIS
.B bitcoin-tx
[\fI\,options\/\fR] \fI\,<hex-tx> \/\fR[\fI\,commands\/\fR]
@ -9,7 +9,7 @@ bitcoin-tx \- manual page for bitcoin-tx v29.2.0
.B bitcoin-tx
[\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR]
.SH DESCRIPTION
Bitcoin Core bitcoin\-tx utility version v29.2.0
Bitcoin Core bitcoin\-tx utility version v29.3.0rc1
.PP
The bitcoin\-tx tool is used for creating and modifying bitcoin transactions.
.PP

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-UTIL "1" "October 2025" "bitcoin-util v29.2.0" "User Commands"
.TH BITCOIN-UTIL "1" "January 2026" "bitcoin-util v29.3.0rc1" "User Commands"
.SH NAME
bitcoin-util \- manual page for bitcoin-util v29.2.0
bitcoin-util \- manual page for bitcoin-util v29.3.0rc1
.SH SYNOPSIS
.B bitcoin-util
[\fI\,options\/\fR] [\fI\,command\/\fR]
@ -9,7 +9,7 @@ bitcoin-util \- manual page for bitcoin-util v29.2.0
.B bitcoin-util
[\fI\,options\/\fR] \fI\,grind <hex-block-header>\/\fR
.SH DESCRIPTION
Bitcoin Core bitcoin\-util utility version v29.2.0
Bitcoin Core bitcoin\-util utility version v29.3.0rc1
.PP
The bitcoin\-util tool provides bitcoin related functionality that does not rely on the ability to access a running node. Available [commands] are listed below.
.SH OPTIONS

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-WALLET "1" "October 2025" "bitcoin-wallet v29.2.0" "User Commands"
.TH BITCOIN-WALLET "1" "January 2026" "bitcoin-wallet v29.3.0rc1" "User Commands"
.SH NAME
bitcoin-wallet \- manual page for bitcoin-wallet v29.2.0
bitcoin-wallet \- manual page for bitcoin-wallet v29.3.0rc1
.SH SYNOPSIS
.B bitcoin-wallet
[\fI\,options\/\fR] \fI\,<command>\/\fR
.SH DESCRIPTION
Bitcoin Core bitcoin\-wallet utility version v29.2.0
Bitcoin Core bitcoin\-wallet utility version v29.3.0rc1
.PP
bitcoin\-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.
.PP

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIND "1" "October 2025" "bitcoind v29.2.0" "User Commands"
.TH BITCOIND "1" "January 2026" "bitcoind v29.3.0rc1" "User Commands"
.SH NAME
bitcoind \- manual page for bitcoind v29.2.0
bitcoind \- manual page for bitcoind v29.3.0rc1
.SH SYNOPSIS
.B bitcoind
[\fI\,options\/\fR]
.SH DESCRIPTION
Bitcoin Core daemon version v29.2.0
Bitcoin Core daemon version v29.3.0rc1
.PP
The Bitcoin Core daemon (bitcoind) is a headless program that connects to the Bitcoin network to validate and relay transactions and blocks, as well as relaying addresses.
.PP

View file

@ -1,6 +1,6 @@
Bitcoin Core version 29.x is now available from:
Bitcoin Core version 29.3rc1 is now available from:
<https://bitcoincore.org/bin/bitcoin-core-29.x/>
<https://bitcoincore.org/bin/bitcoin-core-29.3/test.rc1/>
This release includes various bug fixes and performance
improvements, as well as updated translations.
@ -37,6 +37,36 @@ unsupported systems.
Notable changes
===============
### P2P
- #33050 net, validation: don't punish peers for consensus-invalid txs
- #33723 chainparams: remove dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us
### Validation
- #32473 Introduce per-txin sighash midstate cache for legacy/p2sh/segwitv0 scripts
- #33105 validation: detect witness stripping without re-running Script checks
### Wallet
- #33268 wallet: Identify transactions spending 0-value outputs, and add tests for anchor outputs in a wallet
- #34156 wallet: fix unnamed legacy wallet migration failure
- #34226 wallet: test: Relative wallet failed migration cleanup
- #34123 wallet: migration, avoid creating spendable wallet from a watch-only legacy wallet
- #34215 wallettool: fix unnamed createfromdump failure walletsdir deletion
### Mining
- #33475 bugfix: miner: fix `addPackageTxs` unsigned integer overflow
### Build
- #34227 guix: Fix `osslsigncode` tests
### Documentation
- #33623 doc: document capnproto and libmultiprocess deps in 29.x
### Test
- #33612 test: change log rate limit version gate
@ -51,8 +81,17 @@ Credits
Thanks to everyone who directly contributed to this release:
- Anthony Towns
- Antoine Poinsot
- Ava Chow
- David Gumberg
- Eugene Siegel
- fanquake
- furszy
- Hennadii Stepanov
- ismaelsadeeq
- Pieter Wuille
- SatsAndSports
- willcl-ark
As well as to everyone that helped with translations on

View file

@ -300,7 +300,6 @@ public:
// release ASAP to avoid it where possible.
vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd
vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9
vSeeds.emplace_back("dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us."); // Luke Dashjr
vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd
vSeeds.emplace_back("seed.btc.petertodd.net."); // Peter Todd, only supports x1, x5, x9, and xd
vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost

View file

@ -463,8 +463,8 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
++nConsecutiveFailed;
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight >
m_options.nBlockMaxWeight - m_options.block_reserved_weight) {
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight +
m_options.block_reserved_weight > m_options.nBlockMaxWeight) {
// Give up if we're close to full and haven't succeeded in a while
break;
}

View file

@ -1631,6 +1631,9 @@ RPCHelpMan sendall()
// estimate final size of tx
const TxSize tx_size{CalculateMaximumSignedTxSize(CTransaction(rawTx), pwallet.get())};
if (tx_size.vsize == -1) {
throw JSONRPCError(RPC_WALLET_ERROR, "Unable to determine the size of the transaction, the wallet contains unsolvable descriptors");
}
const CAmount fee_from_size{fee_rate.GetFee(tx_size.vsize)};
const std::optional<CAmount> total_bump_fees{pwallet->chain().calculateCombinedBumpFee(outpoints_spent, fee_rate)};
CAmount effective_value = total_input_value - fee_from_size - total_bump_fees.value_or(0);

View file

@ -1721,7 +1721,13 @@ isminetype CWallet::IsMine(const COutPoint& outpoint) const
bool CWallet::IsFromMe(const CTransaction& tx) const
{
return (GetDebit(tx, ISMINE_ALL) > CAmountMap());
LOCK(cs_wallet);
for (const CTxIn& txin : tx.vin) {
if (IsMine(txin.prevout)) {
return true;
}
}
return false;
}
CAmountMap CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const

View file

@ -50,6 +50,7 @@ DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1)))
assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING
PAY_TO_ANCHOR = CScript([OP_1, bytes.fromhex("4e73")])
ANCHOR_ADDRESS = "bcrt1pfeesnyr2tx"
def key_to_p2pk_script(key):
key = check_key(key)

View file

@ -206,6 +206,8 @@ BASE_SCRIPTS = [
'wallet_listreceivedby.py --descriptors',
'wallet_abandonconflict.py --legacy-wallet',
'wallet_abandonconflict.py --descriptors',
'wallet_anchor.py --legacy-wallet',
'wallet_anchor.py --descriptors',
'feature_reindex.py',
'feature_reindex_readonly.py',
'wallet_labels.py --legacy-wallet',

133
test/functional/wallet_anchor.py Executable file
View file

@ -0,0 +1,133 @@
#!/usr/bin/env python3
# Copyright (c) 2025-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php.
import time
from test_framework.blocktools import MAX_FUTURE_BLOCK_TIME
from test_framework.descriptors import descsum_create
from test_framework.messages import (
COutPoint,
CTxIn,
CTxInWitness,
CTxOut,
)
from test_framework.script_util import (
ANCHOR_ADDRESS,
PAY_TO_ANCHOR,
)
from test_framework.test_framework import BitcoinTestFramework, SkipTest
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
from test_framework.wallet import MiniWallet
class WalletAnchorTest(BitcoinTestFramework):
def add_options(self, parser):
self.add_wallet_options(parser)
def set_test_params(self):
self.num_nodes = 1
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
# ELEMENTS: Pay-to-anchor outputs carry a 0-value, spendable scriptPubKey.
# Elements consensus (`VerifyAmounts` in confidential_validation.cpp) rejects
# any 0-value output whose scriptPubKey is not `IsUnspendable()`, so the P2A
# construction used by this test cannot be broadcast on an Elements chain.
raise SkipTest("Pay-to-anchor outputs are rejected by Elements consensus (0-value spendable output)")
def test_0_value_anchor_listunspent(self):
self.log.info("Test that 0-value anchor outputs are detected as UTXOs")
# Create an anchor output, and spend it
sender = MiniWallet(self.nodes[0])
anchor_tx = sender.create_self_transfer(fee_rate=0, version=3)["tx"]
anchor_tx.vout.append(CTxOut(0, PAY_TO_ANCHOR))
anchor_tx.rehash() # Rehash after modifying anchor_tx
anchor_spend = sender.create_self_transfer(version=3)["tx"]
anchor_spend.vin.append(CTxIn(COutPoint(anchor_tx.sha256, 1), b""))
anchor_spend.wit.vtxinwit.append(CTxInWitness())
anchor_spend.rehash() # Rehash after modifying anchor_spend
submit_res = self.nodes[0].submitpackage([anchor_tx.serialize().hex(), anchor_spend.serialize().hex()])
assert_equal(submit_res["package_msg"], "success")
anchor_txid = anchor_tx.hash
anchor_spend_txid = anchor_spend.hash
# Mine each tx in separate blocks
self.generateblock(self.nodes[0], sender.get_address(), [anchor_tx.serialize().hex()])
anchor_tx_height = self.nodes[0].getblockcount()
self.generateblock(self.nodes[0], sender.get_address(), [anchor_spend.serialize().hex()])
# Mock time forward and generate some blocks to avoid rescanning of latest blocks
self.nodes[0].setmocktime(int(time.time()) + MAX_FUTURE_BLOCK_TIME + 1)
self.generate(self.nodes[0], 10)
self.nodes[0].createwallet(wallet_name="anchor", disable_private_keys=True)
wallet = self.nodes[0].get_wallet_rpc("anchor")
wallet.importaddress(ANCHOR_ADDRESS, rescan=False)
# The wallet should have no UTXOs, and not know of the anchor tx or its spend
assert_equal(wallet.listunspent(), [])
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", wallet.gettransaction, anchor_txid)
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", wallet.gettransaction, anchor_spend_txid)
# Rescanning the block containing the anchor so that listunspent will list the output
wallet.rescanblockchain(0, anchor_tx_height)
utxos = wallet.listunspent()
assert_equal(len(utxos), 1)
assert_equal(utxos[0]["txid"], anchor_txid)
assert_equal(utxos[0]["address"], ANCHOR_ADDRESS)
assert_equal(utxos[0]["amount"], 0)
wallet.gettransaction(anchor_txid)
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", wallet.gettransaction, anchor_spend_txid)
# Rescan the rest of the blockchain to see the anchor was spent
wallet.rescanblockchain()
assert_equal(wallet.listunspent(), [])
wallet.gettransaction(anchor_spend_txid)
def test_cannot_sign_anchors(self):
self.log.info("Test that the wallet cannot spend anchor outputs")
for disable_privkeys in [False, True]:
self.nodes[0].createwallet(wallet_name=f"anchor_spend_{disable_privkeys}", disable_private_keys=disable_privkeys)
wallet = self.nodes[0].get_wallet_rpc(f"anchor_spend_{disable_privkeys}")
if self.options.descriptors:
import_res = wallet.importdescriptors([
{"desc": descsum_create(f"addr({ANCHOR_ADDRESS})"), "timestamp": "now"},
{"desc": descsum_create(f"raw({PAY_TO_ANCHOR.hex()})"), "timestamp": "now"}
])
assert_equal(import_res[0]["success"], disable_privkeys)
assert_equal(import_res[1]["success"], disable_privkeys)
else:
wallet.importaddress(ANCHOR_ADDRESS)
anchor_txid = self.default_wallet.sendtoaddress(ANCHOR_ADDRESS, 1)
self.generate(self.nodes[0], 1)
wallet = self.nodes[0].get_wallet_rpc("anchor_spend_True")
utxos = wallet.listunspent()
assert_equal(len(utxos), 1)
assert_equal(utxos[0]["txid"], anchor_txid)
assert_equal(utxos[0]["address"], ANCHOR_ADDRESS)
assert_equal(utxos[0]["amount"], 1)
if self.options.descriptors:
assert_raises_rpc_error(-4, "Missing solving data for estimating transaction size", wallet.send, [{self.default_wallet.getnewaddress(): 0.9999}])
assert_raises_rpc_error(-4, "Unable to determine the size of the transaction, the wallet contains unsolvable descriptors", wallet.sendall, recipients=[self.default_wallet.getnewaddress()])
else:
assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, [{self.default_wallet.getnewaddress(): 0.9999}])
assert_raises_rpc_error(-6, "Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.", wallet.sendall, recipients=[self.default_wallet.getnewaddress()])
assert_raises_rpc_error(-4, "Error: Private keys are disabled for this wallet", wallet.sendtoaddress, self.default_wallet.getnewaddress(), 0.9999)
assert_raises_rpc_error(-4, "Unable to determine the size of the transaction, the wallet contains unsolvable descriptors", wallet.sendall, recipients=[self.default_wallet.getnewaddress()], inputs=utxos)
def run_test(self):
self.default_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
self.test_0_value_anchor_listunspent()
self.test_cannot_sign_anchors()
if __name__ == '__main__':
WalletAnchorTest(__file__).main()

View file

@ -5,9 +5,11 @@
"""Test the listtransactions API."""
from decimal import Decimal
import time
import os
import shutil
from test_framework.blocktools import MAX_FUTURE_BLOCK_TIME
from test_framework.messages import (
COIN,
tx_from_hex,
@ -17,7 +19,9 @@ from test_framework.util import (
assert_array_result,
assert_equal,
assert_raises_rpc_error,
find_vout_for_address,
)
from test_framework.wallet_util import get_generate_key
class ListTransactionsTest(BitcoinTestFramework):
@ -114,6 +118,8 @@ class ListTransactionsTest(BitcoinTestFramework):
self.run_invalid_parameters_test()
self.test_op_return()
self.test_from_me_status_change()
def run_rbf_opt_in_test(self):
"""Test the opt-in-rbf flag for sent and received transactions."""
@ -329,6 +335,47 @@ class ListTransactionsTest(BitcoinTestFramework):
assert 'address' not in op_ret_tx
def test_from_me_status_change(self):
self.log.info("Test gettransaction after changing a transaction's 'from me' status")
self.nodes[0].createwallet("fromme")
default_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
wallet = self.nodes[0].get_wallet_rpc("fromme")
# The 'fee' field of gettransaction is only added when the transaction is 'from me'
# Run twice, once for a transaction in the mempool, again when it confirms
for confirm in [False, True]:
key = get_generate_key()
default_wallet.importprivkey(key.privkey)
send_res = default_wallet.send(outputs=[{key.p2wpkh_addr: 1}, {wallet.getnewaddress(): 1}])
assert_equal(send_res["complete"], True)
vout = find_vout_for_address(self.nodes[0], send_res["txid"], key.p2wpkh_addr)
utxos = [{"txid": send_res["txid"], "vout": vout}]
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
# Send to the test wallet, ensuring that one input is for the descriptor we will import,
# and that there are other inputs belonging to only the sending wallet
send_res = default_wallet.send(outputs=[{wallet.getnewaddress(): 1.5}], inputs=utxos, add_inputs=True)
assert_equal(send_res["complete"], True)
txid = send_res["txid"]
self.nodes[0].syncwithvalidationinterfacequeue()
tx_info = wallet.gettransaction(txid)
assert "fee" not in tx_info
assert_equal(any(detail["category"] == "send" for detail in tx_info["details"]), False)
if confirm:
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
# Mock time forward and generate blocks so that the import does not rescan the transaction
self.nodes[0].setmocktime(int(time.time()) + MAX_FUTURE_BLOCK_TIME + 1)
self.generate(self.nodes[0], 10, sync_fun=self.no_op)
wallet.importprivkey(key.privkey)
# TODO: We should check that the fee matches, but since the transaction spends inputs
# not known to the wallet, it is incorrectly calculating the fee.
# assert_equal(wallet.gettransaction(txid)["fee"], fee)
tx_info = wallet.gettransaction(txid)
assert "fee" in tx_info
assert_equal(any(detail["category"] == "send" for detail in tx_info["details"]), True)
if __name__ == '__main__':
ListTransactionsTest(__file__).main()