mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-22 08:09:19 +01:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3f866a8df | ||
|
|
a9ea715835 | ||
|
|
c119b0a176 | ||
|
|
1b19c894a2 | ||
|
|
3afbc7d67d | ||
|
|
8b726bf556 | ||
|
|
9d73176d00 | ||
|
|
195f0dfd0e | ||
|
|
e5d097b639 | ||
|
|
c8426706de | ||
|
|
e15b306017 | ||
|
|
95fded1069 | ||
|
|
d464b2af30 | ||
|
|
7a97a56ffb | ||
|
|
f668a3a859 | ||
|
|
fad1c55301 | ||
|
|
c1061be14a | ||
|
|
39af5f2164 |
@@ -9,7 +9,12 @@ export LC_ALL=C
|
||||
GIT_HEAD=$(git rev-parse HEAD)
|
||||
if [ -n "$CIRRUS_PR" ]; then
|
||||
COMMIT_RANGE="${CIRRUS_BASE_SHA}..$GIT_HEAD"
|
||||
echo
|
||||
git log --no-merges --oneline "$COMMIT_RANGE"
|
||||
echo
|
||||
test/lint/commit-script-check.sh "$COMMIT_RANGE"
|
||||
else
|
||||
COMMIT_RANGE="SKIP_EMPTY_NOT_A_PR"
|
||||
fi
|
||||
export COMMIT_RANGE
|
||||
|
||||
@@ -36,8 +41,3 @@ if [ "$CIRRUS_REPO_FULL_NAME" = "bitcoin/bitcoin" ] && [ "$CIRRUS_PR" = "" ] ; t
|
||||
${CI_RETRY_EXE} gpg --keyserver hkps://keys.openpgp.org --recv-keys "${KEYS[@]}" &&
|
||||
./contrib/verify-commits/verify-commits.py;
|
||||
fi
|
||||
|
||||
if [ -n "$COMMIT_RANGE" ]; then
|
||||
echo
|
||||
git log --no-merges --oneline "$COMMIT_RANGE"
|
||||
fi
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
AC_PREREQ([2.69])
|
||||
define(_CLIENT_VERSION_MAJOR, 24)
|
||||
define(_CLIENT_VERSION_MINOR, 0)
|
||||
define(_CLIENT_VERSION_BUILD, 0)
|
||||
define(_CLIENT_VERSION_BUILD, 1)
|
||||
define(_CLIENT_VERSION_RC, 0)
|
||||
define(_CLIENT_VERSION_IS_RELEASE, true)
|
||||
define(_COPYRIGHT_YEAR, 2022)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH BITCOIN-CLI "1" "November 2022" "bitcoin-cli v24.0.0" "User Commands"
|
||||
.TH BITCOIN-CLI "1" "December 2022" "bitcoin-cli v24.0.1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-cli \- manual page for bitcoin-cli v24.0.0
|
||||
bitcoin-cli \- manual page for bitcoin-cli v24.0.1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-cli
|
||||
[\fI\,options\/\fR] \fI\,<command> \/\fR[\fI\,params\/\fR] \fI\,Send command to Bitcoin Core\/\fR
|
||||
@@ -15,7 +15,7 @@ bitcoin-cli \- manual page for bitcoin-cli v24.0.0
|
||||
.B bitcoin-cli
|
||||
[\fI\,options\/\fR] \fI\,help <command> Get help for a command\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core RPC client version v24.0.0
|
||||
Bitcoin Core RPC client version v24.0.1
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\-?
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH BITCOIN-QT "1" "November 2022" "bitcoin-qt v24.0.0" "User Commands"
|
||||
.TH BITCOIN-QT "1" "December 2022" "bitcoin-qt v24.0.1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-qt \- manual page for bitcoin-qt v24.0.0
|
||||
bitcoin-qt \- manual page for bitcoin-qt v24.0.1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-qt
|
||||
[\fI\,command-line options\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core version v24.0.0
|
||||
Bitcoin Core version v24.0.1
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\-?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH BITCOIN-TX "1" "November 2022" "bitcoin-tx v24.0.0" "User Commands"
|
||||
.TH BITCOIN-TX "1" "December 2022" "bitcoin-tx v24.0.1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-tx \- manual page for bitcoin-tx v24.0.0
|
||||
bitcoin-tx \- manual page for bitcoin-tx v24.0.1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-tx
|
||||
[\fI\,options\/\fR] \fI\,<hex-tx> \/\fR[\fI\,commands\/\fR] \fI\,Update hex-encoded bitcoin transaction\/\fR
|
||||
@@ -9,7 +9,7 @@ bitcoin-tx \- manual page for bitcoin-tx v24.0.0
|
||||
.B bitcoin-tx
|
||||
[\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR] \fI\,Create hex-encoded bitcoin transaction\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-tx utility version v24.0.0
|
||||
Bitcoin Core bitcoin\-tx utility version v24.0.1
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\-?
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH BITCOIN-UTIL "1" "November 2022" "bitcoin-util v24.0.0" "User Commands"
|
||||
.TH BITCOIN-UTIL "1" "December 2022" "bitcoin-util v24.0.1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-util \- manual page for bitcoin-util v24.0.0
|
||||
bitcoin-util \- manual page for bitcoin-util v24.0.1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-util
|
||||
[\fI\,options\/\fR] [\fI\,commands\/\fR] \fI\,Do stuff\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-util utility version v24.0.0
|
||||
Bitcoin Core bitcoin\-util utility version v24.0.1
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\-?
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH BITCOIN-WALLET "1" "November 2022" "bitcoin-wallet v24.0.0" "User Commands"
|
||||
.TH BITCOIN-WALLET "1" "December 2022" "bitcoin-wallet v24.0.1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-wallet \- manual page for bitcoin-wallet v24.0.0
|
||||
bitcoin-wallet \- manual page for bitcoin-wallet v24.0.1
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-wallet version v24.0.0
|
||||
Bitcoin Core bitcoin\-wallet version v24.0.1
|
||||
.PP
|
||||
bitcoin\-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.
|
||||
By default bitcoin\-wallet will act on wallets in the default mainnet wallet directory in the datadir.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH BITCOIND "1" "November 2022" "bitcoind v24.0.0" "User Commands"
|
||||
.TH BITCOIND "1" "December 2022" "bitcoind v24.0.1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoind \- manual page for bitcoind v24.0.0
|
||||
bitcoind \- manual page for bitcoind v24.0.1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoind
|
||||
[\fI\,options\/\fR] \fI\,Start Bitcoin Core\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core version v24.0.0
|
||||
Bitcoin Core version v24.0.1
|
||||
.SH OPTIONS
|
||||
.HP
|
||||
\-?
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
24.0 Release Notes
|
||||
==================
|
||||
24.0.1 Release Notes
|
||||
====================
|
||||
|
||||
Bitcoin Core version 24.0 is now available from:
|
||||
Due to last-minute issues (#26616), 24.0, although tagged, was never fully
|
||||
announced or released.
|
||||
|
||||
<https://bitcoincore.org/bin/bitcoin-core-24.0/>
|
||||
Bitcoin Core version 24.0.1 is now available from:
|
||||
|
||||
<https://bitcoincore.org/bin/bitcoin-core-24.0.1/>
|
||||
|
||||
This release includes new features, various bug fixes and performance
|
||||
improvements, as well as updated translations.
|
||||
|
||||
@@ -2007,8 +2007,15 @@ void PeerManagerImpl::RelayTransaction(const uint256& txid, const uint256& wtxid
|
||||
auto tx_relay = peer.GetTxRelay();
|
||||
if (!tx_relay) continue;
|
||||
|
||||
const uint256& hash{peer.m_wtxid_relay ? wtxid : txid};
|
||||
LOCK(tx_relay->m_tx_inventory_mutex);
|
||||
// Only queue transactions for announcement once the version handshake
|
||||
// is completed. The time of arrival for these transactions is
|
||||
// otherwise at risk of leaking to a spy, if the spy is able to
|
||||
// distinguish transactions received during the handshake from the rest
|
||||
// in the announcement.
|
||||
if (tx_relay->m_next_inv_send_time == 0s) continue;
|
||||
|
||||
const uint256& hash{peer.m_wtxid_relay ? wtxid : txid};
|
||||
if (!tx_relay->m_tx_inventory_known_filter.contains(hash)) {
|
||||
tx_relay->m_tx_inventory_to_send.insert(hash);
|
||||
}
|
||||
@@ -3396,6 +3403,21 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
||||
// they may wish to request compact blocks from us
|
||||
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, /*high_bandwidth=*/false, /*version=*/CMPCTBLOCKS_VERSION));
|
||||
}
|
||||
|
||||
if (auto tx_relay = peer->GetTxRelay()) {
|
||||
// `TxRelay::m_tx_inventory_to_send` must be empty before the
|
||||
// version handshake is completed as
|
||||
// `TxRelay::m_next_inv_send_time` is first initialised in
|
||||
// `SendMessages` after the verack is received. Any transactions
|
||||
// received during the version handshake would otherwise
|
||||
// immediately be advertised without random delay, potentially
|
||||
// leaking the time of arrival to a spy.
|
||||
Assume(WITH_LOCK(
|
||||
tx_relay->m_tx_inventory_mutex,
|
||||
return tx_relay->m_tx_inventory_to_send.empty() &&
|
||||
tx_relay->m_next_inv_send_time == 0s));
|
||||
}
|
||||
|
||||
pfrom.fSuccessfullyConnected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -512,7 +512,7 @@ void BitcoinGUI::createMenuBar()
|
||||
connect(minimize_action, &QAction::triggered, [] {
|
||||
QApplication::activeWindow()->showMinimized();
|
||||
});
|
||||
connect(qApp, &QApplication::focusWindowChanged, [minimize_action] (QWindow* window) {
|
||||
connect(qApp, &QApplication::focusWindowChanged, this, [minimize_action] (QWindow* window) {
|
||||
minimize_action->setEnabled(window != nullptr && (window->flags() & Qt::Dialog) != Qt::Dialog && window->windowState() != Qt::WindowMinimized);
|
||||
});
|
||||
|
||||
@@ -527,7 +527,7 @@ void BitcoinGUI::createMenuBar()
|
||||
}
|
||||
});
|
||||
|
||||
connect(qApp, &QApplication::focusWindowChanged, [zoom_action] (QWindow* window) {
|
||||
connect(qApp, &QApplication::focusWindowChanged, this, [zoom_action] (QWindow* window) {
|
||||
zoom_action->setEnabled(window != nullptr);
|
||||
});
|
||||
#endif
|
||||
|
||||
@@ -730,7 +730,9 @@ static RPCHelpMan migratewallet()
|
||||
std::shared_ptr<CWallet> wallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!wallet) return NullUniValue;
|
||||
|
||||
EnsureWalletIsUnlocked(*wallet);
|
||||
if (wallet->IsCrypted()) {
|
||||
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: migratewallet on encrypted wallets is currently unsupported.");
|
||||
}
|
||||
|
||||
WalletContext& context = EnsureWalletContext(request.context);
|
||||
|
||||
|
||||
@@ -102,15 +102,13 @@ void CoinsResult::Clear() {
|
||||
coins.clear();
|
||||
}
|
||||
|
||||
void CoinsResult::Erase(std::set<COutPoint>& preset_coins)
|
||||
void CoinsResult::Erase(const std::set<COutPoint>& coins_to_remove)
|
||||
{
|
||||
for (auto& it : coins) {
|
||||
auto& vec = it.second;
|
||||
auto i = std::find_if(vec.begin(), vec.end(), [&](const COutput &c) { return preset_coins.count(c.outpoint);});
|
||||
if (i != vec.end()) {
|
||||
vec.erase(i);
|
||||
break;
|
||||
}
|
||||
for (auto& [type, vec] : coins) {
|
||||
auto remove_it = std::remove_if(vec.begin(), vec.end(), [&](const COutput& coin) {
|
||||
return coins_to_remove.count(coin.outpoint) == 1;
|
||||
});
|
||||
vec.erase(remove_it, vec.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ struct CoinsResult {
|
||||
* i.e., methods can work with individual OutputType vectors or on the entire object */
|
||||
size_t Size() const;
|
||||
void Clear();
|
||||
void Erase(std::set<COutPoint>& preset_coins);
|
||||
void Erase(const std::set<COutPoint>& coins_to_remove);
|
||||
void Shuffle(FastRandomContext& rng_fast);
|
||||
void Add(OutputType type, const COutput& out);
|
||||
|
||||
|
||||
@@ -969,5 +969,45 @@ BOOST_AUTO_TEST_CASE(SelectCoins_effective_value_test)
|
||||
BOOST_CHECK(!result);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(wallet_coinsresult_test, BasicTestingSetup)
|
||||
{
|
||||
// Test case to verify CoinsResult object sanity.
|
||||
CoinsResult available_coins;
|
||||
{
|
||||
std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", m_args, CreateMockWalletDatabase());
|
||||
BOOST_CHECK_EQUAL(dummyWallet->LoadWallet(), DBErrors::LOAD_OK);
|
||||
LOCK(dummyWallet->cs_wallet);
|
||||
dummyWallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
|
||||
dummyWallet->SetupDescriptorScriptPubKeyMans();
|
||||
|
||||
// Add some coins to 'available_coins'
|
||||
for (int i=0; i<10; i++) {
|
||||
add_coin(available_coins, *dummyWallet, 1 * COIN);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// First test case, check that 'CoinsResult::Erase' function works as expected.
|
||||
// By trying to erase two elements from the 'available_coins' object.
|
||||
std::set<COutPoint> outs_to_remove;
|
||||
const auto& coins = available_coins.All();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
outs_to_remove.emplace(coins[i].outpoint);
|
||||
}
|
||||
available_coins.Erase(outs_to_remove);
|
||||
|
||||
// Check that the elements were actually removed.
|
||||
const auto& updated_coins = available_coins.All();
|
||||
for (const auto& out: outs_to_remove) {
|
||||
auto it = std::find_if(updated_coins.begin(), updated_coins.end(), [&out](const COutput &coin) {
|
||||
return coin.outpoint == out;
|
||||
});
|
||||
BOOST_CHECK(it == updated_coins.end());
|
||||
}
|
||||
// And verify that no extra element were removed
|
||||
BOOST_CHECK_EQUAL(available_coins.Size(), 8);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
} // namespace wallet
|
||||
|
||||
@@ -112,5 +112,50 @@ BOOST_FIXTURE_TEST_CASE(FillInputToWeightTest, BasicTestingSetup)
|
||||
// Note: We don't test the next boundary because of memory allocation constraints.
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(wallet_duplicated_preset_inputs_test, TestChain100Setup)
|
||||
{
|
||||
// Verify that the wallet's Coin Selection process does not include pre-selected inputs twice in a transaction.
|
||||
|
||||
// Add 4 spendable UTXO, 50 BTC each, to the wallet (total balance 200 BTC)
|
||||
for (int i = 0; i < 4; i++) CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||
auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), m_args, coinbaseKey);
|
||||
|
||||
LOCK(wallet->cs_wallet);
|
||||
auto available_coins = AvailableCoins(*wallet);
|
||||
std::vector<COutput> coins = available_coins.All();
|
||||
// Preselect the first 3 UTXO (150 BTC total)
|
||||
std::set<COutPoint> preset_inputs = {coins[0].outpoint, coins[1].outpoint, coins[2].outpoint};
|
||||
|
||||
// Try to create a tx that spends more than what preset inputs + wallet selected inputs are covering for.
|
||||
// The wallet can cover up to 200 BTC, and the tx target is 299 BTC.
|
||||
std::vector<CRecipient> recipients = {{GetScriptForDestination(*Assert(wallet->GetNewDestination(OutputType::BECH32, "dummy"))),
|
||||
/*nAmount=*/299 * COIN, /*fSubtractFeeFromAmount=*/true}};
|
||||
CCoinControl coin_control;
|
||||
coin_control.m_allow_other_inputs = true;
|
||||
for (const auto& outpoint : preset_inputs) {
|
||||
coin_control.Select(outpoint);
|
||||
}
|
||||
|
||||
// Attempt to send 299 BTC from a wallet that only has 200 BTC. The wallet should exclude
|
||||
// the preset inputs from the pool of available coins, realize that there is not enough
|
||||
// money to fund the 299 BTC payment, and fail with "Insufficient funds".
|
||||
//
|
||||
// Even with SFFO, the wallet can only afford to send 200 BTC.
|
||||
// If the wallet does not properly exclude preset inputs from the pool of available coins
|
||||
// prior to coin selection, it may create a transaction that does not fund the full payment
|
||||
// amount or, through SFFO, incorrectly reduce the recipient's amount by the difference
|
||||
// between the original target and the wrongly counted inputs (in this case 99 BTC)
|
||||
// so that the recipient's amount is no longer equal to the user's selected target of 299 BTC.
|
||||
|
||||
// First case, use 'subtract_fee_from_outputs=true'
|
||||
util::Result<CreatedTransactionResult> res_tx = CreateTransaction(*wallet, recipients, /*change_pos*/-1, coin_control);
|
||||
BOOST_CHECK(!res_tx.has_value());
|
||||
|
||||
// Second case, don't use 'subtract_fee_from_outputs'.
|
||||
recipients[0].fSubtractFeeFromAmount = false;
|
||||
res_tx = CreateTransaction(*wallet, recipients, /*change_pos*/-1, coin_control);
|
||||
BOOST_CHECK(!res_tx.has_value());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
} // namespace wallet
|
||||
|
||||
@@ -4102,8 +4102,8 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
|
||||
|
||||
// Make list of wallets to cleanup
|
||||
std::vector<std::shared_ptr<CWallet>> created_wallets;
|
||||
created_wallets.push_back(std::move(res.watchonly_wallet));
|
||||
created_wallets.push_back(std::move(res.solvables_wallet));
|
||||
if (res.watchonly_wallet) created_wallets.push_back(std::move(res.watchonly_wallet));
|
||||
if (res.solvables_wallet) created_wallets.push_back(std::move(res.solvables_wallet));
|
||||
|
||||
// Get the directories to remove after unloading
|
||||
for (std::shared_ptr<CWallet>& w : created_wallets) {
|
||||
|
||||
78
test/functional/p2p_tx_privacy.py
Executable file
78
test/functional/p2p_tx_privacy.py
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2022 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""
|
||||
Test that transaction announcements are only queued for peers that have
|
||||
successfully completed the version handshake.
|
||||
|
||||
Topology:
|
||||
|
||||
tx_originator ----> node[0] <---- spy
|
||||
|
||||
We test that a transaction sent by tx_originator is only relayed to spy
|
||||
if it was received after spy's version handshake completed.
|
||||
|
||||
1. Fully connect tx_originator
|
||||
2. Connect spy (no version handshake)
|
||||
3. tx_originator sends tx1
|
||||
4. spy completes the version handshake
|
||||
5. tx_originator sends tx2
|
||||
6. We check that only tx2 is announced on the spy interface
|
||||
"""
|
||||
from test_framework.messages import (
|
||||
msg_wtxidrelay,
|
||||
msg_verack,
|
||||
msg_tx,
|
||||
CInv,
|
||||
MSG_WTX,
|
||||
)
|
||||
from test_framework.p2p import (
|
||||
P2PInterface,
|
||||
)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.wallet import MiniWallet
|
||||
|
||||
class P2PTxSpy(P2PInterface):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.all_invs = []
|
||||
|
||||
def on_version(self, message):
|
||||
self.send_message(msg_wtxidrelay())
|
||||
|
||||
def on_inv(self, message):
|
||||
self.all_invs += message.inv
|
||||
|
||||
def wait_for_inv_match(self, expected_inv):
|
||||
self.wait_until(lambda: len(self.all_invs) == 1 and self.all_invs[0] == expected_inv)
|
||||
|
||||
class TxPrivacyTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
|
||||
def run_test(self):
|
||||
self.wallet = MiniWallet(self.nodes[0])
|
||||
self.wallet.rescan_utxos()
|
||||
|
||||
tx_originator = self.nodes[0].add_p2p_connection(P2PInterface())
|
||||
spy = self.nodes[0].add_p2p_connection(P2PTxSpy(), wait_for_verack=False)
|
||||
spy.wait_for_verack()
|
||||
|
||||
# tx_originator sends tx1
|
||||
tx1 = self.wallet.create_self_transfer()["tx"]
|
||||
tx_originator.send_and_ping(msg_tx(tx1))
|
||||
|
||||
# Spy sends the verack
|
||||
spy.send_and_ping(msg_verack())
|
||||
|
||||
# tx_originator sends tx2
|
||||
tx2 = self.wallet.create_self_transfer()["tx"]
|
||||
tx_originator.send_and_ping(msg_tx(tx2))
|
||||
|
||||
# Spy should only get an inv for the second transaction as the first
|
||||
# one was received pre-verack with the spy
|
||||
spy.wait_for_inv_match(CInv(MSG_WTX, tx2.calc_sha256(True)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
TxPrivacyTest().main()
|
||||
@@ -107,6 +107,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
self.generate(self.nodes[0], 121)
|
||||
|
||||
self.test_add_inputs_default_value()
|
||||
self.test_preset_inputs_selection()
|
||||
self.test_weight_calculation()
|
||||
self.test_change_position()
|
||||
self.test_simple()
|
||||
@@ -1189,6 +1190,50 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
|
||||
self.nodes[2].unloadwallet("test_preset_inputs")
|
||||
|
||||
def test_preset_inputs_selection(self):
|
||||
self.log.info('Test wallet preset inputs are not double-counted or reused in coin selection')
|
||||
|
||||
# Create and fund the wallet with 4 UTXO of 5 BTC each (20 BTC total)
|
||||
self.nodes[2].createwallet("test_preset_inputs_selection")
|
||||
wallet = self.nodes[2].get_wallet_rpc("test_preset_inputs_selection")
|
||||
outputs = {}
|
||||
for _ in range(4):
|
||||
outputs[wallet.getnewaddress(address_type="bech32")] = 5
|
||||
self.nodes[0].sendmany("", outputs)
|
||||
self.generate(self.nodes[0], 1)
|
||||
|
||||
# Select the preset inputs
|
||||
coins = wallet.listunspent()
|
||||
preset_inputs = [coins[0], coins[1], coins[2]]
|
||||
|
||||
# Now let's create the tx creation options
|
||||
options = {
|
||||
"inputs": preset_inputs,
|
||||
"add_inputs": True, # automatically add coins from the wallet to fulfill the target
|
||||
"subtract_fee_from_outputs": [0], # deduct fee from first output
|
||||
"add_to_wallet": False
|
||||
}
|
||||
|
||||
# Attempt to send 29 BTC from a wallet that only has 20 BTC. The wallet should exclude
|
||||
# the preset inputs from the pool of available coins, realize that there is not enough
|
||||
# money to fund the 29 BTC payment, and fail with "Insufficient funds".
|
||||
#
|
||||
# Even with SFFO, the wallet can only afford to send 20 BTC.
|
||||
# If the wallet does not properly exclude preset inputs from the pool of available coins
|
||||
# prior to coin selection, it may create a transaction that does not fund the full payment
|
||||
# amount or, through SFFO, incorrectly reduce the recipient's amount by the difference
|
||||
# between the original target and the wrongly counted inputs (in this case 9 BTC)
|
||||
# so that the recipient's amount is no longer equal to the user's selected target of 29 BTC.
|
||||
|
||||
# First case, use 'subtract_fee_from_outputs = true'
|
||||
assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{wallet.getnewaddress(address_type="bech32"): 29}], options=options)
|
||||
|
||||
# Second case, don't use 'subtract_fee_from_outputs'
|
||||
del options["subtract_fee_from_outputs"]
|
||||
assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{wallet.getnewaddress(address_type="bech32"): 29}], options=options)
|
||||
|
||||
self.nodes[2].unloadwallet("test_preset_inputs_selection")
|
||||
|
||||
def test_weight_calculation(self):
|
||||
self.log.info("Test weight calculation with external inputs")
|
||||
|
||||
|
||||
@@ -316,6 +316,7 @@ BASE_SCRIPTS = [
|
||||
'rpc_deriveaddresses.py',
|
||||
'rpc_deriveaddresses.py --usecli',
|
||||
'p2p_ping.py',
|
||||
'p2p_tx_privacy.py',
|
||||
'rpc_scantxoutset.py',
|
||||
'feature_txindex_compatibility.py',
|
||||
'feature_unsupported_utxo_db.py',
|
||||
|
||||
@@ -393,6 +393,15 @@ class WalletMigrationTest(BitcoinTestFramework):
|
||||
|
||||
assert_equal(bals, wallet.getbalances())
|
||||
|
||||
def test_encrypted(self):
|
||||
self.log.info("Test migration of an encrypted wallet")
|
||||
wallet = self.create_legacy_wallet("encrypted")
|
||||
|
||||
wallet.encryptwallet("pass")
|
||||
|
||||
assert_raises_rpc_error(-15, "Error: migratewallet on encrypted wallets is currently unsupported.", wallet.migratewallet)
|
||||
# TODO: Fix migratewallet so that we can actually migrate encrypted wallets
|
||||
|
||||
def run_test(self):
|
||||
self.generate(self.nodes[0], 101)
|
||||
|
||||
@@ -402,6 +411,7 @@ class WalletMigrationTest(BitcoinTestFramework):
|
||||
self.test_other_watchonly()
|
||||
self.test_no_privkeys()
|
||||
self.test_pk_coinbases()
|
||||
self.test_encrypted()
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletMigrationTest().main()
|
||||
|
||||
@@ -46,6 +46,8 @@ def main():
|
||||
commit_range = merge_base + "..HEAD"
|
||||
else:
|
||||
commit_range = os.getenv("COMMIT_RANGE")
|
||||
if commit_range == "SKIP_EMPTY_NOT_A_PR":
|
||||
sys.exit(0)
|
||||
|
||||
commit_hashes = check_output(["git", "log", commit_range, "--format=%H"], universal_newlines=True, encoding="utf8").splitlines()
|
||||
|
||||
|
||||
@@ -97,6 +97,8 @@ def main():
|
||||
commit_range = merge_base + "..HEAD"
|
||||
else:
|
||||
commit_range = os.getenv("COMMIT_RANGE")
|
||||
if commit_range == "SKIP_EMPTY_NOT_A_PR":
|
||||
sys.exit(0)
|
||||
|
||||
whitespace_selection = []
|
||||
tab_selection = []
|
||||
|
||||
Reference in New Issue
Block a user